My iPhone application supports a proprietary network protocol using the CocoaAsyncSocket library. I need to be able to send a network message out when my iPhone application is closed. The code that sends the message is getting called from the app delegate, but the application shuts down before the message actually goes out. Is there a way to keep the application alive long enough for the message to go out?
Bruce
The docs from Apple don't specifically state this, but the sense I get from looking around the Web and from personal experience is that you have about 4 to 5 seconds after the user hits the Home button to shut your app before your application actually terminates. The iPhone OS is controlling this so you can't block the termination to allow your program to finish first. Basically when your time is up, your program is killed.
There may be another solution, though. First I'd confirm that your code is really taking more than 5 seconds to run. Perhaps you can have it run in response to a button tap, and time how long it runs. If it is more than 5 seconds, you probably are running into this time out issue.
You might then find a way to trigger a message to be sent from a server that is always running. You should have enough time to trigger a remote action, which in turn could then take as long as it needs to run.
Or perhaps you could save the vital information to the iPhone file system on exit, and send that message the next time someone starts the application, which should theoretically give you enough time.
Hope this helps!
I assume you're already calling it from your AppDelegate's:
- (void)applicationWillTerminate:(UIApplication *)application
But as you've discovered there's no guarantee it'll be called or will be allowed to finish. There are a few options that may or may not work depending on what you're trying to do:
If you need the server to perform some sort of cleaning operation triggered by when the client app is gone then you could try watching for TCP socket closure on the server and treating that as the triggering event. But if you explicitly need to send data back with the closure this may not work.
If the data you're sending back is not time-sensitive then you can do like most of the analytics libraries do and cache the data (along with a uuid) on the client then try to send it on app closure. If it goes through, you can clear the cache (or do it the next time the app is run). If it doesn't, it's saved and you can send out when the app is run next. On the server, you would use the uuid to avoid duplicate requests.
If the material is time-sensitive then your best bet is to implement heartbeat and send periodic updated values to the server. Then when the client app dies the server times out the heartbeat and can use the last received value as the final closing point of data.
In either case, if an explicit closure event is required by your custom protocol then you may want to reconsider using it in a real-life mobile environment where things have to be much more fluid and tolerant of failure.
As others have noted, there's no way to be absolutely certain that you'll be able to send this, but there are approaches to help.
As Ken notes, you do in practice get a few seconds between "willTerminate" and forced termination, so there generally is time to do what you need.
A problem you're almost certainly running into is with CocoaAsyncSocket. When you get the "willTerminate" message, you're on the last run loop of the main thread. So if you block the main thread, and CocoaAsyncSocket is running on the main thread, it'll never get processed. As I recall, CocoaAsyncSocket won't actually send all the data until the next event loop.
One approach, therefore, is to keep pumping the event loop yourself:
- (void)applicationWillTerminate:(UIApplication *)application
{
// ...Send your message with CocoaAsyncSocket...
while (! ...test to see if it sent...)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
I've also looked at putting this work onto a background thread and letting the main thread terminate, in theory letting us go back to Springboard while continuing to run for a few seconds. It's not immediately clear to me whether this will work properly using NSThread (which are detached). Using POSIX threads (which are joinable by default) may work, but probably circumvents any advantages of the background thread. Anyway, it's something to look at if useful. In my apps, we've used the "post next time we launch" approach, since that always works (even if you crash).
Related
I have a Mac OS X app and I want to detect or catch the event when the user Force Close my app.
At first I thought applicationWillTerminate will do the work but it didn't:
func applicationWillTerminate(_ aNotification: Notification) {
print("applicationWillTerminate called")
}
Do you have any idea how can I do that?
Major credit goes to #caseynolan for putting work in here to come up with an answer. Unfortunately, there are, in fact, some major issues with the suggested approach.
First, signal handlers are per-process. There are other, legitimate uses of signals, and installing handlers will negatively impact their behavior without very careful work.
Second, signals interrupt a thread, and that thread can be doing anything, including holding locks. Examples of things that hold locks - malloc/free, the Objective-C runtime.
This is an part of a class of problems known as "async safety". If you check out "man sigaction" (note, sigaction is a much improved API over signal) you see that there are actually a very small number of functions that are safe to call from a signal handler. Calling unsafe functions, like NSLog, will sometimes work. But, will also sometimes deadlock, depending on what the thread was doing at the time.
Now, I will admit that my experience with signals does not include use of SIGTERM. However, since it can be delivered at any time, it still will be subject to async safety issues, even thought it is not a crash.
In short: it is almost certainly unsafe to run your code in a signal handler, and it will deadlock sometimes. And, those deadlocks will occur during unpredictable points of its execution.
Third, there are fatal events that aren't mapped to signals, on top of SIGKILL. This may not matter to do, depending on how strong a guarantee you need about detecting your process termination.
What I'd do Instead:
I think you're only safe option is to use a sentinel process. The idea is you start up a child process, and in that child, observe the parent. If/when that process disappears, you can then execute your code.
This is a safe, deadlock-risk free way to monitor for arbitrary process exits. However, it could be challenging to move your operations out-of-process. Unfortunately, I believe that's a necessary aspect of this, if you want it to be reliable.
Good luck!
Disclaimer: I'm not an expert, and researching for this post has stretched the limits of my rusty C/Objective-C knowledge. I don't know how the following code is viewed by Apple for submissions to the App Store, so YMMV.
The following has been shamelessly ripped from Wikipedia and reworked:
#import <Foundation/Foundation.h>
/**
This will handle signals for us, specifically SIGTERM.
*/
void handleSignal(int sig) {
if (sig == SIGTERM) {
// Caught a SIGTERM
}
/*
SIGTERM is a clear directive to quit, so we exit
and return the signal number for us to inspect if we desire.
We can actually omit the exit(), and everything
will still build normally.
If you Force Quit the application, it will still eventually
exit, suggesting a follow-up SIGKILL is sent.
*/
exit(sig);
}
/**
This will let us set a handler for a specific signal (SIGTERM in this case)
*/
void setHandler() {
if (signal(SIGTERM, handleSignal) == SIG_ERR) {
NSLog(#"Failed to set a signal handler.");
} else {
NSLog(#"Successfully set a signal handler.");
}
}
Call only asynchronous-safe functions within signal handlers. See here.
You could place the above in a C/Objective-C file and use it in Swift via a Bridging Header. Call setHandler() somewhere in the beginning of your application's lifecycle, e.g. in applicationDidFinishLaunching, and you should now have a chance to do some work before your app is Force Quit. I don't know how much time you get here, so I'd keep the workload as light as possible (avoid starting mission-critical stuff here, I guess?).
Here's some background info:
In a typical Quit
The Quit procedure is actually a part of Apple Events.
If the application is NSDocument-based, the behavior depends on the saving parameter, which has one of these three values:
NSSaveOptionsNo: The application quits without sending a close message to any document.
NSSaveOptionsYes: Each unmodified document is sent a close message; each modified document is sent the following message:
saveDocumentWithDelegate:didSaveSelector:contextInfo:
NSSaveOptionsAsk: (This is the default value if no saving parameter is supplied in the event.) If there are modified documents
open, the NSDocumentController sends itself this message:
reviewUnsavedDocumentsWithAlertTitle:cancellable:delegate:didReviewAllSelector:contextInfo:
If the application is not NSDocument-based, the application delegate is sent this message (if it is implemented):
applicationShouldTerminate:
You can modify the default behavior by implementing this method.
Source: How Cocoa Applications Handle Apple Events
During Force Quit
The application is sent a SIGTERM which can be caught and dealt with by your application. Ideally, the application would clean up and exit as gracefully as possible, but this isn't required, and the signal can even be ignored.
An impatient user (and I strongly suspect that Force Quit does this eventually) may send a SIGKILL, which cannot be ignored and totally goes over anything you as the developer can stop.
Extra Information and Resources:
What does Force Quit do in OS X?
What signals does OS X send for the Quit and Force Quit commands?
How Cocoa Applications Handle Apple Events
SIGTERM vs. SIGKILL - major.io
C Signal Handling - Wikipedia
In my app created NSOperationQueue of NSOperation to download multiple images in background.I wanted to know what will happen if network connection is lost while downloading data.Whether all those are operations which are still in queue are marked as finished and removed from queue or will remain in queue and again start downloading process when network is established again or something else?
Can anyone explain how NSOperationQueue works in such scenario?
Thanks in advance!
The current NSOperation will continue with whatever logic is left when it receives the failure signal, and then terminate as normal, causing the NSOperationQueue to begin running the next NSOperation. If the internet still isn't back, it'll fail as well, and will just keep moving through the queue whether the network re-connects or not, failing all the connections unless the network reconnects.
There's probably quite a few different ways to handle this, because there's a lot to consider. For instance, you could put some logic in your code to mark an NSOperation as unsuccessful if it fails due to a network drop-out, and re-add failed NSOperations back into the queue. However, what if the network stays down for ages? Would your implementation be okay to keep trying to download images forever, or should you only let it re-try a particular file download a few times before giving up until the next time that feature is used?
You could also use a series of network reachability checks, such as before an NSOperation is added to the queue, before the NSOperation itself launches the NSURLConnection, etc. Should it fail you could have the NSOperation wait around until the network is re-connected and continue. (It's not ideal to do that, but because you're running it on a background thread it won't freeze the app's interface for the user at least).
If you wanted to, you could even implement the functionality you asked about in your question. That is, upon a network drop-out, cancelling all remaining operations in the NSOperationQueue, informing the user they'll have to re-try when the net comes back on. Then the next time the feature is called, re-add all those remaining NSOperations.
I just got asked to reduce the traffic made by my GWT app. There is one method that checks for status.
This method is an asynchronous call wrapped in a Timer. I know web apps are stateless and all that, but I do wonder if there is some other way to do this, or if everyone has a Timer wrapped around a call when they need this kind of behaviour.
You can check out gwteventservice. It claims to have a way to push server events and notify the client.
I have a feeling they might be implemented as long running (hanging) client to server RPC calls which time out after an interval (say 20sec), and then are re-made. The server returns the callback if an event happens in the meanwhile.
I haven't used it personally but know of people using it to push events to the client. Have a look at the docs . If my assumption is correct, the idea is to send an RPC call to the server which does not return (hangs). If an event happens on the server, the server responds and that RPC call returns with the event. If there is no event, the call will time out in 20 seconds. Then a new call is made to the server which hangs in the same way until there is an event.
What this achieves is that it reduces the number of calls to the server to either one for each event (if there is one), or a call once every 20 seconds (if there isn't one). It looks like the 20 sec interval can be configured.
I imagine that if there is no event the amount of data sent back will be minimal - it might be possible to cancel the callback entirely, or have it fail without data transfer, but I don't really know.
Here is another resource on ServerPush - which is likely what's implemented by gwteventservice.
Running on Google app engine you could use they Channel technology
http://code.google.com/intl/en-US/appengine/docs/java/channel/overview.html
If you need the client to get the status from the server, then you pretty much have to make a call to the server to get it's status.
You could look at reducing the size of some of your messages
You could wind back the timer so the status call goes out less often
You could "optimise" so that the timer gets reset when some other client/server IO happens (i.e. if the server replies you know it is ok, so you don't need to send the next status request).
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.
I am trying to post some data before my application terminates. Iam doing this by genrateing sockets using CFStreamCreatePairWithSocketToCFHost and later on I have the callbacks for reading and writing.
But I am not able to post any data. It goes through all the functions but doesnot enter into callbacks. Does that make sense to anyone?. Is there anyway to get this working?
Thanks,
Sowri
Yes, after applicationWillTerminate is called, there are no more iterations of the run loop. Since CFSocket and CFStream both use the run loop to manage the sockets and to provide data via the callback, this will not work. Also, it's very important to note that the application may be restricted from doing certain things at this stage and that if your application does not terminate, the operating system will terminate the application. It may be a better idea to write a small log to a database and then post that information back the next time the application starts.
My gut feeling is that after the applicationWillTerminate method is done, it will just stop the whole app, without giving any other run loop the chance to execute, let alone do callbacks. So my guess is that calling asynchronous methods in the applicationWillTerminate won't even start. You're just too late at that point to start networking.
The applicationWillTerminate: callback is not the place to do any kind of critical operation because as the name implies, your application will terminate and it won't wait for your code to finish doing something.
What are you trying to post; if you explain why you want to do this we may be able to offer a better solution.