Calling Objective C method with completion handler from Swift3 (completion is IUO?) - swift

so I'm finally getting around to doing my Swift3 conversion. I'm getting MANY of the following errors since we have a legacy codebase that was written in ObjC.
The ObjC definition is here:
-(void)getRecommendationHintsWithCompletion:(void(^)(NSArray *recommendationHints, NSError *error))completion;
in Swift 2.2, we called it like this:
manager.getRecommendationHints { (hints:[AnyObject]!, error: NSError!) in
//code
})
After the swift 3 migrator ran, that line of swift code wasn't changed, but I got the error:
Cannot convert value of type '([AnyObject]!, NSError!) -> ()' to expected argument type '(([Any]?, Error?) -> Void)!'
So I tried:
manager.getRecommendationHints { (hints:[Any]?, error: Error?) in
//code
})
But I still get:
Cannot convert value of type '([AnyObject]?, Error?) -> ()' to expected argument type '(([Any]?, Error?) -> Void)!'
It looks like there's a Implicity Unwrapped Optional on the expected argument, but I'm not sure how to deal with that.
What should I do? Thanks!

Try using Error instead of NSError
manager.getRecommendationHints { (hints:[Any]?, error: Error?) in
//code
}
Hopefully this will solve your problem

Related

startAccelerometerUpdates in Swift [duplicate]

I am converting an app from swift 2 to swift 3 and I'm trying to use the CMMotionManager, but it gives me this error when I try to call the .startAccelerometerUpdates() function... No clue what's wrong though.
This is how I initialize the manager:
let motionManager = CMMotionManager()
Trying to call the function:
motionManager.startAccelerometerUpdates(to: OperationQueue.main) { [weak self] (data: CMAccelerometerData?, error: NSError?) in
self!.outputAccelerationData(data!.acceleration)
}
Error: Cannot convert value of type '(CMAccelerometerData?, NSError?)
-> ()' to expected argument type 'CMAccelerometerHandler' (aka '(Optional, Optional) -> ()')
Thanks!
The cryptic error message boils down to this: in Swift 3 NSError is bridged to Error instead. Write your code like this and the problem should go away:
motionManager.startAccelerometerUpdates(to: OperationQueue.main) { [weak self] (data: CMAccelerometerData?, error: Error?) in
There are many changes in Swift3. Especially in expressions. Many NS suffix of types are removed such as NSError to Error, NSData to Data.
Therefore, change NSError to Error. And if you want to prevent performance issue by multiple instances of CMMotionManager, use SwiftyMotionManager.

Pinterest iOS SDK w/ Swift always returns "Cant convert value of type '(PDKResponseObject) -> ()' to expected argument type 'PDKResponseObject!'"

I'm trying to build a Pinterest iOS clone as an exercise -- first day using xcode and swift. I can't get any function that uses the Pinterest SDK to work properly. Every function gives a Type Error.
For example, here's the code I'm using for login:
#IBAction func login(_ sender: UIButton) {
PDKClient.sharedInstance().authenticate(
withPermissions: [PDKClientReadPublicPermissions],
from: self,
withSuccess: { (success: PDKClientSuccess!) in
print(success)
},
andFailure: { (failure: PDKClientFailure!) in
print(failure)
}
)
}
I get back the following error:
Cannot convert value of type '(PDKClientSuccess!) -> ()' to expected argument type 'PDKClientSuccess!'
If I do withSuccess: PDKClientSuccess! then I get the following error:
ViewController.swift:28:26: Cannot convert value of type 'PDKClientSuccess!.Type' (aka'ImplicitlyUnwrappedOptional<(Optional<PDKResponseObject>) -> ()>.Type') to expected argument type 'PDKClientSuccess!'
If I just do withSuccess: { (PDKClientSuccess) in print("success") } xcode doesn't complain, but it also doesn't work properly.
The documentation seems to be a bit outdated, but here's how they expect the function to look:
- (void)authenticateWithPermissions:(NSArray *)permissions
fromViewController:(UIViewController *)presentingViewController
withSuccess:(PDKClientSuccess)successBlock
andFailure:(PDKClientFailure)failureBlock;
Though based on the error messages I'm getting from xcode, the function is now called 'authenticate', 'withPermissions' is a parameter, and 'fromViewController' is now just 'from'.
So the issue was I was passing a function when it's expecting a closure. So the TypeError makes sense.
The correct code is:
PDKClient.sharedInstance().authenticate(
withPermissions: [PDKClientReadPublicPermissions],
from: self,
withSuccess: { (success) in print(success ?? "succeeds") },
andFailure: { (failure) in print(failure ?? "fails") }
)
Unfortunately this hasn't solved everything... I never hit the withSuccess or withFailure, but at least xcode doesn't complain.

readWithCallback function not working in swift 3

Before the swift update(in swift 2) my code was like the following and it worked successfully with no errors.
client.me.events.readWithCallback({
(list: Array<AnyObject>!, error: MSOrcError!) -> Void in
}
But after the swift update (in Swift 3) I get errors in the previous code, so I changed it to the below code. It has no errors but when I run the app it gets crashes and terminates. When I remove the last line I get an error saying:
Cannot convert value of type 'Any?' to expected argument type
'(([Any]?, MSOrcError?) -> Void)!'
client.me.events.read(callback: Any?{
(list: Array<AnyObject>!, error: MSOrcError!) -> Void in
} as! (([Any]?, MSOrcError?) -> Void)! )
How do I solve this error?
The error message says that the expected argument type is (([Any]?, MSOrcError?) -> Void)!. Why don't you follow it?
client.me.events.read(callback: {(list: [Any]?, error: MSOrcError?) in
//...
})
Or simply:
client.me.events.read {list, error in
//...
}

Completion closure within a closure

Just starting to learn Swift coming from Obj-C - this is something simple I'm not understanding:
class func queryForAllUsersWithCallback(completion: (users :[User]?, error :NSError?) ->()) {
var query = PFQuery(className:User.parseClassName())
query.findObjectsInBackgroundWithBlock ({
(objects:[AnyObject]?, error: NSError?) in
completion(users: objects, error: error);
})
}
Give me a compiler error:
Cannot invoke 'findObjectsInBackgroundWithBlock' with an argument list of type '(([AnyObject]?, NSError?) -> _)'
If I comment out the line:
completion(users: objects, error: error);
the error goes away, so the warning is misleading.
completion takes as its first argument an array of User, whereas objects is an array of AnyObject. There’s no guarantee what is in objects is of the correct type (could be a motley collection of various types for all the compiler knows) so it won’t compile.
If you do a conditional cast it should compile, i.e.:
completion(users: objects as? [User], error: error)
Note, this will check at runtime that every element in objects really is of the correct type. If any of them aren’t, the whole array will be nil when passed to the completion handler. This will compile, since the argument is optional, but might be quite surprising/fail silently or even worse, crash because somewhere inside completion might be the assumption it isn’t nil, so it could get force-unwrapped.
So you might instead want to put some error handling in:
if let users = objects as? [User] {
completion(users: users, error: error)
}
else {
// log or fatalError or something
}
(apologies if the syntax of some of the above isn’t quite right, I haven’t tested the code since your snippet isn’t reproducible/stand-alone)
You just need to cast the objects to User as:
completion(users: objects as? [User], error: error)

Swift parse framework and closures

This is srsly driving me crazy.
I am trying to use getFirstObjectInBackgroundWithBlock() method in swift but I can't figure out how to (not) use the optionals ..
I just want to get the user's score from the parse server And I do it like this:
func updateScoreForCurrentUser(score: Int){
let user = PFUser.currentUser()
// get gameScore for user
var query = PFQuery(className: "GameScore")
query.whereKey("User", equalTo: user!)
query.getFirstObjectInBackgroundWithBlock { (gameScore: PFObject, error: NSError?) -> Void in
gameScore["score"] = score
}
I just get a "Cannot invoke 'getFirstObjectInBackgroundWithBlock' with an argument list of type '((PFObject?, NSError?) -> Void)'"
Can you pleaase help me? Thank you
This error that you are getting is, as you've guessed already that you need to hvae gameScore object as an optional.
"Cannot invoke 'getFirstObjectInBackgroundWithBlock' with an argument
list of type '((PFObject?, NSError?) -> Void)'"
This is not because of swift or its limitations. It's because Parse SDK defines that function like that. And unless Parse changes its API, you will have to use an optional.
And just my two cents on the matter, an Optional is in order here.
Either you will get a PFObject or you will get an Error, not both. So one of them will be nil, hence the use of Optional.