Anyone know how I can show a UIProgressView while I save synchronous to parse.com?
I try to show a progress view before I start the sync save and hide it after the save is done, but this doesn't work. It doesn't show the progress view and just start save right away.
I am starting to think that the sync save takes all the power from everything else and a async save is the best for this issue. But in my case I have to save synchronous since I show the saved data directly after it is saved.
Anyone know how this can be done?
self.startProgress()
self.saveSynchronousToParse()
self.stopProgress()
A 'synchronous' method is also known as a 'blocking' method - it effectively blocks the current thread until it completes.
By default your app is running in the main queue, and this is the queue that performs all of the UI tasks, so once you call "saveSynchronousToParse" (which presumably calls save or some similar synchronous Parse function) your UI will freeze until the task completes. You will probably receive a warning in the console that you are executing a synchronous task on the main thread.
A progress view doesn't really make sense in this case, because you don't get any feedback from the Parse API as to how much progress has been made in saving the information.
A UIActivityView makes more sense. You can use the following to achieve what you are after using an UIActivityView
self.activityView.startAnimating()
self.somePFObject.saveInBackgroundWithBlock {
(success: Bool!, error: NSError!) -> Void in
dispatch_async(dispatch_get_main_queue(),{
self.activityView.stopAnimating()
});
if success {
println("success")
} else {
println("\(error)")
}
}
Related
So I've recently come back to Swift & iOS after a hiatus and I've run into an issue with asynchronous execution. I'm using Giphy's iOS SDK to save myself a lot of work, but their documentation is pretty much nonexistent so I'm not sure what might be happening under the hood in their function that calls their API.
I'm calling my function containing the below code from the constructor of a static object (I don't think that's the problem as I've also tried calling it from a cellForItemAt method for a Collection View).
My issue is that my function is returning and execution continues before the API call is finished. I've tried utilizing DispatchQueue.main.async and removing Dispatch entirely, and DispatchGroups, to no avail. The one thing that worked was a semaphore, but I think I remember reading that it wasn't best practice?
Any tips would be great, I've been stuck on this for waaaaaay too long. Thanks so much in advance
GiphyCore.shared.gifByID(id) { (response, error) in
if let media = response?.data {
DispatchQueue.main.sync {
print(media)
ret = media
}
}
}
return ret
My issue is that my function is returning and execution continues before the API call is finished.
That's the whole point of asynchronous calls. A network call can take an arbitrary amount of time, so it kicks off the request in the background and tells you when it's finished.
Instead of returning a value from your code, take a callback parameter and call it when you know the Giphy call has finished. Or use a promise library. Or the delegate pattern.
The one thing that worked was a semaphore, but I think I remember reading that it wasn't best practice?
Don't do this. It will block your UI until the network call completes. Since you don't know how long that will take, your UI will be unresponsive for an unknown amount of time. Users will think your app has crashed on slow connections.
You could just add this inside a method and use a completion handler and therefore do you not need to wait for the response. You could do it like this:
func functionName(completion: #escaping (YOURDATATYPE) -> Void) {
GiphyCore.shared.gifByID(id) { (response, error) in
if let media = response?.data {
completion(media)
return
}
}
}
Call your method like this
functionName() { response in
DispatchQueue.main.async {
// UPDATE the UI here
}
}
Are all operations and queries onto a Firebase realtime database asynchronous or synchronous or both?
In addition to this, what about Firebase authentication?
So I guess my question is: Do I need to put Firebase operations into a concurrent queue, or is it okay just leaving it in the main queue?
The thing about asynchronous programming is that it’s not really intuitive at first. If you want to fetch some data, it’s natural to want to write code that’s structured something like this:
try {
result = database.get("the_thing_i_want")
// handle the results here
}
catch (error) {
// handle any errors here
}
This is a synchronous call, and it’s short and easy to understand. The result of get() is being returned directly from the function, and the calling code is waiting for it to complete. But this is precisely the problem. You don’t want your code to stop to wait for something that could take a long time.
iOS/Swift:
Firestore.firestore().document("users/pat")
.getDocument() { (snapshot, err) in
if let snapshot = snapshot {
// handle the document snapshot here
}
else {
// handle any errors here
}
}
If you ask me, I’d rather have an asynchronous API that manages all the required threading behind the scenes. So it's always suggested to put Firebase operations into a concurrent queue, not in the main queue.
I just wanted to clear up something that feels a bit unclear for me. Consider the following code that executes a closure asynchronously:
func fetchImage(completion: UIImage? -> ()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
// fetch the image data over the internet
// ... assume I got the data
let image = UIImage(data: data)
dispatch_async(dispatch_get_main_queue()) {
completion(image)
}
}
}
To my understanding, the reason we need to dispatch back to the main thread is because it would otherwise take longer to call the completion closure to give back the image.
However, I feel that perspective is a bit cheesy. For example, I'd also like to create a isLoading property that would be used to prevent multiple network calls from happening at the same time:
func fetchImage(completion: UIImage? -> ()) {
// if isLoading is true, then don't continue getting the image because I want only 1 network operation to be running at 1 time.
if isLoading {
completion(nil)
return
}
isLoading = true
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
let image = UIImage(data: data)
// image creation is complete. Set isLoading to false to allow new fetches
self.isLoading = false
dispatch_async(dispatch_get_main_queue()) {
completion(image)
}
}
}
For this above snippet, my question is - Should I place self.isLoading = false in the dispatch block to the main queue? Or is it insignificant?
All advice appreciated!
It isn't that "it would otherwise take longer", it is that all updates to the UI must be performed on the main queue to prevent corruption that may occur from concurrent updates to the autolayout environment or other UI datastructures that aren't thread-safe.
In prior versions of iOS a common side effect of not updating the UI on the main thread was a delay in that upgrade appearing, however as of iOS 9 you will get an exception.
In terms of your question, it is best that your code behaves consistently. I.e. Either always dispatch the completion handler on the main queue or never do so. This will allow the programmer who is writing the completion block to know whether they need to dispatch UI updates or not.
It is probably best to set isLoading to false as soon as the load has finished, so outside the dispatch_async is best.
Given that your function is retrieving a UIImage there is a good chance that the caller will be updating the UI, so it is probably 'nice' to dispatch the completion handler on the main thread.
To fetch the image from internet in background you simply need to do an async request, you don't need to do it in the background queue as you are doing now.
On the main thread you basically need to do all that things about UI manipulation, because it always run on main thread. This is the important part.
So, the request completion block (the one you'll use to fetch the image) is executed in background (since it is async) and here, inside the block, you need to get the main thread to set the image for the UIImageView for instance.
Other properties than the ones directly related to UI element doesn't needs to be on the main thread as far as I know and I have never had a problem this way.
I'm trying to move my app over to MVC, I have a Parse query which I've moved over to a function in my model class, the function returns a Bool.
When the button in my ViewController below is pressed the model function 'parseQuery' should be run, return a bool and then I need to use that bool to continue. At the moment, the if statement is executed before the function has completed so it always detects false.
How can I ensure that the if statement is completed once the function has completed?
#IBAction func showAllExpiredUsers(sender: AnyObject) {
var success = searchResults.parseQuery()
if success {
print("true")
} else {
print("false")
}
//I have also tried:
searchResults.parseQuery()
if searchResults.parseQuery() {
print("true")
} else {
print("false")
}
You have a few options, but the issue is due to asynchronous calls.
Does Parse expose the same function, with a completion block?
If yes, then you place the processing of the Bool inside the completion block, which is called when the async task is completed.
If not, which I doubt, you could create an NSOperationQueue, with a maxConcurrency of 1 (so it is serial) and dispatch the calls onto the queue with
func addOperationWithBlock(_ block: () -> Void)
This is called on the queue. You would need to store the success bool globally so that you can access it inside the second queued block operation, to check the success state.
Update:
I haven't used parse, but checking the documentation for findObjectsInBackgroundWithBlock (https://parse.com/docs/ios/guide#queries) it takes a completion block where you can process the result, update your bool.
I'm not sure what you are trying to do. You don't need to have the success state of the query. You can check
if (!error) {
// do stuff
} else {
//error occurred - print("error \(error.localizedDescription)"
}
Check the example.
What you need to understand is threading. The async task provides a completion block because its asynchronous, it gets dispatched onto another thread for processing. I'm not sure how much you know about threading but there is something called a thread pool. This thread pool is accessed by Queues. The thread pool is managed by the OS, and makes sure available threads can be used by queues that need work done. As users interact with an application, this (and all UI work) is done on the main thread.
So whenever some processing is going to interfere with possible interaction or UI updates, it should be dispatched (Grand Central Dispatch) or queued (NSOperationQueue, built on top of GCD) off of the main thread.
Anyway, this is why the findObjectsInBackgroundWithBlock call is dispatched off the main thread, because otherwise it would block the main thread until its done, ruining the experience for the user. Also, if the main thread is blocked for more than 1 minute (last time I checked), the OS's watchdog will kill your process.
So yeah, assigning a boolean to the return of the block, would get the return of the function, which occurs before the completion block is done. The completion block is where you code some stuff to be done after the function completes. So the query gets dispatched onto another thread and starts processing, the thread that sent this work off for processing, continues with the rest of its execution. So checking the boolean directly after, wouldn't work because the other thread isn't complete. Even if the other thread finished in time, what is connecting the background thread with the main thread?
This is the beauty of blocks (function pointers), it's a lot cleaner and optimized and keeps code compact. The old way, which is still is use for some older frameworks, is delegates, which detaches the calling code with the callback and adds a delegate dependency. Blocks are beautiful.
Its also important to note that completion blocks don't always get called on the main thread. In many cases its up to you to dispatch the work back to the main thread, to handle any UI work that needs to be done with the objects available inside the completion block.
The query likely takes some time to run and should be run in a background thread with a callback function to handle the response WHEN it completes.
look at the Documentation
Specifically looking at the query.findObjectsInBackgroundWithBlock code:
var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
print(object.objectId)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo!)")
}
}
The above code will execute the query and run the code in the block when it gets the results from Parse. This is known as an asynchronous task, for more information check out this guide
I've been using NSOperationQueue's addOperationWithBlock: to run code in background threads, like so:
self.fetchDataQueue = NSOperationQueue()
for panel in self.panels {
self.fetchDataQueue.addOperationWithBlock() {
() -> Void in
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
//Background code
}
}
}
I'm concerned that I may be doing this wrong. I can't see a way that the fetch queue would be able to know when an operation is done, since there's no completion to call, and I'm not confident it's tracking activity across threads to make sure it's still going.
And the point of using this is so that I don't queue them up in single file and take much longer to process, and so I don't run them all at once and use too much memory.
EDIT: I'm aware that I don't need to be doing dispatch_async, but it's simply an example of some block-based code I may call which may do the same thing, or a web request which may get back after a delay.
Well, your code will run in a background block. If you are using a queue to make sure that one operation only starts when the next one is finished, you may be in trouble: The block that you happen to the NSOperationQueue has finished as soon as it has dispatched the background code to GCD, not when the background code has actually finished which may be much later.