Delay App Progression Until Firebase Data Retrieval Is Finished - swift

In my app, I store data on the Firebase database (Firestore AND Storage) in the form of "Files" (what the user sees). When the user goes to their "Files" tab and selects a certain file (example: "Smith vs Wesson"), the app downloads data on the server (from BOTH Firestore and Storage) related to that file. Here's my problem: the app moves forward before the data has even finished returning and processing (sorted/stored into variables). I don't want the app to progress and take the user to the next screen until this is totally complete. The next three screens show the data retrieved from the server, so if it's moving forward before the data has even retrieved and sorted... well... you see the problem with that.
I tried using something like DispatchQueque.main.asyncAfter to add a three second delay, but the problem with this is that if the user's internet connection is poor, it may take longer than three seconds to retrieve the data. Likewise, if their internet connection is booming, it may take only a second to retrieve the data, but they're still stuck waiting on an unnecessary three-second delay. I only want the delay to last as long as it takes for the retrieval/sorting/storing function to complete its tasks... no matter how short or long.
I'm still learning and am mostly self-taught, so forgive my ignorance. From what I understand from the reading I've been doing, tasks are based off of "threads." The main thread is what the user sees, while there are threads that tasks can be done in the background to keep the user from experiencing longer wait times, etc (such as data retrieval from a server). I know typically you don't want to do tasks on the main thread, but in this case where I don't want the user to be able to progress, I need to find a way to pause the main thread until the other thread has completed the data retrieval and sorting/storing process.
I stumbled across something called "CountDownLatch." I read about it and kind of understand the concept of it... but not the code at all, to be honest. I don't know if CountDownLatch is the correct method to use here or not, but if it is, could someone please show me how I could use either CountDownLatch, or some other delay, to pause the progression of the app until the data is retrieved, sorted, and stored into the variables?
My data retrieval/sorting/storing function is called "getAndAppendClaimData." I handle all of those steps in this function, and it works perfectly. Like I said, I just need to provide some delay until it's finished, so that the code underneath this function isn't executed thereby segueing to the next screen.
So something kinda like this:
while getAndAppendClaimData is still processing {
showLoadingAnimation
}
once getAndAppendClaimData has finished **ALL** of its tasks {
performSegue to next screen
}
NOTE: I DON'T use listeners in my app because I don't need to update the user's screen in realtime... like with a messaging app or something. I just use the .getDocuments and documents.forEach functionalities for my data retrieval.
Please explain your answers or provide links to content explanations. You remember how it was when you were still learning..
Also, before some of you call this post a duplicate.. the other threads are outdated and most of them deal with apps that have listeners for realtime updates – which is different from my circumstance. Another thing, I'm doing a lot of research and learning... so please don't drop the whole "go do your research" bomb. Sometimes you need help tailoring things to your specific situation.
Thanks, I really appreciate the help!

Okay so after more research, I found that one way to keep your app from progressing while server data is downloading and sorting is with DispatchGroup.
First of all, create a DispatchGroup variable:
let dispatchGroup = DispatchGroup()
Then you can "enter" the group at the beginning of the call/function and "leave" the group at the end, once everything has finished processing (such as in completion in a Firebase call). If you're utilizing a loop to sort your data, then make sure to enter dispatchGroup
dispatchGroup.enter()
every time you enter the loop and leave dispatchGroup
dispatchGroup.leave()
at the end of each loop iteration. Once you're finished entering and leaving dispatchGroup for good, then call:
dispatchGroup.notify(queue: .main) {
// Here write whatever you want to do after it's finished retrieving and processing the data...
// Such as performing a segue
}
You would call this .notify outside of your loop, of course.
In my situation, I had a two loops: one to gather/store server data and one to sort it. I didn't want it to start trying to sort until it had finished gathering/sorting, so I executed the second loop inside dispatchGroup.notify, then performed my segue after the second loop finished.
Watch these three tutorials, they helped me out big time!
DispatchQueues
DispatchGroups
Semaphore vs DispatchGroup

Related

How To Cancel Firebase Query in Swift Xcode

How would you cancel a firebase query? For instance, I have two functions. In functionOne I retrieve data from Firebase Firestore. In functionTwo, I'd like to be able to cancel the retrieval process if the user hits "Cancel."
In other words, I don't want the retrieval process going on in the background still when the user cancels. I want it terminated. How can I do this?
Thanks!
I couldn't find anything on the internet about this so far.
It sounds like you have a large data set - in those cases, loading it all in at one time can overwhelm the device and cause your app to crash.
The solution is to use pagination to control the amount of data that's loaded.
While that's the main purpose of paginating your data, it also very useful when you want to give control the user and the ability to 'cancel' a query.
Simply put, if your dataset is 100,000 documents, use pagination to load in 1000 at a time and in between incrementing the pagination cursor, check to see if the user has clicked cancel.
That will be seamless to the user and will cancel when they click cancel.
This technique also enables you to add a loading bar or other indicator to the UI showing how much data has loaded.
There is no way to cancel a Firestore query once it's been started. As in: once the initial query has been sent to the server, there's no way to stop the server from executing it.
But you can stop receiving further updates by detaching any listeners from the queries.

Why my form go to the top of my screen when my UI freeze?

I have a little issue with a form in a delphy XE2 application:
It's an old issue on this application and i have begin to work on it just since a little time.
When the user choose to launch the process with a button's event, my application launch a connexion with an OPCServer , an SQLServer and construct the form for a good following of data take on the tow servers.
The construction of my form involves a blockage of the interface (for approximately 15 sec) because of lot's of data which are necessary for make it.
When it freeze, if the user want drag the form, she go far away, and usually with the TMainMenu which go out of the screen. After that, it's impossible to use the application because we can't drag and we need to close and re-open.
In the old version, the form be already construct before the connexion. So the modification for a dynamic form isn't in link with this issue.
Life of my event :
-Open connexion with OPC Server
-Open SQL Connexion
-Send SQL Command Text
-FieldByName('') for update my UI (Button.Caption// TPage.TStaticText.Caption // TPage.Label1.Caption)
-FieldByName('') for update an array of record
-Close SQL Connexion
-Open SQL Connexion
-Send SQL CommandText
-FieldByName('') for update an other array of record
-Panel.Visible(false)
-TPage.Panel.Show;
-TPage.Panel.BringToFront;
So I haven't MainForm modification can change its position.
I'm a young developer, so I don't know why it moving and what I can make for repair that...
If you want a part of code, ask me what and i edit this, it's very long and i don't want spam answer.
Thank's for read.
The core of your problem is that you have a lengthy process (form construction) which completely blocks the main thread so your application isn't able to process normal Windows messages at the same time. That is why when you move your application it doesn't properly update its interface.
Now based on your description you already have this form construction process split into multiple steps so you could call Application.ProcessMessages between them.
This will force your application to update its UI part.
But beware calling Application.ProcessMessages often could hurt your application performance quite a bit. Why? It is usually a lengthy process because it forces your application to process all the messages that are in its queue.
Normally not all of these messages get processed as soon as they arrive. Windows groups them in the message queue by their priority list, making sure that high priority messages like WM_PAINT are processed as soon as possible while some other low priority messages like demand for application to respond to OS so that OS can see if the application is still working are mostly processed when application is idle or when they are in queue for certain amount of time.
So that is why Application.ProcessMessages can be so slow as it forces your application to process all messages regardless of their priority.
Also bear in mind that using Application.ProcessMessages can in certain scenarios actually become a bit dangerous.
Let me give you an example:
Lets say that clicking on a button starts a lengthy job which can take some time to finish. Now in order to allow your form to be updated you call Application.ProcessMessages in certain intervals. So far it is all good. But what happens if user clicks on that button again?
Since you are calling Application.ProcessMessages which forces your application to process all the messages and since clicking on button creates a MouseClick message which then fires buttons OnClick event which then executes the OnClick method that has been assigned to buttons OnClick event in the end this will cause the same method that was executed on first button click to be executed again.
So now you have this method partially done from first button click and same method executing again for second mouse click. Now the method that was executed from the second click will finish first and then the method that was started from first button click but was interrupted with Application.ProcessMessages handling the second button click will continue its execution to the end.
This all can lead to strange bugs which are hard to track, because you as a programmer normally don't predict that your end user might have clicked the button twice.
So to avoid this I strongly recommend you implement some safeguard mechanisms to prevent such scenarios by temporarily disabling a button for instance.
But the best solution is always to show your user that your application is working which in most cases will dissuade them from clicking the button again, but unfortunately not always.
You should also take a good care when dynamically constructing a form to enable the controls only after all of the controls have been successfully constructed. Failing to do so the user might click on one of your controls and that control could attempt to access some other control which hasn't yet been created at the time. The result hard to track bug which causes Access Violation.
You might also consider showing a splash screen at start instead of half built form. Why?
For once it is much nicer to see and it tells your users to wait a bit. And for second having main form hidden until it is fully created makes sure that user won't be doing any clicks on it prematurely.

Better Handling of getting data from server in iPhone

I need to improve my coding.so i am finding something better.
My problem is i need to fetch the data from server from 10 different url.that url have images 100.
for example i need to hit
http://192.168.11.222/images/a
http://192.168.11.222/images/b
http://192.168.11.222/images/c
http://192.168.11.222/images/d
http://192.168.11.222/images/e
http://192.168.11.222/images/f
http://192.168.11.222/images/g
http://192.168.11.222/images/h
http://192.168.11.222/images/i
so a b c d e are folder on server that contain images.
Currently i am doing this through NSURLConnectionWithTag and then parse the response.and get saved.is there any another better way to handle this? i also need to show progress bar which is also difficult in this case.
I would setup an NSOperationQueue ,with a single operation per URL and set it to, say, three concurrent operations. Then use NSURLConnection's non-asynchronous API to do the download.
For your progress bar, it's probably good enough to update the progress after each individual file is finished, and do two of them at a time (or something). Chances are latency will be more than half the "progress" anyway, so unless you start trying to predict your ping times, a progress bar based on the actual bytes transferred will not be accurate enough to bother (unless these are very big images).
You will need to learn how operation queues and GCD work, but once you've got that sorted it really won't be much code at all, and it will be rock solid.
Basically you want to add a "block" of code to the operation queue for each URL to download, and the queue will figure out how to download each one, and then when each individual block of code is finished it executes another block on the main thread (dispatch_sync(dispatch_get_main_queue(), ^{ ... })) to update the progress bar.
If you are going to write network related code in your app, I recommend take a look at AFNetworking, it's a wrap of network API iOS already provided with much less hassle.

How to stop runaway processes

So I'm working on this application that requests and retrieves webservice content for iPhone. The problem I am running into is this: When I initially request data, it is spawned off as an independent thread so that the application does not become unresponsive due to the network being slow. What this means is that if the user navigates away from the current page before this data finishes downloading, unexpected things can happen.
I have managed to narrow down the problem cases to one relatively simple one: I have some nested tables, so if a user goes down into the "Messages" table, which can sometimes take a little while to download, then back out immediately, and select a different set of messages to view, the previous set of messages ends up loading, because it was still in the queue.
Here are things I have tried:
1) I tried cancelling the operations, but this is futile, because since I only allow one operation in the queue at the time, it triggers immediately
2) I tried validating that the recipient of the data is the same, but this doesn't work because the actual table object is the between the two selections, it just needs a different data set.
Anyone have any general programming suggestions on how to solve this tricky threading problem?
On an iPhone specific note: if I could just stop the user from being able to back out of the messages table, I wouldn't have this problem, because they would basically be locked into that view until the data has finished loading.
Thanks!
This post has some design advice relating to iOS networking and threading. The basic gist of it is "Don't use explicit threading", and I couldn't agree more. NSURLConnection has great built-in functionality for asynchronously loading data from a URL while managing all of the threading for you. They can also be cancelled easily at will.
If you were to use the NSURLConnection paradigm, you can simply cancel any pending request when you back out of the requesting view controller.

Data driven view iOS app

I am new to objective-c/cocoa programming. I am making an application which is to constantly sync with a server and keep its view updated.
Now in a nutshell, heres what I thought of: Initiate an NSTimer to trigger every second or two, contact the server, if there is a change, update the view. Is this a good way of doing it?
I have read elsewhere that you can have a thread running in the background which monitors the changes and updates the view. I never worked with threads before and I know they can be quite troublesome and you need a good amount of experience with memory management to get most out of them.
I have one month to get this application done. What do you guys recommend? Just use an NSTimer and do it the way I though of...or learn multithreading and get it done that way (but keep in mind my time frame).
Thanks!
I think using separate thread in this case would be too much. You need to use threads when there is some task that runs for considerable amount of time and can freeze your app for some time.
In your case do this:
Create timer and call some method (say update) every N seconds.
in update send asynchronous request to server and check for any changes.
download data using NSURLConnection delegate and parse. Note: if there is probability that you can receive a huge amount of data from server and its processing can take much time (for example parsing of 2Mb of XML data) then you do need to perform that is a separate thread.
update all listeners (appropriate view controllers for example) with processed data.
continue polling using timer.
Think about requirements. The most relevant questions, IMO, are :
does your application have to get new data while running in background?
does your application need to be responsive, that is, not sluggish when it's fetching new data?
I guess the answer to the first question is probably no. If you are updating a view depending on the data, it's only required to fetch the data when the view is visible. You cannot guarantee always fetching data in background anyway, because iOS can always just kill your application. Anyway, in your application's perspective, multithreading is not relevant to this question. Because either you are updating only in foreground or also in background, your application need no more than one thread.
Multithreading is relevant rather to the second question. If your application has to remain responsive while fetching data, then you will have to run your fetching code on a detached thread. What's more important here is, the update on the user interface (like views) must happen on the main thread again.
Learning multithreading in general is something indeed, but iOS SDK provides a lot of help. Learning how to use operation queue (I guess that's the easiest to learn, but not necessarily the easiest to use) wouldn't take many days. In a month period, you can definitely finish the job.
Again, however, think clearly why you would need multithreading.