How to make UIAlertView pause thread and wait user response, iOS dev - ios

Please read through all my question and see if it is duplicate with this question
Sometimes I believe iOS is the best OS and xCode is the most elegant IDE too, but sometimes I think they're both the stupidest thing in the world and maybe the whole universe!
What I need SEEMS easy, code logic like this
//code BEFORE asking user's decision
//with a lot of variables, this is important
//call UIAlertView to ask user Yes/No question, now the thread should be paused and waiting for user's tap on button
//code AFTER get user tap a button, selected YES/NO
//MANY variables before asking should be continuely used here
answerIsYES = //response from UIAlertView
if (answerIsYES)
{
//code response to YES
}else{
//code response to NO
}
If you noticed "MANY variables" before and after UIAlertView show, you will not suggest me to use UIAlertViewDelegate. If that, I have to make all the variables at class-level and what if I have to ask user's decision in FOR loop? That cannot jump to SelectedButtonAtIndex method and come back! Am I right? I want to make my code integrated but the UIAlertView and its delegate seems keeping split them.
Put it simple, what I want is just a MessageBox from .NET, it can wait for user's click. So is there any code that can ahieve this on iOS? Is there a code in iOS can just simple "pause" a thread and then triggerd by some event and "resume" it??
I have searched a lot and cannot find a satisfied answer, the answers involved "NSCondition", "NSThread", "loop", but none really works well. But I did found one in the link at first, respond by #Joseph Gagliardo, I don't know if that's the best solution.
Any suggestion will be greatly appreciated.

UIAlertView, like most other aspects of UIKit, is event driven. You don't want the main thread blocked while an alert is being displayed. Many times other things need to keep going while the alert is in view. But there is a good solution to your issue. Blocks.
UIKit classes that have been added more recently are starting to make use of blocks. This makes writing event driven code easier. It eliminates the need for separate delegate methods just to handle the completion of some event.
Do a Google search on "UIAlertView blocks". You will find several 3rd party versions of UIAlertView that make use of blocks. The use of this type of alert view will allow you to keep all of your code in the one method. These solutions don't pause the main thread or anything. It just makes it much easier for the completion block to access all of the variables you have setup just prior to displaying the alert view.

Related

Swift: Gui is not updated / much too late

I am currently programming a Quiz-App for iphone using swift and xcode. Since a week I a stuck with a problem, i wasn't able so solve up to today.
Little Background Information:
All Questions are stored online on a server and are downloaded just in time. The Questions always contain images, so it takes some time to load them. This is why I am pre-loading the next question in background while the user is still thinking about the current question. Therefore I use the class QuestionLoader which extends Thread.
The Problem Situation:
When the user answers a question and presses the continue-button, the function goToNextQuestion() is triggered which shows the next question to the user. Either the pre-loading of the next question is finished and it can be shown instantly or the next question is still loading and we have to wait.
The Problem:
Now if we have to wait, I want to show a Loading-Label and -Indicator. Both are in the gui (placed with interface-builder) and hidden at the time. I do it like this:
func goToNextQuestion()
{
//check if next question is already ready
if (nextQuestionLoader.isFinished==false)
{
//not ready, so wait and show waiting indication
labelLoading.isHidden = false
loadingIndicator.startAnimating()
while(nextQuestionLoader.isFinished == false) {}
//question i now ready, hide waiting indication
labelLoading.isHidden = true
loadingIndicator.stopAnimating()
}
//so next question is ready and all good
}
What I want to happen is that when the user pressed continue and the next question is not ready yet, that the loadingIndicator and the labelLoading are shown until the question is ready.
Now what happens is that continue is pressed the there is nothing shown and the gui is blocked util the question is ready, then it shows the next question and the same thing happens with the following question.
Screenshots of the area of my gui that is concerned for better visualitzation
(Sorry for the different number of points gained in the screenshots, does not have to do with the problem, simply 2 different screenshots)
So the gui update does not happen before the question is loaded completely and does not happen immediately as it should!
Additional Information
I tried the showing of the loading indicators at other points of the code and they show correctly, so there is no problem with this.
When i put print-command before and after the while-loop and follow the output in the terminal, I see that the print-commands are correctly executed before and after the while-loop. But the gui-update does only happen afterwards
The function goToNextQuestion is completely executed in the main-thread!
So how can i achieve that the loading-indication is shown directly and after it is shown, the while-loop waits for the finish of the other thread?
I am really desperate because i have no idea at all how to solve this.
I would really really appreciate some help from you!
Thank you very much in advance for your time!!
There are several problems with your approach.
First, you should not use NSThread/Thread. The system class URLSession handles async downloading for you. (And creating threads is expensive and error-prone.) Use URLSession instead. You should be able to find lots of examples online, including here.
Second, you can't block the main thread like you're doing with your
while(nextQuestionLoader.isFinished == false) {}
Code. Since your download is running on a background thread it will keep running, but by blocking the main thread everything will freeze until the download completes. Your UI changes don't get a chance to be drawn on the screen until the download is complete.
What you should do is use URLSession using either a delegate method or a completion closure. It supports both approaches.
I have a project called Async_demo on Github that includes working code that shows how to use URLSession. It's a simplified example, but it should get you started.

Where to put code that gets checked frequently?

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.
In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.
What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.
Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

Dealing with application processmessages

In our software we have issues with asynchronous execution of events. Sometimes one function will "break into" another one with all kinds of weird results.
I'm dealing here with user-input and timers that fire events.
The issue is that rather then executing the code tied to the events one by one, it's done at the first possible moment that Delphi gives a window for it: application.processmessages. This gives problems in that sometimes half of function A gets done, then function B "breaks in", gets done and after that , the last half of function A gets done. This can give "surprising" results.
Are there good ways to deal with this?
Things I tried:
--
Using a "busy-flag", this has some ups and downs, mostly that everything you do has to know of it.
Removing all application.processmessages where I can. This has pretty good results. But we're relying on some 3rd party components which I found out also fire application.processmessages.
Next I was thinking of trying building some kind of "command-queue" where I can receive my events and fire them in a fifo way.
Apart from rebuilding everything we have from the ground up, are there other/better ways to tackle these issues?
The best way is to eliminate the call to Application.ProcessMessages. Most of the time there is other ways to do what Application.ProcessMessages is supposed to do. You'll need to take a closer look why you need that call, and then find a better solution. For example, you don't need Application.ProcessMessages to update the UI. There is other ways to do that.
If a 3rd party component is calling Application.ProcessMessages then contact that vendor that they should replace this call with a better suited function. If this is not an option, you can try using workarounds like using that component in a thread (if possible). Or create an invisible modal window and execute the methods of the component inside the ShowModal function. That will at least avoid user input messages. (The "invisible modal window" is a Form with BorderStyle=bsNone, size=1×1 and 100% transparency).
Are there good ways to deal with this?
First eliminate all your use of ProcessMessages. As you discovered, it screws up timer event handlers when called from there. Used in other places, it is often subject to race conditions and may hide the real problem. Find out what that problem is and solve it.
But we're relying on some 3rd party components which I found out also fire application.processmessages.
Timer Event handlers are supposed to do only short time work. If you are calling ProcessMessages via a call to a 3rd party library inside a timer event handler, eliminate that call. There is no other cure, except rewriting the library or calling it in another way.
Apart from rebuilding everything we have from the ground up, are there other/better ways to tackle these issues?
Normally you can do background work in threads as well, providing the rules of not calling any VCL RTL methods directly are followed. Here it is not possible if the 3rd party component is calling ProcessMessages.
If you can't alter the 3rd party component, there is possibility to post a message to your form, and put the call in the method that handles this message. With a modern Delphi you could use DelayedAction by #MasonWheeler. But I really recommend you to take the "hard" way and fix the 3rd party lib instead.

Integrating Reachability with ReactiveCocoa?

I'm working on the development of an app using ReactiveCocoa and the time has came to integrate Reachability to handle Network Events.
I'm not sure about the work I've done as some of RAC design guidelines advice against it. But from what I've been researching I couldn't find a proper way to manage this (or more likely, I dind't understand how).
Our idea is to have a unique signal that sends events upon Reachability's notifications. Each of our viewcontrollers would subscribe to that signal and react each one in its specific way (ie alertview, do nothing, etc).
The thing is that we only need our visible viewcontroller to react, so we are using RACDisposable. This way, when a viewcontroller appears it subscribes to the signal, and when it dissapears we dispose it.
From the Design Guidelines of RAC the use of RACDisposable should be avoided, but I can't manage to handle this subscribe/unsubscribe cycle in any other way.
Is this approach correct for this kind of "infinite" signals?
An alternative we though of is using a signal that removes its previous subscribers when a new one joins. Does such a thing exist?
Thanks in advance for any light you can point in my direction.
From this SO post I got to trying takeUntil:[self rac_willDeallocSignal]which seems useful in theory but doesn't really work for me. As this behaviour is encapsulated within a Manager, and it's not supposed to be deallocated, I don't know how this can work in my favour.
An option that crossed my mind is takeUntila new signal fires whenever a method to stop is called; but it seems pretty much just so we don't use RACDisposable.
From this SO post I got to trying takeUntil:[self rac_willDeallocSignal]which seems useful in theory but doesn't really work for me. As this behaviour is encapsulated within a Manager, and it's not supposed to be deallocated, I don't know how this can work in my favour.
Your view controller can do something like this:
- (void)viewWillAppear
{
RACSignal *disappear = [self rac_signalForSelector:#selector(viewWillDisappear)];
[[self.manager.reachabilitySignal takeUntil:disappear] subscribeNext:^(id status) {
// do whatever needs doing with reachability status here
}];
}
and your Manager's reachabilitySignal can be implemented to register/de-register for reachability status changes as needed depending on whether there are any subscribers.

Adding pin on mapView is not working on appstore version

I have submitted my app and been accepted couple of weeks ago. However, I have an issue that one of my critical functionality which is adding pin is not working but it works in building version. Adding pin requires URL connection. Could you please look at the answer that I get from apple developer and guide me please?
It shouldn't be causing your problem (at least not directly), but in general
having networking code like this live inside your UI code is very problematic
for a number of reasons:
-On general architecture grounds, it's a large violation of the MVC (model
view controller) architecture. The pin annotation is obviously heavily tied
to your UI (the view), while networking is clearly part of your backend (the
model).
-Because iOS REQUIRES the main thread to remain responsive, attaching code
that does IO or computation to the main thread is inherently risky. It can be
done and work well, but doing it incorrectly will inevitably lead to crashes.
NSURLConnection works in background and interactions with UI should go on main thread.
So, if you add your pin at the connectionDidFinish method for example, you should do like this:
[mapView performSelectorOnMainThread:#selector(addAnnotation:) withObject:yourAnnotation waitUntilDone:[[NSThread currentThread] isMainThread]];
Hope that helps you. I wrote from windows but the line should work.
I'm not exactly sure about what you are trying to achieve. Are App folks complaining because you are fetching URL directly in the Main thread ? Could you just send a code snippet showing how you fetch urls ?
One option would be to drop the pin (not tied to URL connection at all) and once the pin drops, load your data for the callout or annotation (which is what I'm assuming you are doing). You can always put a loading indicator or progressHUD up to tell the user they need to wait for something. It isn't exactly ideal, but in instances where you need to wait for data, what are you going to do?
Apple really doesn't care about blocking the UI so long as the user is aware you are blocking it. Like a progress bar or progress HUD. The user can see the app is still working and doing something. Not showing anything and having the UI just freeze while it waits for the main thread/main loop to return isn't going to work. It is just bad design and Apple will likely ding you for it.

Resources