0

I am trying to load photos in Core Data but this requires some background threads, so it happens that my Object gets saved in Core Data without its photo, I get that photo is nil. And the same is happening with the photos of my groups.

In short, at each iteration I save an object in Core Data and the saving happens faster than the loading of my Photo data.

I tried Semaphors, barriers, groups,... but none worked. I am certainly doing something wrong but I don't know what. If someone could help me I would really appreciate, it has been 2 weeks I am struggling with this same problem. This is my code in a simplified version, the full version is accessible below:

// MARK - ViewDidLoad
override func viewDidLoad() {
    // This method is only called once, so you want to create any controls or arrays here
    super.viewDidLoad()

    // Verifying connectivity to internet
    if Reachability.isConnectedToNetwork() == true {

    // Getting data from Parse + getDataInBackgroundWithBlock

    }else{

    }
}


// MARK - Action of the login button
@IBAction func loginBtn_click(sender: AnyObject) {
    if usernameTxt.text == "" || passwordTxt.text == "" {
        self.displayAlert("Error", message: "Please enter a username and password")
    }else{

        // MARK - Login the user
        PFUser.logInWithUsernameInBackground(usernameTxt.text!, password: passwordTxt.text!) { (user, error) -> Void in

            if error == nil {

                let queue = dispatch_queue_create("com.karagan.app.queue", DISPATCH_QUEUE_CONCURRENT)

                dispatch_async(queue, { () -> Void in
                    self.getObjectsFromQueryWithAscendingOrder("Add", key: "user", value: userName, ascendingOrder: "added", completionHandler: { (objects1) -> Void in

                // Update of an array in background 

                    })
                })



                dispatch_async(queue, { () -> Void in

               // Update of an array in background

                })



                dispatch_async(queue, { () -> Void in
                    self.getObjectsFromQueryWithAscendingOrder("Group", key: "membersUsername", value: userName, ascendingOrder: "membersUsername", completionHandler: { (objects3) -> Void in

                 // Update of an array in background

                        // MARK - Adding all the users in Core data
                        for var i = 0; i < usersFromParseDataBase.count; i++ {

                            let entity = NSEntityDescription.entityForName("Users", inManagedObjectContext: self.managedObjectContext)
                            let newUser = Users(entity: entity!, insertIntoManagedObjectContext: self.managedObjectContext)
                            if self.arrayAddedUsers.contains(usersFromParseDataBase[i].username){

                // Saving new records in Core Data                  

                            }
                        }
                    })
                })

                dispatch_async(queue, { () -> Void in
                    self.getObjectsFromQueryWithDescendingOrder("Group", key: "membersUsername", value: userName, descendingOrder: "conversationUpdate", completionHandler: { (objects4) -> Void in

                            if array.count == 2 {

                    // Getting data in background - photo.getDataInbackgroundWithBlock                  

                                }
                            }else{

                // Getting data in background - photo.getDataInbackgroundWithBlock

                            }
                        }
                    })
                })

                dispatch_barrier_async(queue, { () -> Void in

        // Doing some stuff in background which needs the data from threads above 

                })

            }else{
                // Print error from loginWithUsernaemInBackground
            }
        }
    }
}

This is what gets displayed to the log, so you can clearly see that the barrier gets executed before the loading of pictures is done executing.

TEST 3 - interacting user has been saved to core data
Loading profile image
Loading profile image
Loading group image
Loading profile image
Loading profile image
9
5
Loading group image
Loading group image
Loading group image
Loading group image

The thing is that I need this data for other operations later. My code is not consistent.

C00kieMonsta
  • 338
  • 3
  • 20
  • 1
    you might want to take a look at dispatch_group_create(), dispatch_group_wait() and dispatch_group_async() in order to have a group of tasks that you want to wait to finish before you go on. This link might help: http://stackoverflow.com/questions/11909629/waiting-until-two-async-blocks-are-executed-before-starting-another-block – dee zg Apr 24 '16 at 10:25
  • If the code blocks that you're dispatching are, themselves, asynchronous tasks, you might have to explicitly `dispatch_group_enter`/`leave`. It will be hard to diagnose your problem or advise further without seeing the code that generates these log messages. Or, frankly, because your code is so complicated, you may want to construct [a much simpler, reproducible example](http://stackoverflow.com/help/mcve) that manifests the sort of bug you describe. – Rob Apr 24 '16 at 10:29
  • Hello Rob, I added a simpler version of my code with only the most important parts. Is it comprehensive ? If not I can try to make it simpler. Thanks for your help. dee zg, I already tried dispatch_group_create()... but I still got the same problem. It is just that I don't know how to implement it correctly. I always get the same problem unless I add "sleep(Uint32)" to my last block, but this is not consistent. – C00kieMonsta Apr 24 '16 at 17:12
  • If someone finds a solution to my problem I would be really thankful cause it's been something like 3-4 weeks I am stuck with this problem and none of the questions on stack overflow were helpful unfortunately. – C00kieMonsta Apr 24 '16 at 18:01

1 Answers1

-1

I think you are on the right track. When I create a simple program and use dispatch_barrier_async it works perfectly:

let queue = dispatch_queue_create("com.karagan.app.queue", DISPATCH_QUEUE_CONCURRENT)

dispatch_async(queue, { () -> Void in
    sleep(1)
    print("1")
})

dispatch_async(queue, { () -> Void in
    sleep(2)
    print("2")
})

dispatch_async(queue, { () -> Void in
    sleep(3)
    print("3")
})

dispatch_async(queue, { () -> Void in
    sleep(4)
    print("4")
})

dispatch_barrier_async(queue, { () -> Void in
    print("done")
})

Output:

1
2
3
4
done

I think the problem is that dispatch_async doesn't wait for the call toPFFile.getDataInBackgroundWithBlock to finish:

photoFile.getDataInBackgroundWithBlock({ (imageData, error) -> Void in
    if error == nil {
        self.arrayImageFiles.append(imageData!)
        print("Loading group image")
    }
})

Try switching all calls to getDataInBackgroundWithBlock with the synchronous call to PFFile.getData (https://parse.com/docs/osx/api/Categories/PFFile%28Synchronous%29.html#/c:objc%28cs%29PFFile%28im%29getData:) instead:

var imageData = photoFile.getData
if (imageData != nil) {
    self.arrayImageFiles.append(imageData!)
}

This shouldn't really have any performance issues since you are already executing this code asynchronously.

markwatsonatx
  • 3,391
  • 2
  • 21
  • 19
  • Thanks Mark for your answer. I still have to do some tests, but this might help me. I did not know it was possible to load the image data on the main thread. I really appreciate your help and will keep you informed if it really solves my problem. – C00kieMonsta Apr 25 '16 at 16:58
  • Using `dispatch_barrier_async` _can only_ be used as a "sentinel" iff the previously submitted tasks to the same queue are _synchronous_. In the OPs case, they are asynchronous, though. That is, the call returns _immediately_ and - for the queue - this block has been finished. – CouchDeveloper Apr 27 '16 at 07:24
  • @CouchDeveloper This is what I said in my answer and why I recommended making those tasks synchronous. Am I missing something? – markwatsonatx Apr 27 '16 at 07:41
  • Network requests should not be performed synchronously. One viable approach is to use dispatch groups as suggested by @dezg in her/his comment in the OPs question. The answers pointed by the link are correct and are well explained. – CouchDeveloper Apr 27 '16 at 07:46
  • So, in this case since the network call is done in an async block it shouldn't freeze up the UI, but it will force the images in each block to wait for the previous image in the same block to load. – markwatsonatx Apr 27 '16 at 08:07