performQuery skips CompletionHandler - cloudkit

My completionHandler is being skipped over in my performQuery.
let container = CKContainer.defaultContainer()
var publicDB: CKDatabase!
publicDB = container.publicCloudDatabase
let query = CKQuery(recordType: "Centers", predicate: NSPredicate(value: true))
publicDB.performQuery(query, inZoneWithID: nil, completionHandler: { results, error in
if error != nil
{
dispatch_async(dispatch_get_main_queue())
{
println("error loading: \(error)")
}
}
else
{
self.centerResults = results
}
})
var center = Center()
for item in centerResults
When I get to that bottom "for" statement, centerResults is nil. My intent is to read all the entries in my Public "Centers" schema.
In my Dashboard, I have a "Centers" schema, with 4 Public records.
What could be wrong?

The performQuery is an asynchronous call. so you will probably reach the for loop before the completionHandler has been called. Try moving the code at the bottom to inside the completionHandler. And set a debug on the first line in the completionHandler to see what's going on.

Related

A function always returns nil, due to misplacement of statements assigning values

In such a function I get a nil value but I do not understand why. The code in the middle returns an image (and I'm sure about it, I've also checked with some print statements). I do not know how is it possible that it always returns nil. It is like it ignores all the code running in the middle and considers just the first and last statement.
func getImagesDownloaded(reference: StorageReference) -> UIImage {
var imagePassedIn : UIImage?
reference.getData(maxSize: 10*1024*1024) { (data, error) in
guard let imageObject = UIImage(data: data!) else {print("Error has occurred: \(String(describing: error?.localizedDescription))"); return}
imagePassedIn = imageObject
}
if imagePassedIn == nil {
print("Error, getImagesDownloaded is not working")
}
return imagePassedIn!
}
The issue is that StorageReference.getData is an asynchronous function, but you're trying to synchronously return a value. You need to use a completion handler to return the asyncronously retrieved value.
func getImagesDownloaded(reference: StorageReference, completion: (UIImage?,Error?)->()) {
reference.getData(maxSize: 10*1024*1024) { (data, error) in
guard error == nil, let data = data else {
completion(nil,error)
return
}
guard let image = UIImage(data: data) else {
completion(nil, FirebaseErrors.expectedImage)
return
}
completion(image,nil)
}
}
enum FirebaseErrors: Error {
case expectedImage
}
Then you need to use it like this:
getImagesDownloaded(reference: yourStorageReference, completion: { image, error in
guard let image = image, error == nil else {
print(error)
return
}
// Assign your image to a UIImageView or do anything else with it inside the closure (before the ending `}`)
yourImageView.image = image
})
You are using a closure and the imageObject is probably not returned when you are doing the nil check.
Swift is executed line by line and when you have async code it executes the next line and doesn't wait for the result.
you should move the imagePassedIn check in the closure.
You're setting imagePassedIn inside a completion block. This means that when you are ready to set it, you've already retuned it.
In a few words, when using completion blocks, the code bellow won't be waiting for it to finish in order to execute.
Update your functions to this:
func getImagesDownloaded(reference: StorageReference, _ completion: (UIImage) -> ()) {
reference.getData(maxSize: 10*1024*1024) { (data, error) in
guard let data = data, let imageObject = UIImage(data: data) else {print("Error has occurred: \(String(describing: error?.localizedDescription))"); return}
completion(imageObject)
} else {
print("Error, getImagesDownloaded is not working")
}
}

Not using force unwrapping in guard statement when throwing an ErrorType in Swift

I want to remove force unwrap (fetchRequestError!) from this code. I know that It must not make problems because when mainQueuePosts is nil fetchRequestError has value. But I want to make it better. And I don't want the retu
func fetchMainQueuePost(predicate predicate: NSPredicate? = nil,
sortDescriptors: [NSSortDescriptor]? = nil) throws -> [SeenPosts] {
let fetchRequest = NSFetchRequest(entityName: "SeenPosts")
let mainQueueContext = coreDataStack.mainQueueContext
var mainQueuePosts: [SeenPosts]?
var fetchRequestError: ErrorType?
mainQueueContext.performBlockAndWait() {
do {
mainQueuePosts = try mainQueueContext.executeFetchRequest(fetchRequest) as? [SeenPosts]
} catch let error {
fetchRequestError = error
}
}
guard let posts = mainQueuePosts else {
throw fetchRequestError!
}
return posts
}
My solution, which is not good is:
guard let posts = mainQueuePosts else {
if let err = fetchRequestError {
throw err
}
fatalError()
}
fatalError() is never executed. but I think its not a good idea
The usual way to throw an error in a method which throws itself is to hand over the error to the caller by removing the do - catch block.
As a standard fetch is synchronous anyway you don't need performBlockAndWait() and you can safely forced downcast to [SeenPosts] because the fetch request is distinct.
If the fetch succeeds the array will be returned, in case of an error the error will be thrown.
func fetchMainQueuePost(predicate predicate: NSPredicate? = nil,
sortDescriptors: [NSSortDescriptor]? = nil) throws -> [SeenPosts] {
let fetchRequest = NSFetchRequest(entityName: "SeenPosts")
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = sortDescriptors
return try coreDataStack.mainQueueContext.executeFetchRequest(fetchRequest) as! [SeenPosts]
}

How to ensure a query happens before another query within the same function?

I have a function which has multiple queries set up in it. The function is long and involves alot, but works perfectly, save for one issue. There is a query which depends on some information with the query before and it occasionally crashes the app when the query before it is not totally done and caused the second one to unwrap a nil value. Here is the general setup:
func QueryandAppend(completion: (() -> Void)?){
let userLinkQuery = PFUser.query()//query1
userLinkQuery?.findObjectsInBackground(block: { (objects, error) in
if let users = objects{
for object in users{
if let user = object as? PFUser{
userLinkArray[user.objectId!] = user.username
}
}
}
})
let appendingQuery = PFQuery(classname: "Stuff")//query2
appendingQuery.findObjectsInBackground { (objects, error) in
for object in objects{
creatorArray.append(userLinkArray[object["User"] as! String]!)
}
...
completion!()
}
The completion handler is used to make sure the entire function (and all of the queries) have been completed before running something in the viewDidLoad(). How do I ensure, though, that within the function query1 is done before query2 can run?
The prototypical way to do this with network and other asynchronous operations is to chain the operations, perhaps even break the individual network operations up each into a specific routine. The individual operations have completion callbacks, so just invoke your second routine from the completion of the first.
func QueryandAppend(completion: ((Bool) -> Void)?) {
PFUser.query().findObjectsInBackground { (objects, error) in
guard error == nil, let users = objects else {
completion?(false)
return
}
for object in users {
if let user = object as? PFUser,
let objectId = user.objectId {
userLinkArray[objectId] = user.username
}
}
PFQuery(classname: "Stuff").findObjectsInBackground { (objects, error) in
if error != nil {
// Handle error case
completion?(false)
return
}
for object in objects {
creatorArray.append(userLinkArray[object["User"] as! String]!)
}
completion?(true)
}
}
}
Alternatively, for a more modern stylistic approach, you could take a look at a good Promise framework, such as PromiseKit

Using the output of a function in another function?

i have a fetch request and i want to use the result to inform other functions. the fetch is
func getExercises() {
guard let managedObjectContext = managedObjectContext else { return }
let userExercise = UserExercise(context: managedObjectContext)
let request: NSFetchRequest<UserExercise> = UserExercise.fetchRequest()
request.predicate = NSPredicate(format: "usersroutine == %#", self.routineName)
do {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let queryResults = try context.fetch(request)
print ("num of results = \(queryResults.count)")
for exercise in queryResults as [NSManagedObject] {
print("Exercise NAME: \(exercise.value(forKey: "name"))")
}
} catch {
print("Error with request: \(error)")
}
The other functions are (capitals added where id want the output to be):
func configure(_ cell: TodaysRoutineTableViewCell, at indexPath: IndexPath) {
let userExercise = THE GET EXERCISE FUNCTIONS OUTPUT(at: indexPath)
and another:
fileprivate func updateView() {
var hasUserExercises = false
if let UserExercise = THE GET EXERCISE FUNCTIONS OUTPUT {
hasUserExercises = UserExercise.count > 0
}
I thought perhaps id just add queryResults in there and it would be fine, but as thats local to that function i just get errors. Whats the correct way to achieve this?
updated to show errors image
// MARK: - COREDATA FETCH
func getExercises(completion: (([NSManagedObject]?, NSError?) -> Void)?) {
guard let managedObjectContext = managedObjectContext else { return }
let userExercise = UserExercise(context: managedObjectContext)
let request: NSFetchRequest<UserExercise> = UserExercise.fetchRequest()
request.predicate = NSPredicate(format: "usersroutine == %#", self.routineName)
do {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let exercises: [NSManagedObject]?
do {
let queryResults = try context.fetch(request)
print(queryResults)
for exercise in queryResults as [NSManagedObject] {
print("Exercise NAME: \(exercise.value(forKey: "name"))")
}
completion(exercises, nil)
} catch {
print("Error with request: \(error)")
completion(exercises, error)
}
}
}
Use of unresolved identifier 'exercises'
Based on the code you have shown in the image, reason for you first error is that, you have used the property exercises which is not declared yet. exercises in your completion closure's argument will not be accessible within that function. Its a parameter that needs to be passed to that closure.
When you call the completion block, you will have to pass an array of NSManagedObject to it which you construct after you fetch the results. In your case, construct an array within that for..in loop and call the completion block after that loop.
let exercises: [NSManagedObject]?
do {
let queryResults = try context.fetch(request)
print(queryResults)
for exercise in queryResults as [NSManagedObject] {
//Using the result, build the exercises array here
}
completion(exercises, nil)
} catch {
print("Error with request: \(error)")
completion(exercises, error)
}
Nil is not compatible with expected argument type '[NSManagedObject]'
This is because exercises argument is not an optional but you are passing nil to it. You will have to make the exercises argument as optional to take both non-nil and nil values. Change your method signature to below:
func getExercises(completion: ((exercises: [NSManagedObject]?, error: NSError?) -> Void)?)
Note: Make sure you handle nil appropriately in your completion closure you pass to the getExercises function.
Being fetch something that you could do in an async way, the right way would be to use a completion block (callbacks).
So your getExercises function could have a completion block parameter, for instance:
func getExercises(completion: ((exercises: [String], error: NSError?) -> Void)?)
Then, when your data is ready inside the getExercises function, you can invoke the completion block as follows:
completion?(exercises, error)
This question mark before completion is because the completion block is an optional parameter, so you could pass this block or not, up to you.
Maybe some update is required for Swift 3, just check the syntax for completion blocks.
Edit: You will need to pass a closure as an argument for your getExercises function when you call it:
getExercises(completion: { (exercises, error) in
//Do what you want with the result, this is the result of getExercises
print(exercises)
})
Now, inside getExercises you will pass the result to your closure:
func getExercises() {
// ...
do {
// ...
completion(exercises, nil)
} catch {
print("Error with request: \(error)")
completion?([], error)
}
}
Exercises will be of a certain type, not sure what you are handling there, I placed an array of String as example.

Variables goes nil after execution of block

I'm having difficulty assigning a value after execution of a closure.
The problem is in //step4. It prints the contents and it captures no nil,
but at //step5 it prints Nil.
I tried to creating a class and assigning the values within the HTTP Request, but no change.
My code:
var x: String!override func viewDidLoad() {
super.viewDidLoad()
//1
let urlAsString = "http://date.jsontest.com/"
let url = NSURL(string: urlAsString)!let urlSession = NSURLSession.sharedSession()
//2
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: {
data, response, error - > Void in
if error != nil {
println(error.localizedDescription)
}
var err: NSError ?
//3
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: & err) as!NSDictionary
if err != nil {
println("JSON Error \(err!.localizedDescription)")
}
//4
let jsonDate = jsonResult["date"] as!String
let jsonTime = jsonResult["time"] as!String
//Step 4
//let's Assume
x = jsonDate
println("Inside \(x)") // Prints Date :)
dispatch_async(dispatch_get_main_queue(), {
self.dateLabel.text = jsonDate
self.timeLabel.text = jsonTime
})
}) //end of JsonQuery
jsonQuery.resume()
//Step 5
println("Outside \(x)") // Prints nil
}
Your web request is an asynchronous request. That means it will finish, and the completion block will be called, at some point in the future. Some time long after you call jsonQuery.resume(). Some time long after your whole function returns.
Your variable doesn't go back to nil after the call - you check it long before it is ever set to nil. It is the job of your completion block to do everything that is necessary to process the data and store the results.
Your "step 5" will likely execute before steps 3 or 4. Calling resume does not block and wait for the response to compete so you execute step 5 before the completion block is called.