Eric H Eric H - 7 months ago 109
Swift Question

How to refactor swift callback to promise with PromiseKit

I am attempting my first foray into swift promises with PromiseKit, after some experience with doing them with bluebird in node. My original service function for authentication with a callback looks like this:

private func requestNewToken(email : String, password: String,
completion: (success: Bool, token: String?, userId : String?, message: String? ) -> Void) {

Alamofire.request(Router.Authenticate(email, password).URLRequest)
.responseJSON { request, response, result in
switch result {
case .Success(let data):
let jsonData = JSON(data)

let success = jsonData["success"].bool!
let message = jsonData["message"].string!

if(success) {
let token = jsonData["token"].string
let userId = jsonData["_id"].string

self.deleteSavedUser()
self.saveUserToCoreData(email, password: password, token: token!, userId: userId!)
completion(success: true, token: token, userId: userId, message: message)
return
}

completion(success: false, token: nil, userId: nil, message: message)
print("Token request failed with error: \(message)")

case .Failure(_, let error):
print("Request failed with error: \(error)")
completion(success: false, token: nil, userId : nil, message: nil)
}
}
}


I'm attempting to rewrite to something like this:

private func requestNewTokenPromise(email : String, password:
String) -> Promise<UserLoginData?>{
return Promise{ fulfill, reject in
Alamofire.request(Router.Authenticate(email,password).URLRequest)
.responseJSON { request, response, result in
switch result {
case .Success(let data):
let jsonData = JSON(data)
fulfill(jsonData)
case .Failure(_, let error):
print("Request failed with error: \(error)")
reject(error)
}
}
}.then { json in

let success = json["success"].bool!
let message = json["message"].string!

if(success) {
let token = json["token"].string
let userId = json["_id"].string

self.deleteSavedUser()
self.saveUserToCoreData(email, password: password, token: token!, userId: userId!) //sets the class's savedUser property

return Promise(self.savedUser);
}

return Promise(nil);
}
}


However, I get an error on my then line:

AuthenticationService.swift:147:6: Cannot convert return expression of type 'Promise<AnyObject?>'
(aka 'Promise Optional<AnyObject>> ') to return type 'Promise<UserLoginData?>' (aka 'Promise<Optional<UserLoginData>>')


I had thought by resolving the promise with Promise() that I'd be fulfilling the original promise type. Where am I going wrong?

Answer

I was incorrect in assuming code can be sent straight to promises, sometimes you just need to wrap a callback:

public func login(email: String, password: String) -> Promise<UserLoginData> {
        return Promise { fulfill, reject in
            requestNewToken(email, password: password){
                success, returnToken, userId, message in

                if(!success){
                    reject(LoginError.LoginFailed(message))
                    return
                }
                self.deleteSavedUser()
                self.saveUserToCoreData(email, password: password, token: returnToken!, userId: userId!)
                let user = self.fetchUserFromCoreData()
                fulfill(user!)
            }
        }
    }