Search code examples
swiftalamofirerx-swiftrequest-validation

Observable.create inside generic Alamofire call function not working when calling validate()


I want to make a validated network request using Alamofire, through a 'generic' function. If I don't use RX at all or if I don't call validate() or anyCustomValidate() it works fine, but if I use it with the Observable.create and validate() as below, it does not work. This is what it does instead:

     1.makes tokenRefresh call
     2.receives the new token
     3.calls completion(true, 0.0) (as below)

but then nothing happens...

Here is my generic function:

func sendRequest<Response: Codable>(endpoint: URLRequestConvertible) -> Observable<Response> {
    return Observable<Response>.create { observer in
        let request = self.session.request(endpoint)
            .validate()
            .responseJSON { response in
                print(response)
                switch response.result {
                case .success:
                    do {
                        let model = try JSONDecoder().decode(Response.self, from: response.data!)
                        observer.onNext(model)
                    } catch {
                        print("generic call function error: \(error)")
                        observer.onError(RequestError.failedParsingError(ErrTypes.somethingWrong.rawValue))
                    }
                case .failure:
                observer.onError(RequestError.failedParsingError(ErrTypes.somethingWrong.rawValue))
                }
        }
        return Disposables.create {
            request.cancel()
        }
    }.observeOn(MainScheduler.instance)
}

This is how I use it to get a specific response:

func getAddresses(clientId: Int) -> Observable<[AddressesResponse]> {
    return sendRequest(endpoint: CustomerEndPoint.getAddresses(userID: clientId))
}

And this is how I call the function:

 @objc func buttonPressed() {
    getAddresses(clientId: 1111).subscribe(onNext: { addresses in
        print(addresses)
    }).disposed(by: disposeBag)
}

This is my Request retrier:

extension AuthHandler: RequestRetrier {

func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
    guard let request = request as? DataRequest, request.delegate.data != nil else { fatalError() }
    guard let headerStatusCode = request.response?.statusCode else {
        completion(false, 0.0)
        return
    }
    if headerStatusCode == 401 || headerStatusCode == 403 {
        refreshToken(success: {
            completion(true, 0.0)
        }, failure: { message in
            print(message)
        })
    } else {
        if let json = String(data: request.delegate.data!, encoding: String.Encoding.utf8),
        let jsonObject = dictionaryWithJSONString(json) as? [String:Any],
        let statusMessage = jsonObject["statusMessage"] as? [String: Any],
        let status = statusMessage["status"] as? Int {
            if status == 401 || status == 403 {
                self.refreshToken(success: {
                    completion(true, 0.0)
                }, failure: { message in
                    completion(false, 0.0)
                    print(message)
                })
            }
        }
    }
}

After a while this gets printed on the console log:

2019-02-17 22:25:13.368846+0100 AlamofireValidator[76262:1527976] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C1.1:2][0x7fc5cf50e020] get output frames failed, state 8196

Solution

  • Errors like this almost always mean that your completion block or observer is not getting called in one of the paths.

    In this specific case it looks to me that your last couple of if checks are suspect. Put the two print statements below in and put break points on them. I think you will find that one of them is getting triggered.

    {
        if let json = String(data: request.delegate.data!, encoding: String.Encoding.utf8),
            let jsonObject = dictionaryWithJSONString(json) as? [String:Any],
            let statusMessage = jsonObject["statusMessage"] as? [String: Any],
            let status = statusMessage["status"] as? Int {
            if status == 401 || status == 403 {
                self.refreshToken(success: {
                    completion(true, 0.0)
                }, failure: { message in
                    completion(false, 0.0)
                    print(message)
                })
            }
            else {
                print("the problem is here")
            }
        }
        else {
            print("the problem is here.")
        }