Search code examples
iosswiftobservablerx-swift

RxSwift Type of expression is ambiguous without more context


I'm facing this issue while trying to nest 2 network API using RxSwift, the flatMap keeps saying type of expression is ambiguous. I'm new to RxSwift so this takes me almost 2 days but no clues. I tried to declare every thing explicit but not helping. enter image description here

I'm not sure why it didn't know the return type of the closure. Here is full code:

private func uploadImage(imageUrl: URL) -> Observable<String> {
    do {
        let data = try Data(contentsOf: imageUrl)
        let fileExtension = imageUrl.pathExtension
        return Single<String>.create(subscribe: { single in
            AppManager.shared.dataAdapter.apiCallUploadDocument(data: data, fileExtension: fileExtension) { result in
                switch result {
                case .success(let documentId):
                    single(.success(documentId))
                case .failure(let error):
                    single(.failure(error))
                }
            }
            return Disposables.create()
        }).asObservable()
    } catch let error {
        return Observable.error(error)
    }
}

func subscribePlan(data: SCSubscribeData) -> Observable<PurchaseResponse> {
    let settings = AppManager.shared
    let idCardPhoto = data.idCardPhoto
    let governmentPassportPhoto = data.governmentPasspordIdPhoto
    let isUserProfileUpdated = settings.loginResult.profileUpdated
    if isUserProfileUpdated {
        let icDocumentType = settings.eligibilityInput.getSubscriberData()["idType"] ?? ""
        return uploadImage(imageUrl: idCardPhoto.url)
            .flatMap({ documentId in
                let param: [String: String] = [
                    "eligibilityDocument": documentId,
                    "icDocument": "EXISTED",
                    "icDocumentType": icDocumentType,
                    "eligibilityDocumentNo": data.idNumber,
                ]
                let scAdditionalParameter: [String : AnyObject] = [
                    "properties": param
                ]
                return subscribe(purchaseModel: data.purchaseModel, additionalParam: scAdditionalParameter)
            })
        
    }
    return Observable<PurchaseResponse>.error(NSError())
}

func subscribe(purchaseModel: PurchaseModel, additionalParam: [String: AnyObject]) -> Observable<PurchaseResponse> {
    return Single.create(subscribe: { single in
        AppManager.shared.dataAdapter.apiCallPurchaseSubscription(purchaseModel: purchaseModel,
                                                         additionalParameter: additionalParam,
                                                         result: { result in
            switch result {
            case .success(let res):
                single(.success(res))
            case .failure(let error):
                single(.failure(error))
            }
        })
        return Disposables.create()
    }).asObservable()
}

Solution

  • The fundamental problem here is that you are using dictionaries of type [String: AnyObject] and then trying to put non-Objects in them. The param type is not an Object (class type), it's a Dictionary (struct type). Use [String: Any] instead.

    Update

    It might be instructive to show how I figured out the problem. I simply moved the offending closure into its own function:

    func purchaseResponse(data: SCSubscribeData, icDocumentType: String, documentId: String) -> Observable<PurchaseResponse> {
        let param: [String: String] = [
            "eligibilityDocument": documentId,
            "icDocument": "EXISTED",
            "icDocumentType": icDocumentType,
            "eligibilityDocumentNo": data.idNumber,
        ]
        let scAdditionalParameter: [String : AnyObject] = [
            "properties": param
        ]
        return subscribe(purchaseModel: data.purchaseModel, additionalParam: scAdditionalParameter)
    }
    

    By doing this, the error jumps right out. Also, if you want, you can curry the above function and use it in the original code:

    import Curry // https://github.com/thoughtbot/curry
    
    func subscribePlan(data: SCSubscribeData) -> Observable<PurchaseResponse> {
        let settings = AppManager.shared
        let idCardPhoto = data.idCardPhoto
        let governmentPassportPhoto = data.governmentPasspordIdPhoto
        let isUserProfileUpdated = settings.loginResult.profileUpdated
        if isUserProfileUpdated {
            let icDocumentType = settings.eligibilityInput.getSubscriberData()["idType"] ?? ""
            return uploadImage(imageUrl: idCardPhoto.url)
                .flatMap(curry(purchaseResponse)(data)(icDocumentType))
        }
        return Observable<PurchaseResponse>.error(NSError())
    }