I'm sending an push notification a at 8am to start GPS in an application running in background. Push notification receiving, but GPS not started tracking. How can i achieve this? I m using following code.
func application(application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
NSLog("userInfoComp %#",userInfo);
var app = UIApplication.sharedApplication()
var bgTask2 : UIBackgroundTaskIdentifier?
bgTask2 = app.beginBackgroundTaskWithExpirationHandler { () -> Void in
app.endBackgroundTask(bgTask2!)
bgTask2 = UIBackgroundTaskInvalid
}
if bgTask2 == UIBackgroundTaskInvalid {
return
}
PSLocationManager.sharedLocationManager().prepLocationUpdates()
PSLocationManager.sharedLocationManager().startLocationUpdates()
}
You can not start location service when your app is in background mode. You need to use startMonitoringSignificantLocationChanges when your app is active with user's permission (to track him even if user is not using the app) to get location update in background mode.
You will get location update even when app is terminated by user at every 5 min if user is not stationary.
If your app is in background you may get location update using startLocationUpdates but it's not reliable even if 3-4 apps are in background os may stop updating location.
Related
I need to execute a task when the app is in background state. For example, when the app enters the background state, then every 5 minutes(app is in background in this time) a task is executed.
I tried with location changed but I can't use a precise location(for battery consume) then I used significant location changed but If user doesn't move or doesn't change cell tower location is not updated.
Can you help me about it?
Yo could use the iOS Background Fetch feature where you can specify minimum background fetch interval. But actual interval between successive invocation of your code will be determined by iOS framework.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let data: String? = nil
do {
//fetch some data
if let data = getSomeNewData() {
/* use the completionHandler to talk to the system and tell us if the app fetched new data, or if no data was available. */
completionHandler(.newData)
} else {
completionHandler(.noData)
}
} catch {
print(error)
completionHandler(.failed)
}
}
see also question: swift-ios-refreshing-app-data-when-in-background
Another option is to setup a server that will send a (silent) push notification to your app every 5 minutes that your app can react to.
I am using remote notification with content-available: true flag to launch the app in the background on silent push notification and process or fetch updates from remote API. The code executes fine when the app is in the foreground, or in suspended state after previous run.
During tests in background when the application is launched by the system based on incoming silent push notification, the code is processed only partially and the app is quickly suspended after about 150 ms. I expected the app will be given 30 seconds to process the incoming notification and its payload. Do I need to adjust the app capabilities or request a background task if I need more time to process and/or fetch new data?
Deployment target iOS 8, testing on iOS 9. Xcode 7.3.1, Swift 2.2.1.
Capabilities: Background Modes ON, Modes: Remote notifications Enabled
AppDelegate
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if let userInfo = userInfo as? [String: AnyObject] {
// Processing of the payload is done in separate Operation class (using Operations framework)
// The completion handler is called on the end of the processing/fetching in that operation
// But in case of launching the app in the background it never reaches the call of the completion handler
let parseNotificationOperation = ParseNotificationOperation(userInfo: userInfo, fetchCompletionHandler: completionHandler)
MainService.shared.enqueueApnsOperation(parseNotificationOperation)
}
}
Martin Koles,
You can make use of expirationHandlers to get extended time for background execution. Though how much time will iOS assign to your app depends on various factore which we cant controll we have noticed mostly it provides till 3 mins for our app to execute in background.
Here is how you can achieve it :)
In you AppDelegate declare,
UIBackgroundTaskIdentifier backgroundTask;
and when you recieve APNS inside didReceiveRemoteNotification write this,
if (!backgroundTask || backgroundTask == UIBackgroundTaskInvalid) {
backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
//do all clean up job here gets called few seconds before expiration
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
}];
}
EDIT
Just realized you are making use of swift so here is code for you in swift :)
Declare a variable called backgroundTask in AppDelegate,
var backgroundTask : UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
and use it in your didRecieveRemoteNotification as below,
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if let userInfo = userInfo as? [String: AnyObject] {
let parseNotificationOperation = ParseNotificationOperation(userInfo: userInfo, fetchCompletionHandler: completionHandler)
MainService.shared.enqueueApnsOperation(parseNotificationOperation)
if (backgroundTask == UIBackgroundTaskInvalid) {
backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler { () -> Void in
self.endTask()
}
}
}
}
Finally write a method to invalidate your expiration handler once you are done with it :)
func endTask(){
//do all your clean up here, this is called few seconds before the task expires.
UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid;
}
That's it :) Happy coding buddy :)
In application we have mechanism like native Reminder app in iOS with firing notifications when user enter or exit in some region.
But two devices behave differently (5 and 5s) in same time. All devices have enable notifications, and allow use locations.
Two devices have a some "travel" and in the route created 10 points. First device (5) when came to finish received only 6 notifications, (5s) don't receive any notification.
But my question is how I can know when my app is restart in background or continue working. Because, all log in app I redirect into a file, and after download container and analyze what happened in app in travel time.
I noticed app restart in same times when device is enter to region and my log marks fired in the file but notifications don't receive. This is happended when app try to get some information from web service in didFinishLaunchingWithOptions
And maybe this is problem. How to know distinguish restart app or continue working. Thx.
Are you checking UIApplicationLaunchOptionsLocationKey in didFinishLaunchingWithOptions similar to (sorry, Swift is what I have now):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsLocationKey] != nil {
// app was launched in response to incoming location event
}
}
Additionally, if you're not already doing this you may need to create notifications differently if app is in background:
// Show an alert if application is active
if UIApplication.sharedApplication().applicationState == .Active {
if let message = notefromRegionIdentifier(region.identifier) {
if let viewController = window?.rootViewController {
showSimpleAlertWithTitle(nil, message: message, viewController: viewController)
}
}
}
else {
// Otherwise present a local notification:
let notification = UILocalNotification()
notification.alertBody = notefromRegionIdentifier(region.identifier)
notification.soundName = "Default";
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}
I am searching the way about how to handle push notification payload data as soon as the notification reaches to the client app without opening or tapping it.And I am still not getting the data unless the user tap or open it from notification center or banner or alert.The function didReceiveRemoteNotification only triggered when the user click the notification on the screen.So,how to get the notification payload data when the notification arrive to client app even the user ignore(without open or tap) it.
INFO : I heard that GCM(Google Cloud Messaging) can make notification handler if the client app user tapped the notification or not.It can catch the notification payload json data as soon as it reach the client app without even need user to tap or open it.Is that right?
I really need a hand to pick me up with getting notification payload data on ios without even need a user to open or tap it.
Update : The app is still running on device which mean it was active.I can get the payload data when i click my notification which was "{aps:} json i get it.But,I still cant get the data when i don't open the notification"
Here is my state
When the app was at foreground,I get the data.
1.I run the App,
2.Send Notification,
3.Get the notification with an alert,
4.I get the data(payload).
Work fine when app is active.
But,when the app reach to background
1.Run The app,
2.Close The App by pressing home button,
3.Send Notification,
4.Get the notification.
5.But,cant get the data until i click notification that I was receive at banner
or notification center.
But,when i click the notification at banner or notification it went to app and then i get the data.
Is there anyway that i can get the data if the app in background when the notification received.
Here is the code :
import UIKit
import RealmSwift
let realm = try! Realm()
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var data : [NSObject : AnyObject]!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//one singnal is the push notification service that i use for push notification.
let oneSignal = OneSignal(launchOptions: launchOptions, appId: "__app_id__") { (message, additionalData, isActive) in
NSLog("OneSignal Notification opened:\nMessage: %#", message)
if additionalData != nil {
NSLog("additionalData: %#", additionalData)
}
}
oneSignal.enableInAppAlertNotification(true)
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("User Info : \(userInfo)")
if let custom = userInfo["custom"] as? NSDictionary{
if let a = custom["a"] as? NSDictionary{
print("A : \(a)")
}
}
}
I came across the same problem. As mentioned in the previous comments, this post is quite helpful.
According to Apple,
When a remote notification arrives, the system displays the
notification to the user and launches the app in the background (if
needed) so that it can call this method. Launching your app in the
background gives you time to process the notification and download any
data associated with it, minimizing the amount of time that elapses
between the arrival of the notification and displaying that data to
the user.
The first thing you have to do is to allow your app to do something when in background. You do this by adding Required background mode in your info.plist, then add it App downloads content in response to push notifications. Your info.plist should look something like this:
Now this is done, your app should awake when it receive a notification. You can execute a small code inside didReceiveRemoteNotification. Such as this.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
//do some code here
UIBackgroundFetchResult.NewData
}
Note that you have to pay attention to the completionHandler:
As soon as you finish processing the notification, you must call the
block in the handler parameter or your app will be terminated. Your
app has up to 30 seconds of wall-clock time to process the
notification and call the specified completion handler block.
Let me know if everything is clear :)
I have a small swift app written. I have successfully configured the remote notification services. Here is the code
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
println("received notification")
var remoteServer = RemoteServer()
remoteServer.sendDataToServer { (success) -> () in
completionHandler(UIBackgroundFetchResult.NewData)
}
}
Now, I am using Houston ruby gem to send the notifications. which looks something like this
require 'houston'
APN = Houston::Client.production
APN.certificate = File.read("production_certificate.pem")
token = "token here"
notification = Houston::Notification.new(device: token)
notification.content_available = true
notification.alert = "test"
notification.badge = 0
APN.push(notification)
I can successfully see the message on the lock screen. But its when I swipe the notification , which opens the app, thats when the didReceiveRemoteNotification method gets called.
I am wondering if there is a way to fire off didReceiveRemoteNotification without the user having to swipe the notification ? (Pretty much no user activity required) ?
I tried setting the alert to nil. But then since no user activity can be detected didReceiveRemoteNotification never gets fired off.
How should I handle this scenario? Should I be using performFetchWithCompletionHandler , but then how to fire it from the remote server?
UPDATE: the didReceiveRemoteNotification does get sent out when the app is currently opened, but as soon as the device is locked. The same isn't getting called.
Thanks