Swift parse framework and closures - swift

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.

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.

ReactiveKit Bond KVO observe UserDefaults

I was previously using RxSwift and I decided I did not want to use it anymore and was able to convert everything over to Bond which I am much more familiar with. Since the new changes though to Bond v5, I cannot seem to figure out how to observe values in UserDefaults. The following code ends up giving me a fatal error.
userDefaults.reactive
.keyPath(LocationManager.HomeLocationKey, ofType: String.self, context: .immediateOnMain)
.map(self.initLocation(from:))
.bind(to: self.homeLocation)
userDefaults is a reference to UserDefaults.standard and LocationManager.HomeLocationKey is a string. I am providing the initLocation function below as I know it will be asked for. Below that function I will post the error that I am receiving after the app starts up.
func initLocation(from string: String?) -> Location?
{
guard let dataString = string
else { log.warning("Location data did not exist, returning nil"); return nil }
let json = JSON.parse(dataString)
return Location(from: json)
}
Error:
fatal error: Could not convert nil to String. Maybe `dynamic(keyPath:ofExpectedType:)` method might be of help?): file /Users/sam/Documents/iOS Apps/Drizzle/Pods/Bond/Sources/Shared/NSObject+KVO.swift, line 58
It might not be obvious, but if the observed value can be nil, the ofType argument must be an Optional type. In your case, that would be:
userDefaults.reactive
.keyPath(LocationManager.HomeLocationKey, ofType: Optional<String>.self, context: .immediateOnMain)
...

Swift: can not invoke method with correct arguments?

I am trying to find a count of entities which satisfy predicate. According to documentation and "header" files, this should work:
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "FileRecord")
let ctx: NSManagedObjectContext = GetCtx()
let res = try ctx.count(for: fetch)
however I get compile error:
Cannot invoke 'count' with an argument list of type '(for: NSFetchRequest<NSFetchRequestResult>)'
however when I create the fetch request from FileRecord like this instead:
let fetch: NSFetchRequest<FileRecord> = FileRecord.fetchRequest()
I get the following error:
Cannot convert value of type 'NSFetchRequest<FileRecord>' to expected argument type 'NSFetchRequest<NSFetchRequestResult>'
Please anyone got an idea what can be wrong? I am a decent C++ language lawyer but I am just a novice with Swift...
EDIT: SOLVED
My example above was wrong. In reality I had code functionally identical to:
let res: UInt64 = try ctx.count(for: fetch)
since count returns Int, the function did not match. The error message was not specific enough for me to see it immediately. Thanks to all who have tried to help :)

How to delete an entire [AnyObject] array retrieved with a `findObjectsInBackgroundWithBlock?

Does anyone know if its possible to delete an entire [AnyObject] array retrieved with a findObjectsInBackgroundWithBlock query?
The code bellow deletes one array item per time. But not all of them at once.
Have tried comments?.removeAll(keepCapacity: true) but compiler gives me an error: Immutable value of type '[AnyObject]' only has mutating members named remove all
getObjectInBackgroundWithId does not work as it only gets one object per time.
Quite stuck here... didn't find anything around about removing the whole array.
println("QUERY - DELETE COMMENTS")
var query = PFQuery(className: "Comments")
var post = currentObject
query.whereKey("bellongsToPost", equalTo: post)
query.findObjectsInBackgroundWithBlock { (comments: [AnyObject]?, error: NSError?) -> Void in
for comment in comments! as [AnyObject]
{
comment.deleteInBackground()
}
}
Just do:
PFObject.deleteAll(comments)
Obviously you need to unwrap comments, and do try catch if using Swift 2

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)