startAccelerometerUpdates in Swift [duplicate] - swift

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.

Related

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

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

NSURLSession crashing from Swift

I've got a simple class that uses an NSURLSession.
class test {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration());
func f() {
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0), { () -> Void in
var task = self.session.dataTaskWithRequest(request, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if error != nil {
// cry
return;
}
var error: NSError? = nil;
var dict = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as! Dictionary<String, String>;
// use dict
});
task.resume();
});
}
When I try to deserialize the data as JSON, the application crashes.
I've determined that the data seems to be of the right length but the content looks like garbage in the debugger, so it seems to me that the data object passed in is broken. Furthermore, I suspect some stack smashing as I can step through this completion handler and see that it's the attempt to deserialize the data that's crashing, but when the actual crash occurs, the stack in the debugger mentions nothing about the completion handler or any of my code.
I've seen several samples of using NSURLSession that look pretty much exactly like mine that just work. I tried using the shared session instead of making a new one, but that did not help either.
What is causing this crash?
Seems that the JSON was not actually all strings- I brainfarted and one of them was actually a number.
The real problem in the question is that Swift is completely worthless when handling the problem of force casts failing. Not only do you not get any kind of useful error at runtime that you could handle or recover from, but the debugging information presented when it occurs is completely misleading and points to totally the wrong place. You simply get a trap in a function without symbols with the wrong callstack.

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.

'NSSecureCoding!' is not a subtype of 'NSURL' using Swift when trying to replace closure argument type

I'm using the
loadItemForTypeIdentifier:options:completionHandler: method on an NSItemProvider object to extract a url from Safari via a Share extension in iOS 8.
In Objective-C, this code compiles and works:
[itemProvider loadItemForTypeIdentifier:(#"public.url" options:nil completionHandler:^(NSURL *url, NSError *error) {
//My code
}];
In Swift however, I'm getting "NSSecureCoding!' is not a subtype of 'NSURL" compile error when I try to do something similar:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: { (urlItem:NSURL, error:NSError!) in
//My code
})
If I add the bang to NSURL argument type as in NSURL! I get "Cannot convert the expression's type 'Void' to type 'Void'" compile error. And if I leave the default argument typed as NSSecureCoding!, it compiles, but the block/closure doesn't run.
What am I doing wrong?
You don't need to specify the types for urlItem or error as they can be inferred from the declaration of loadItemForTypeIdentifier:options:completionHandler. Just do the following:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: {
(urlItem, error) in
//My code
})
Even better, you can move the closure outside of the method call:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil) {
(urlItem, error) in
//My code
}
This API makes use of reflection internally: it looks at the type of the block’s first parameter to decide what to return. This is dependent on Objective-C’s looser enforcement of the block signature compared to Swift — and therefore does not work in Swift. (Yes, still.) See this discussion on the developer forums.
I recommend filing a bug report with Apple and writing small Objective-C wrapper methods to read each type of data you need.
It’s possible I’ve overlooked something. If someone has found a neat way to make this work in Swift I’m keen to hear it.
The new API to use is canLoadObject and loadObject, use it as this:
if (itemProvider.canLoadObject(ofClass: NSURL.self)) {
print("==== attachment is URL")
itemProvider.loadObject(ofClass: NSURL.self, completionHandler:
{
(data, error) in
print("==== url object = \(data)")
})
}
The same can be used for UIImage
https://developer.apple.com/documentation/uikit/drag_and_drop/data_delivery_with_drag_and_drop
Just in case you or other ones still need a solution, it is simple. Just add a side variable with a cast to NSURL. Here it is:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil) {
(urlItem, error) in
let url : NSURL = urlItem : NSURL
// Whatever you like to do with your new url
}