How to run a service in iOS background to receive local notification? - ios

I am working on app where there is an MQTT connection with the server and server is sending some values related to device and UI changes accordingly. But when app is in background user should get local notification that certain values are changed. I know background service are not allowed in iOS but I want to make sure that is that there is no way to achieve this.
I successfully added local notification with app in background by UIApplication.shared.beginBackgroundTask but it's only work for 3 min exact after that apple terminates the app.
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskInvalid)
}
func endBackgroundTask() {
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
And just calling self.registerBackgroundTask() makes the app runnable in background for three min.
Next that I am going to try is that background fetch and widget to run service, Here I just want some suggestion that is there any chance that one of above two will work ?

It sounds like "Communicating with an External Accessory" would be the background mode that fits your application.
See Apple Docs for reference.
You have to activate Backround Mode for your project and set the value to "external-accessory". Then you can do ongoing small downloads in background. Apple mentions heart rate monitors as an example.
Please note that continous background polling is waste of energy and would deplete battery quickly. Check if this is really needed for your application. If the user just needs infrequent notifications/alarms, remote notifictions would be a much better solution. I use remote notifications in my own projects and it works very smooth and reliable. Additional benefit is, that it would wake up an app even if the user has closed it before.

For more than 3 Minute. You will be enable any mode. Otherwise when app will enter in background app. After 3 min.App will not perform any action.

Related

Xamarin/MAUI iOS best approach to update app in background every 15 minutes?

I would like that my app could make a small api call to a server so that it checks if there is an update, the data is very small and it shouldn't require that much time to execute, if it fails, it's not a problem that will try the next 15 minutes. It will show a generic notification to the user, so that the real data update will be done when the app is loaded. I know how to do the notification part, but not the api call.
The app should do this call when it's in the background and even if the user closes it from the multitasking UI and auto start when the device is turned on but the app hasn't been opened once yet.
In Android you can archieve this with the AlarmManager but from what I understand there is no way for this on iOS other than remote push notifications?
In the iOS part, you can achieve it with Backgrounding with Tasks​. However, iOS backgrounding is much more restrictive than on Android as there are only a few specific tasks that apps are allowed to do in the background (things like VOIP, audio, location, etc.). You can use a UIApplication background task to delay that suspension for a few minutes, but you can’t delay it indefinitely.
public override void DidEnterBackground (UIApplication application) {
nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});
new Task ( () => {
DoWork();
UIApplication.SharedApplication.EndBackgroundTask(taskID);
}).Start();
}
The other approach is using remote notifications which means applications can register to receive notifications from a provider, and use the notification to kick off an update before the user opens the application.

How to make WCSession reachable when the watchOS app is running in background with HKWorkoutSession

My watchOS app uses workout API in order to stay running while the app goes to background. The issue is that WCSession becomes unreachable when the app is in background. However, I'm able to run my code and on some condition, it needs to send a message to the iPhone counterpart app.
The specifics of the app require that user doesn't have to interact with it - if there is a timeout, the watch app should send the message to the phone automatically.
Is this possible to achieve? Thanks.
I believe the handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) method is what you're looking for. Documentation Link
Without seeing your code I can't be sure what your current progress is, but I have used this method to update watchOS Complications in the background as the user's location changes.
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
if WKExtension.shared().applicationState == .background {
// Do your background work here.
if let watchComplication = task as? WKWatchConnectivityRefreshBackgroundTask {
pendingConnectivityTasks.append(watchComplication)
}
}
task.setTaskCompletedWithSnapshot(true)
}
completePendingConnectivityTasksIfNeeded()
}
As a side note I will add if your app is not an actual workout app, it will get rejected during App Review for using a HealthKit workout session.

How to continue a Timer once an app enters background [duplicate]

This question already has answers here:
How to call a function when the app is inactive (e.g. playing music in background)?
Swift 3 - How to make timer work in background
(10 answers)
Closed 2 years ago.
I am trying to execute a simple function that runs a Timer in the background of the app.
In short,
A 10 second Timer starts as soon as app begins
I lock the device after seeing confirmation the Timer has begun in the Foreground
10 seconds later, with the app in the Background, I should expect a log to appear. This currently works in the simulator but not on the device
Full code below. You can also download the app itself to try.
After consulting popular answers such as this (which also contains conflicting answers about whether a Timer actually runs in the background or not), I am not exactly what the authoritative answer is on at the topic.
Any help is appreciated.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var timer : Timer?
if timer == nil {
NSLog("Timer started")
timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true, block: {_ in NSLog("After 10 seconds show up") //this will not work on a device
//Also, I get the error: Can't end BackgroundTask: no background task exists with identifier 1 (0x1), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
})
}
}
}
It may be confusing, but questions like that actually do answer the question. Bottom line, the app is completely suspended (including timers) when the app enters the background. You can, however, request a little time before it is suspended as outlined in Extending Your App’s Background Execution Time. You’ll see many online references to this being able to allow an extra three minutes of background execution before it is suspended, but in iOS 13 this has been further reduced down to only 30 seconds.
Now, apps that have legitimate need or background execution (e.g. a navigation app, a VOIP app, a music playing app, etc.) can request special background execution modes (see About the Background Execution Sequence, but none of these are intended solely for the purpose of keeping a timer running in the background.
If you want to notify a user at some designated time in the future, use user notification service. But do not just attempt to keep your app running in the background.
By the way, while that link describes the process, their code snippet is misleading. See https://stackoverflow.com/a/23831862/1271826 for a more contemporary rendition.

How to run iOS app in background forever?

I am making an iOS application for myself only. I need to execute certain code every 30 minutes when application is in background.
As I am only user of this app, don’t need to worry about batter percentage and apple review process. I can use any/all background modes VOIP, music, etc.
Is is possible to run that code in background every 30 minutes?
Kindly guide me the direction.
Its posible.
One way to do it is to create a fake VPN packet tunnel extension. And put your code in VPN Manager class.
VPN extension part will keep running while your app is in background or even force quite by user.
You can write your code in this method
NEPacketTunnelProvider
override func startTunnelWithOptions(options: [String : NSObject]?, completionHandler: (NSError?) -> Void) {
fetchData()
}
func fetchData() {
// Do not use the NSTimer here that will not run in background
let q_background = DispatchQueue.global(qos: .background)
let delayInSeconds: Double = 300.0 // seconds
let popTime = DispatchTime.now() + DispatchTimeInterval.seconds(Int(delayInSeconds))
q_background.asyncAfter(deadline: popTime) {
// Fetch your data from server and generate local notification by using UserNotifications framework
fetchData()
}
}
Why not go with Background Fetch?
It is available for apps likes News or Social media, so that apps can have the latest data even before the user interaction. It allows periodic background execution as is.
A simple call will fetch data every hour.
// Fetch data once an hour.
UIApplication.shared.setMinimumBackgroundFetchInterval(3600)
Lastly its not a workaround or any private API. Your app will be accepted by appstore as well.

Frequent time interval background fetch made

I have developed two different applications
App1 - With Healthkit enabled.
App2 - ionic application
App1 task : Read data from healthkit which is store in the server.
App2 task : Retrieve the stored data from server and display.
I started App2 from App1 using openURLScheme. So App1 running on the background mode and also It should be continued more than 3 mins to an hour.
I tried following Scenario:
bgTask = self.applicationUI!.beginBackgroundTaskWithName("MyTask", expirationHandler: { () -> Void in
self.applicationUI!.endBackgroundTask(self.bgTask!)
self.bgTask = UIBackgroundTaskInvalid;
})
self.bgTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
self.monitorInBackground()
})
I get error : permittedBackgroundDuration: 180.000000 reason: finishTask
extent background process for next 3 minute (After IOS 7 introduce. before IOS 7, the process execution time was 10 minute).
Note :
I hope it can be feasible using APNS silent notification. But I expected better solution other than the APNS.
Apple has a good section on background execution in their documentation.
The two ways of doing this are a silent notification (as you suggest) and background fetch.
The "pro" of using a silent notification is that you can control when it happens fairly precisely as long as your user is online. (Which they probably have to be to access the server anyway.) But, yes, it adds a lot of complexity.
Background fetch works nicely, but you don't get much control over when it happens. This may or may not be a problem, depending on what your app does.
Other options that might work include background audio, location updates and VoIP, but they might get you rejected.
Just running a background task won't work -- that's designed for finishing off tasks rather than keeping them running for a long time.

Resources