Search code examples
iosswiftalamofire

Alamofire publish decodable


I have a HTTP request to my server like this.

func loginUser( username: String, password: String )->AnyPublisher<UserModel, Error>{

    let url = URL( string: "\(Credentials.BASE_URL)auth/login")!
    let userSignInModel = SignInModel(username: username, password: password)
    
    return AF.request(url,
                      method: .post,
                      parameters: userSignInModel,
                      encoder: JSONParameterEncoder.default)
        .validate()
        .publishDecodable(type: UserModel.self)
        .value()
        .mapError{$0 as Error}
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
}

and get response like this

    self.dataManager.loginUser(username: self.logInUsername, password: self.logInPassword)
        .sink { (response) in
            print( response )
            switch response {
                case .failure( let error ):
                    self.createAlert(with: error.localizedDescription,
                                     for: .loginAlert,
                                     responseCode: error.asAFError?.responseCode)
                case .finished:
                    break
            }
        } receiveValue: { (userModel) in                
            self.token = userModel.token
            self.userID = userModel.user.id
        }.store(in: &cancellableSet)

but the problem is that I am not able to get error message from the server, how it can be done?


Solution

  • There are several different approaches to parsing responses which return success or response values. Perhaps the simplest is to map any initial failures to your own error type which parses the information you need. For example, given this error type:

    struct NetworkError: Error {
      let initialError: AFError
      let backendError: BackendError?
    }
    

    Where BackendError encapsulates the information returned from the backend. Then you can map the response from Alamofire.

    AF.request(url,
               method: .post,
               parameters: userSignInModel)
        .validate()
        .publishDecodable(type: UserModel.self)
        .map { response in
            response.mapError { error in
                // Check to see if it's an error that should have backend info.
                // Assuming it is, parse the BackendError.
                let backendError = response.data.flatMap { try? JSONDecoder().decode(BackendError.self, from: $0) }
                return NetworkError(initialError: error, backendError: backendError)
            }
        }
    

    Other alternatives include an enum-based response type that separates your success and failures, or your own response serializer which does the error parsing internally.