I am using pure data messages from FCM to display notifications locally on my app devices. For some reason, on iOS devices only, the data messages are CANCELED by iOS whenever the device is restarted up until my app is opened for the first time. This leads to my app not receiving any data messages and thus not being able to display notifications and process them accordingly. I am using React Native / Expo to build my app.
This below log lines show iOS cancellations, which continue to retry until the app is opened for the first time following the restart.
iOS Logs:
default 16:57:19.114198+0100 dasd Submitted Activity: com.apple.pushLaunch.XXXXXXXXXXXXXX at priority 5 (Fri Dec 30 16:57:19 2022 - Sat Dec 31 16:57:19 2022)
default 16:57:19.118067+0100 dasd Daemon Canceling Activities: {(
com.apple.pushLaunch.XXXXXXXXXXXXXX
)}
default 16:57:19.118227+0100 dasd CANCELED: com.apple.pushLaunch.XXXXXXXXXXXXXX at priority 5 <private>!
default 17:16:58.004240+0100 dasd com.apple.pushLaunch.XXXXXXXXXXXXXX:[
{name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Absolutely Must Not Proceed, Score: 0.00, Rationale: [{[pushDisallowed]: Required:0.00, Observed:1.00},]}}
], FinalDecision: Absolutely Must Not Proceed}
Once the app is started for the first time, the data messages are received by my app. Following this, the issue does not take place once the app has been started the first time, even if it is is subsequently killed. However, on the next restart, the same issue will take place once again.
The following data message is sent using firebase admin sdk:
Message firebaseMessage = Message.builder()
.putData("notificationId", notificationId)
.putData("title", title)
.putData("body", message)
.putData("imageUrl", imageUrl)
.putData("channelImageUrl", channelImageUrl)
.putData("channelId", "channel-notifications")
.setApnsConfig(ApnsConfig.builder()
.setAps(Aps.builder()
.setContentAvailable(true).build())
.putHeader("apns-push-type", "background")
.putHeader("apns-priority", "5")
.putHeader("apns-topic", <my-app-bundle-id>)
.build())
.setAndroidConfig(AndroidConfig.builder()
.setPriority(AndroidConfig.Priority.HIGH)
.build())
.setToken(recipient)
.build();
The issue does not occur on Android.
Does anyone know what could be wrong and how to fix for my app to start receiving data messages even after a restart on iOS?
I'm trying to reach the file which I was downloading earlier by NSURLSession. It seems I can't read the location of the file, even though I'm doing it before delegate method ends (as the file is temporary).
Still, I'm getting nil when trying to access the data under location returned from NSURLSession delegate and error 257.
The code goes as following:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSError *movingError = nil;
NSData *fileData = [NSData dataWithContentsOfFile:location.path options:0 error:&movingError]; // is nil
NSLog(#"%#", movingError); // is error 257
}
What's wrong with this code..? I saw similar questions NSURLSessionDownloadTask - downloads but ends with error and iPhone - copying a file from resources to Documents gives an error but these completely doesn't apply to my case.
-- edit --
I've created a new project and pasted the very same code. It works... So:
1) In my project I'm receiving error 257, probably some configuration of the project is invalid or the fact I'm using backgroundTasks somewhere else in the app messes things up
2) Same as in 1 happens if I put the source files of this download to the external framework linked in by Carthage
3) On demo project I created (copy-pasted files used in 1 & 2) everything works corretly.
If someone has an idea what can cause the fact it isn't working - would be awesome.
Late to the discussion, but I've seen the same/similar issue and investigated.
With my investigation, what you are seeing is expected (should I say “it's observed”?).
But since I'm not 100% sure how you are using URL session, I put down below what I'm trying to do, followed by my observations (important ones only).
Also, I created a sample project and put it here as downLoader.zip. You can play and check how URL session background download works. But It's better to read thru below before trying to play, though this is fairly a long note.
A. what I'm trying to do
I'm developing kind a map app, where I need to download 1000+ of small size (0.5-150kB, mostly ~20kB) PNG files at a time. Total download size of them is ~50MB, and it takes a couple of minutes to download all of them. I think that keeping users to my app just waiting for download is bad design, so I made the app to use URL Session’s background download.
I have to admit, however, that Apple’s doc says that “Background sessions are optimized for transferring a small number of large resources that can be resumed as necessary.” So, the way I’m using background session is totally opposite. But, anyway...
B. what I've observed
Below, I’ve listed my observations and my guess of why. I should say guess since they are not documented.
(1) observation: sometimes, files that should come with didFinishDownlaodingTo don’t exist.
Temporary files are put at:
/var/mobile/Containers/Data/Application/randomHexString/Library/Caches/com.apple.nsurlsessiond/Downloads/yourName.yourApp. The randomHexString changes from app’s run to run. When the files don’t exist, the randomHexString that came with didFinishDownloadingTo is the one from the “previous” session. Now, “previous” here means the session at app’s previous run !, which clearly doesn't exist at the current session at current run.
There's another scenario for non-existent file, which is that the randomHexString is ok, but the files don't exist. This seems to happen after session cancel is requested (invalidateAndCancel) and before session becomes invalid (didBecomeInvalidWithError).
Guess: This happens especially on the development phase since we terminate the app while downloading, manually or by debugger or just by bug. It seems once the download requests are handed and accepted to the OS, the OS handles our requests even after the app quits. Note we can’t know if the OS has definitely accepted the requests or not. Even after the return from URLSessionDownloadTask:resume(), sometimes files don’t exit at the next launch and sometimes they do.
Workaround: If files don’t exist, just ignore them. In a while, “this time’s” files should come.
(2) observation: sometimes, the duplicated (same) files come with didFinishDownloadingTo.
My app converts downloaded PNG files to other format. W/in didFinishDownlaodingTo, I move temp files (== OS designated) to another my app’s directory, then spawn thread (GCD) to convert the format and delete downloaded temp files. So, the other thread (didFinishDownlaodingTo) to overwrite is an issue.
Workaround: I make list of URLSessionTask:taskIdentifier and w/in didFinishDownlaodingTo, check the duplication by retrieving the taskID list to ignore duplicated files.
(3) observation: even after the user terminated the app, OS relaunches the app again.
After the user terminated the app from task switcher, quite often, the OS relaunches the app w/ application:handleEventsForBackgroundURLSession:completionHandler.
Note the sequence of relaunch is that didFinishLaunchingWithOptions comes first as regular launch, then handleEventsForBackgroundURLSession comes next.
From user’s POV, when he/she terminated the app, that’s it, done! It looks strange even after they terminated the app, it relaunches in itself and notifies something to them. It's like a zombie.
Guess: The Apple’s document says “If the user terminates your app, the system cancels any pending tasks”. The definition of “pending tasks” is not clearly stated but I understand this is from iOS POV and not user’s or program developer’s. As guessed in (1), iOS seemed to have accepted the download requests, and they are not “pending tasks” anymore.
Workaround: I create a “flagFile” after all of download requests are handed to iOS w/ URLSessionDownloadTask:resume(), of which file means that “download is ongoing”. When users terminate the app, at UIApplicationDelegate:applicationWillTerminate, I delete the flag file. Or, if all of download requests not handed to iOS, there's no flag file. Then at app’s relaunch at UIApplicationDelegate:handleEventsForBackgroundURLSession, I check if we have the flag file. If missing, then I can assume that users terminated. Two choices here. Choice-1: I will not recreate URL session. What happens next is that iOS will terminate my app in about 20 seconds. I have no idea if this (== not creating the URL session) is a legal operation but it works. Users can launch w/in this 20 sec, so I put some more code to handle that scenario. Choice-2: I create URL session. What happens next is that iOS calls delegate methods didFinishDownlaodingTo/didCompleteWithError, followed by urlSessionDidFinishEvents. If I don't do anything here, the process (app) keeps alive indefinitely w/o any notification to users: nothing in task switcher. This is nothing more than waste of memory. The option here is to fire local notification and let users know of my app, so that they can go back to my app and can terminate (Again!), though my app clearly appears as a zombie. One issue for both choices: applicationWillTerminate may not be called in certain situation (though I've yet confirmed). At this case, flag file is left as regular ops and show zombie to users. So, the flag file method is just mitigation to the issue, but I think it works most of the time for my app.
Note the app is relaunched sometimes when it's killed by xcode debugger or killed by OS w/ bug (SEGFALUT).
(4) observation: after the app is terminated (by user, etc.) then relaunched by OS, the app is occasionally in active state (UIApplication.shared.applicationState is .active).
I want to notify the user on the download completion by local notification, but since it's active, local notification doesn't fire. So, I need to use UIAlertController instead. Therefore, I can't provide consistent user experience, and should look strange for users: most of the time local notification and very occasionally UIAlert. Note when app started in active state, it appears in task switcher.
Guess: totally no idea how this can happen. One good(?) thing is this happens only occasionally.
Workaround: seems none.
(5) observation: handleEventsForBackgroundURLSession/urlSessionDidFinishEvents is called just once.
I start the download after started background task (application.beginBackgroundTask). Then in expiration handler of beginBackgroundTask, I call endBackgroundTask. I don't know exactly why, but after endBackgroundTask, my process is still given lots of process time, so I can keep requesting download. This might be because download files keep coming w/ didFinishDownlaodingTo. To be a good citizen, I suspend to request further download, and fire local notification to user to put the app to foreground. Now, once I suspend the request, in 4-5 seconds, OS determines the URL session is over and handleEventsForBackgroundURLSession then urlSessionDidFinishEvents are called. This is one-off event. When the user put the app in foreground to resume download, then put it again in background, no handleEventsForBackgroundURLSession/urlSessionDidFinishEvents will come anymore. What's unclear to me is the definition of session's start and end. It seems the session starts at first URLSessionTask.resume(), then ends w/ timeout, which seems to be determined by URLSessionConfiguration.timeoutIntervalForRequest. However, setting large number here (1000 sec) doesn't affect anything, it's always 4-5 seconds.
Guess: no idea
Workaround: don't relay on urlSessionDidFinishEvents while the app is alive. Relay only when app relaunched by OS and at initial handleEventsForBackgroundURLSession/urlSessionDidFinishEvents.
===============
Below, I listed about the sample project (downLoader.zip). You can verify all of above with the sample.
The app has a list of download files. The number of files is 1921, and 56MB in total. They are 256x256 PNG map tile files that are located in a server managed by Geo Spatial Information Authority of Japan (GSI). After downloaded, they are moved to Library/Cache/download. If your device is jail-broken, you can view them w/ Filza.
crash itself to test relaunch
emulate background task expiration
logging to file since debugger doesn't work after relaunch by OS. The file is in Documents, can be moved to PC.
Do play with the real device. Simulator doesn't relaunch the app.
Project built w/ Xcode 8.3.3 and tested w/ iphone6+/9.3.3 and iphone7+/10.3.1
To see if app relaunched w/ xcode, go to Debug/Attach to Process menu and see if downLoader is listed.
===============
I think URL session background download behaves trickily, especially at relaunch. We need to consider at least the observations I listed above, or the app users will get confused.
I found same error code 257 from online users feedback.In the error scene, the location always refers to a strange bundle identifier:com.sdyd.SDMobileMixSmart. And I guess this's an apple system bug.
I am developing an app that logs down the screen on/off events. It's a kind of smartphone usage analysis app. All the app does is to write down a log like this:
2015 July 25 at 03:54:12 PM - Screen on
2015 July 25 at 03:59:38 PM - Screen off
2015 July 25 at 04:20:52 PM - Screen on
2015 July 25 at 04:22:32 PM - Screen off
...
2015 July 26 at 10:20:32 AM - Screen on
2015 July 26 at 10:22:11 AM - Screen off
2015 July 26 at 11:30:38 AM - Screen on
2015 July 26 at 10:31:02 AM - Screen off
...
"Screen on": user press home button (or power button) and enter passcode/unlock password if there is any.
"Screen off": user press power button to turn off the screen.
I was able to find some way to do this this on Android using the broadcast receiver to capture the events sent by the system. But in iOS there seems to be a problem since iOS only allows background services to run several minutes, I am not even sure if I can detect the "screen on/off" event on iOS.
I did some researches on this and found some articles, but those weren't help much:
http://www.macworld.com/article/1164616/how_ios_multitasking_really_works.html
Lock Unlock events iphone
My question is "Is it possible to make an app like this in iOS (latest version - 8.4) ?"
Thanks.
It may not be possible to meet all your requirements within the published, non jail broken iOS device using the background service. I can see the notifications come across, I'm just not sure about the backgrounding.
Since others have been saying it's not possible, I'm digging a little deeper here to see just how much can be done.
Because iOS is currently restricted to a small number of background modes (situations where events are delivered in the background), or a mode where your app is granted a few minutes of time after the user navigates away from your app, the primary issue is going to be tricking the system into allowing your app to get time in the background when needed.
There are several background modes, described in the Programming Guide to Background Execution. If you can, for example, send push notifications periodically to awaken the app to "download content", you may be able to get some time periodically as the system sees fit to do so.
The background Daemon is a possible solution, but only for your own use, but not via the App Store. The official take on this is in the App Store Review Guidelines -- the relevant section is 2.8 (presumably you'd get your daemon on by having the app install it "behind the scenes"):
2.8 Apps that install or launch other executable code will be rejected
There may be some system logs that iOS keeps for itself; if you can gain access to those, you'd have your data. I doubt, however, that this is available programmatically from a non jail broken phone.
I was able to test out some Swift (2.0) code that uses the Darwin Notifications mentioned in one of the Stack Overflow discussions that your original question led to: Lock / Unlock Events for iPhone. I didn't actually run in the background, but I did verify that the events are eventually delivered, even if the app isn't running when the actual lock event takes place. When my app is switched in, the notification gets called. So, if you were able to get time from the system, you'd get the (delayed) notification when Apple's algorithms decide to give you time.
The code snippet (I stuffed it into some random app) that allows the listening is as follows:
import UIKit
import CoreFoundation
class MainViewController: UIViewController, UIWebViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// CoreFoundation compatible types
var cfstr: CFString = "com.apple.iokit.hid.displayStatus" as NSString
var notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterAddObserver(notificationCenter, nil,
{ (noti: CFNotificationCenter!, aPtr: UnsafeMutablePointer<Void>, aStr: CFString!, bPtr: UnsafePointer<Void>, aDict: CFDictionary!) -> () in
print("got notification") }, cfstr, nil, CFNotificationSuspensionBehavior.DeliverImmediately)
}
// [... more stuff ...]
}
If you can use the C or objective C code i guess this code might help you.
notify_register_dispatch("com.apple.iokit.hid.displayStatus", ¬ify_token, dispatch_get_main_queue(), ^(int token) {
uint64_t state = UINT64_MAX;
notify_get_state(token, &state);
//notify_cancel(token);
debug("com.apple.iokit.hid.displayStatus = %llu", state);
});
Provided that you are able to run your app in background modes.
State will provide the screen on off status
I've been doing a application which involves uploading videos to S3(comparatively large size) using AWS iOS SDK enabling background mode/fetch.
However I have observed that the background upload task continues for a while approx 3 mins and then it gets suspended/app gets killed.
MyApp[2548] has active assertions beyond permitted time:
{(
identifier: Background Content Fetching (66) process: MyApp[2548] permittedBackgroundDuration: 30.000000 reason: backgroundContentFetching owner pid:33 preventSuspend preventThrottleDownUI preventIdleSleep preventSuspendOnSleep
)}
The issue is very much explained in here https://trovepromo-tf.trove-st...
The answer given here "If you do not call the completionHandler within 30 seconds your app will crash in the background" seems right. So Ive implemented URLSessionDidFinishEventsForBackgroundURLSession: in my VC and
handleEventsForBackgroundURLSession:
in AppDelegate.
However the problem is still that the upload gets suspended after 3 minutes( I think OS does this)
My understanding is that URLSessionDidFinishEventsForBackgroundURLSession: delegate is called before this shutdown/suspension. Am I right about this?
Is there anyway I could continue the upload from inside the URLSessionDidFinishEventsForBackgroundURLSession:
somehow?
Or is there any automatic way that the upload process continues regardless of the fact that app has been woken up and put back to sleep.
On start app i called AudioSessionInitialize and check result value.
If it was kAudioSessionInitializationError then i informed user that application can not work properly cause audio does not work.
But since iOS 7 this function was deprecated.
It was similar question before: A fix for AudioSessionInitialize Deprecated? but... it has no any mention about how to get initialization error status.
And of course, i know how to initialize audio session without this function: Just calling [AVAudioSession sharedInstance] cause hidden call of AudioSessionInitialize. But it returns non zero value anyway. So new interface does not allow to get initialization error status.
upd: I'm not sure it's important but i receive message to stdout (which i redirected to log file) while call with error happens:
ERROR: [7971] 146: Unable to talk to server: error 0x10000003
(268435459)
explaination why i need this:
It happens very rarely (less then 0,1% of case) but it happens for any firmware. Last case registered for iOS 7.0.3 - so it's actual return value still (btw, first case registered for first firmware my app supported: iOS 2.2). It's important feature (cause i develop alarm clock) - it allows to know user about issues in the evening and not in the morning (cause in the morning this issue will cause oversleep).