I'm using the AsyncUDPSocket third party library in my iPhone app and for the most part it works great. I have a singleton instance of an AsyncUDPSocket that I use for all my network traffic. My app is registered for location tracking in the background and will wake up and send location update packet(s) over the network while running in the background. This all works smashingly running in the background, foreground, phone locked or unlocked, except when I do the following:
Start my app
Disable location tracking in my app settings
(so no background waking up)
Press the home button (app goes into background, socket is "freeze-dried" with rest of app)
Lock phone
Unlock phone
Resume app
Attempt to restart tracking and send something out the socket. As soon as I try, I get a SIGPIPE/EPIPE error and the app crashes.
I figured the best way to deal with this would be to close and release the socket whenever the application exits and background tracking is not enabled, but when I try [socket close] or [socket release] on the AsyncUDPSocket, I get various EXC_BAD_ACCESS errors. I've filed a bug with the dev team, but was wondering if anyone here could give some ideas on how to either avoid the SIGPIPE error entirely or other ways to keep the socket alive without releasing it. Thanks.
Great observation - yes, seems that after you send task to background and then lock the phone, sockets get dropped and next time you try to use it one gets bludgeoned with a SIGPIPE.
Ideas on how to deal with it here:
SIGPIPE crash when switching background task
(it's either set ignore for SIGPIPE for the whole app, or for the socket, or provide hanler for it)
ps. also - seems that setting to ignore SIGPIPE does not work with attached debugger, so compare with and w/o.
Just in case anyone's curious (which, judging by this question's stats, they're not), I was not able to determine what was causing the SIGPIPE error, but did eventually sort out my memory management issues (which were due to a faulty implementation of onUdpSocketDidClose in my delegate) so that I am able to reinitialize the socket each time the app restarts.
Related
I'm having an issue that's incredibly hard to debug. If my app is using WiFi and it sits idle for around 30 minutes, sometimes when I wake it up, the NSURLConnection no longer responds. Requests are sent, but never return.
At first, I thought this was a memory bug with the instances being released too early and thus never returning a response. However, if I put the app in the background, go into settings, turn off WiFi so 3G is used exclusively, and return to the app, the internet connection magically comes back to life and all pending NSURLConnections return and complete.
Obviously this is an issue for people using WiFi. Is this really a WiFi issue, or am I missing something? Going to another app like Safari, using the WiFi radio and returning to my app doesn't solve the problem - connections still don't return. I've traced this with Xcode and I'm running out of ideas.
Also 'Application Uses WiFi' Info.plist flag is set to ON and this is firmware 5.1.
is 'Application Uses WiFi' the same as UIRequiresPersistentWiFi?
Update: This has nothing to do with the Wifi flag - it can die within 5 minutes. So far, I've only been able to duplicate it on my iPhone 4s with 5.1 firmware. It's not really a solution, but I'm erasing the phone to try it with a fresh install to see if that has any effect. I have verified that NSURLConnection is always called on the main thread, and set breakpoints at connection:failedWithError: and connection:didReceiveResponse:. When the connection dies, none of these return until I disable and re-enable WiFi, and then all return at once. This happens on a local server as well, and the server still returns if I ping it with a web browser.
For any others running into this issue, it's due to TestFlight v1.0 and below:
Why does NSURLConnection fail to reach the backend?
Are you actually transmitting/receiving any data through the NSURLConnection? If not, is it possible you are hitting a TCP session timeout? Seems unlikely if the problem persists on a local server, but any intervening stateful firewall/packet inspector might be casting off your TCP connection.
While the device is powered on, is it possible for iOS to automatically terminate my app (calling applicationWillTerminate:) while it's in the background?
I'm also curious what happens in two other cases, three in total:
Device is powered on
Device is powered off
Device loses battery
I'm asking because I want to know how often applicationWillTerminate: is likely to get called. I want to know this because that's where I'm registering for remote notifications. And if there's a failure sending the device token to the server, I want to know how likely it is that that method will get called again (i.e., retry sending the device token to the server).
If your application supports multitasking (the default for anything linked against iOS 4.0+), this method will almost never be called. The documentation says it may be called in cases where the application is running in the background and the system wants to terminate. However, in my experience, I've only ever seen this actually called when running a music app that's actively playing music in the background and the system is jettisoning everything. In cases where I have background tasks running (not music, but short-term background tasks), I've seen the app terminated without this method being called.
I wouldn't ever rely on this being called and try and do all the clean-up you need to do in your delegate methods for transitioning into the background and your background task completion blocks (which do get executed for at least a few seconds before the app gets jettisoned).
Not only can iOS terminate your app automatically, but the user can kill it manually. In fact, the only time the user can kill your app is when it's in the background. Furthermore, when your app is "in the background" it's more likely to be suspended than actually running, so don't count on doing a lot of processing when you're not the foreground app.
As for how likely it is that you'll get -applicationWillTerminate:, that'll depend on the user and how they're using their device. You should handle it appropriately when you get it, and go about your business otherwise.
When memory is running low, iOS can shut down your app, calling applicationWillTerminate.
The docs say this about the method:
... However, this method may be called in situations where the application is running in the background (not suspended) and the system needs to terminate it for some reason.
Check out iOS Developer Library : iOS App Programming Guide : App Termination.
So here is the issue I am facing. Certain portions of the application I am building open some c network sockets that allow connections to various servers/services. However, if the application goes to sleep, these socket connections are lost, and error out when trying to reload them. So what I want to do is basically notify the user when the app launches again, that the application needs to be restarted. The main question is, can I present them with a button that will kill the app by using exit(0) without my app getting rejected?
Apple says that the user should be in control of when the app is killed, and in this case I see that they are, but I am not sure of Apple's opinion on this. Has anyone else used this? Have you been rejected for this? Thanks in advance for any advice!
EDIT:
Thank you everyone for your advice. I am trying to take everything into consideration, but because the app needs to be submitted ASAP, I just need to know, if we can not get another solution, if the above proposed solution, will get rejected or not.
Your application delegate receives notifications when significant events affect the life of the application. Rather than ask your user to recreate a session, you should attempt to discontinue network operations and then resume them at the appropriate times in the application's lifecycle automatically.
You can gracefully kill network sockets (amongst other things) in any number of places as the application prepares to exit or enter the background via callbacks in your application delegate:
applicationWillResignActive:
applicationWillEnterBackground:
applicationWillTerminate:
Potentially reconstruct sockets in:
applicationDidBecomeActive
applicationWillEnterForeground
Have you tried not allowing the app to run in the background? Then it will be killed whenever the user exits to the home screen. This might be a bit aggressive, but would solve the problem. From Apple's opting out of background execution:
"If you do not want your application to remain in the background when
it is quit, you can explicitly opt out of the background execution
model by adding the UIApplicationExitsOnSuspend key to your
application’s Info.plist file and setting its value to YES.
When an application opts out, it cycles between the not running,
inactive, and active states and never enters the background or
suspended states.
When the user taps the Home button to quit the application, the
applicationWillTerminate: method of the application delegate is called
and the application has approximately five seconds to clean up and
exit before it is terminated and moved back to the not running state."
See also: How to prevent my app from running in the background on the iPhone
The documentation is pretty explicit about this, "There is no API provided for gracefully terminating an iOS application." See Technical Q&A QA1561
How do I programmatically quit my iOS application?.
To be blunt, terminating an app to cleanup a socket is just like dealing with memory management by forcing an app to exit instead of calling release.
What about bringing up a modal view controller telling the user to quit the application? You could make this view controller without any dismiss button, so the user is obligated to kill the app.
I am trying to determine feasibility of certain features required in a (potential) project. I am not (yet) looking for a how-to, just a can-do. I apologize for any vagueness and ignorance: the former due to an NDA that makes Apple's NDA look like GPL, and the latter due to the fact that I have no iPhone or MAC experience.
I do have a solid understanding of objective-c and interface builder (going back to NextStep) and some PDA development experience, so I'll probably understand the answers, even if my questions are naive. I have done a bit of browsing, so I know some buzzwords.
I can't go into detail about the actual project, but I have come up with a lame analogy.
a large number of users in the same room are asked to complete task(s) in the app (say a puzzle)
they are under supervision, but the monitor cannot watch them all closely
they are not allowed to leave the application until done.
they cannot send/receive phone calls or messages during the task.
the monitor receives notification of various steps during the task
the monitor is notified when the task is completed, or the app is exited
the app sends a heartbeat, so the monitor also knows if signal is lost
Jailbreak is not an option.
The app should also work on iPod Touch.
So the things I need to do that seem dicey to me are
can I turn off (or require the user to turn off) phone and sms but still be able send http to my server
can I prevent an app from being switched out (even if, say an alarm app triggers or the phone rings).
failing prevention, can I at least detect any of these events and notify my server.
failing notification, can I record the event for the monitor to check later.
The user will be aware (and in fact welcome) these restrictions. It's a trust issue - the user must not seek help or use a helper app to solve the puzzle, and wants the monitor (and other contestants) to know that he did not. It's feasible for the user to click on an "OK, I understand and approve" screen at the beginning, but not for each communication to the server.
The app would only communicate with a central server (run by my company) - the monitor would not be able to buy the server software, and the url's of the server would not be user (or monitor) modifiable.
Hey! This looks like an app for taking exams. Not what I am doing, but that would be cool too!
EDIT --
I changed the title and am adding a few more parts to the question, based in part on mmc's answer. The App may run in an offline mode that would have to do the following:
So using the exam analogy, the user off line experience would be something like this
Launch App
App download test questions, registers start time, etc.
Turn off phone (if app can't do it by itself)
Disable any app that might interrupt my App (can app do this?)
Resume and Take Test
Indicate test done (or finish last problem).
Turn on phone (if app can't) and restart App (if needed).
App uploads test results and log of any interruptions.
So the question becomes
Am I sure that I at least get to log any interruption I can't prevent
can I know the cause of the interruption (phone answered, alarm launch, user initiated)?
can the user be prevented from modifying the log
can I know what other Apps are running when I start? (to guard against a daemon that occasionally displays a hint or something.
I would still like to run with real time uploads, so a few other ideas come to mind.
If I can reliably detect and record that the phone or another app was used, that might be almost as good as preventing it.
Can the user prevent the phone from ringing even if it's on (eg. call forward + ringer off)
Can my app know if the ringer is off
Same question for sms messaging
If I can't block it, can the user just ignore (silent) call or message and not leave my App. Would my app know that?
Sounds like you might be better off doing away with the phone, and making it exclusive to the iPod Touch.
You've nailed your trouble spots.
There is no way to disable phone functionality, and at the same time maintain network functionality of any type (3G, WiFi, or bluetooth) If you disable the phone operations with Airplane mode, all of them are disabled.
There is no way to prevent the Home button from returning you to Springboard
You can notify a server of a premature app interruption (there is an applicationWillTerminate: method on your app delegate) but is not reliable. If the operation takes too long, your app will be forcibly terminated.
You could write to the local file system that a premature interruption happened, and this would be far more reliable, as this operation would be much faster.
My application needs Internet Connection. It seems like if I keep my iPhone idle for a while it shuts down its 3G connection. Once I wake it up (slide to unlock) and run my application, it cannot connect to the Internet. I have to run Youtube or Safari first, then it gets the Internet connection, then I have to shut down Youtube/ Safari and then I can use my application to login to my service.
Could you please let me know how can I activate 3G connection from my application (so that I can use my application directly after it wakes up from the idle state and I do not have to run other applications like Youtube/ Safari?
Thanks.
To disable the idle timer, take a look at the idleTimerDisabled property of the UIApplication class.
From Apple:
The default value of this property is
NO. When most applications have no
touches as user input for a short
period, the system puts the device
into a “sleep” state where the screen
dims. This is done for the purposes of
conserving power. However,
applications that don't have user
input except for the
accelerometer—games, for instance—can,
by setting this property to YES,
disable the “idle timer” to avert
system sleep.
Important: You should set this
property only if necessary and should
be sure to reset it to NO when the
need no longer exists. Most
applications should let the system
turn off the screen when the idle
timer elapses. This includes audio
applications. With appropriate use of
Audio Session Services, playback and
recording proceed uninterrupted when
the screen turns off. The only
applications that should disable the
idle timer are mapping applications,
games, or similar programs with
sporadic user interaction.
There is obviously another better solution, but you could load a blank page with:
[NSString stringWithContentsOfUrl ... ]
The connection will be established if it is necessary.
Only NSURLConnection (and any APIs that are layered on top of it) reinitializes the data connection after waking from sleep. To reinitialize the data connection create a dummy NSURLConnection to a non-local address and cancel it right away; then the socket API will work as expected.
There is a post on the developer forums where an Apple dev explains this in detail (but I can't find it at the moment)
Are you sure you're establising the connection correctly? My application does the same using sockets and it has no problems to re-establish the connection after device sleep. Use Reachability API in SystemConfiguration framework to get notified when coverage is available and after that make your connection attempt. Note that a time period - from several seconds to couple of minutes - has to elapse after the device awakes to gain Internet connectivity, so be patient.
There is Reachability sample from Apple, search also stackoverflow for reachability and you'll find more hints how to implement it.
Actually, you get the same problem when you change the network settings on your phone between launches of the application. For instance let's say that you use the WIFI connection when you launch the app. Then you close the app and switch off the WIFI so that the device uses the carrier's network. When you relaunch the app the socket won't be able to connect unless you do the trick with the dummy NSURLConnection (or you launch the browser before lanuching the app).
Also, canceling the NSURLConnection right after initializing it (with connectionWithRequest or initWithRequest) did not work for me. Either do not cancel the request or wait some time before canceling it (e.g. with performSelector:withObject:afterDelay:).