Background task swift 3.0 iOS - ios

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.

Related

Background fetch stopped working on ios 13 and newer

If a method is Deprecated, does this mean it will not work in updated systems?
I was using setMinimumBackgroundFetchInterval for background fetch and I believe it stopped working for ios 13 and newer versions.
Do you think it is because it is deprecated or a bug?
When I use Simulate Background Fetch it works fine, but it does not fetch when I really use the app.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let successCallback: (_ data: Double) -> Void = { (data) in
if data == nil {
// No new data
completionHandler(.noData)
} else {
// There is a new data set
self.bodyMassObserverQueryTriggered() // count todays steps and updates database
completionHandler(.newData)
}
}
getSteps(completion: successCallback) // count todays steps and updates database
}
and I have the following lines in applicationdidfinishwithoptions:
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
UIApplication.shared.setMinimumBackgroundFetchInterval(5*60)
Do I need anything else?
EDIT: One thing i found is i needed to remove BGTaskSchedulerPermittedIdentifiers from Info.plist according to apple docs:
Adding a BGTaskSchedulerPermittedIdentifiers key to the Info.plist disables application(:performFetchWithCompletionHandler:) and setMinimumBackgroundFetchInterval(:) in iOS 13 and later.

How to use background task using Swift 3?

I am new in background tasks. I have a small work that I am fetching tweets and If my app is in background mode then also it should fetch tweets, but I don't know how.
I am using simply Timer in Appdelegate didFinishLaunchOption Method. When I will close the app then it's not working. I am new in that so please any suggestion. Here below is my code:
Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(getTweets), userInfo: nil, repeats: true).
func getTweets() {
let locationName = Helper.sharedInstance.userDefault.value(forKey: ModelKey.currentLocation) as? String
let accessToken = Helper.sharedInstance.userDefault.value(forKey: ModelKey.twitterAccessToken) as? String
if (locationName == "Bengaluru" && nil != accessToken) || (locationName == "Bangalore" && nil != accessToken){
tweetModel.getTweets(accessToken: accessToken!, city: ModelKey.blrcitytraffic, cityName: "Bengaluru")
}
}
Text to speech is also there but when I will close the app then it stops speaking. If I am not using app then also it can fetch tweets and text to speech should work using a background mode. How long does that work?
A background task means you need to use background threads. Threads in iOS are too many, but if you want to make only background task, you should use two threads; the main and background thread that their structure is:
DispatchQueue.global(qos: .background).async {
//background code
DispatchQueue.main.async {
//your main thread
}
}
So, you firstly initialize the global queue with background mode. This thread can be used for background task and then you must use main thread (only if you want) for doing something when the background task is finished. This can be an option. Another option should be applicationDidEnterBackground in appDelegate and you can only must put your code in that method.
You need to do three things:
In your Info.plist add the following entry for key Required background modes to allow background network access:
Required background modes: App downloads content from the network
In your AppDelegate add to your applicationDidEnterBackground():
func applicationDidEnterBackground(_ application: UIApplication) {
// Fetch no sooner than every (60) seconds which is thrillingly short actually.
// Defaults to Infinite if not set.
UIApplication.shared.setMinimumBackgroundFetchInterval( 60 ) )
}
Also in AppDelegate implement
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
var fetchResult: UIBackgroundFetchResult!
if doingYourStuffActuallyCreatesNetworkTraffic() {
fetchResult = UIBackgroundFetchResult.newData
} else if thereWasAnError() {
fetchResult = UIBackgroundFetchResult.failed
} else {
fetchResult = UIBackgroundFetchResult.noData
}
completionHandler( fetchResult )
return
}
There are still some pitfalls, e.g. there is no guaranteed maximum fetch interval, and background execution might behave substantially different in XCode/Simulator than on real devices.
You could take a look at this pretty similiar topic:
performFetchWithCompletionHandler never gets fired
and of course
https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

How to write code that will not pause when user press home button

I want to implement image sync in my ios app. for this what i have done is
take a photo
upload that photo to firebase
get the firebase stored url and send it to an api server so that server stores that info to a database
It works ok as long as the app is running, but when I exit out from the app pressing home button everything pauses.
So how can run a code that will not pause if app goes to home page?
As per this documentation,
For tasks that require more execution time to implement, you must
request specific permissions to run them in the background without
their being suspended. In iOS, only specific app types are allowed to
run in the background:
Apps that play audible content to the user while in the background,
such as a music player app
Apps that record audio content while in the
background Apps that keep users informed of their location at all
times, such as a navigation app
Apps that support Voice over Internet
Protocol (VoIP) Apps that need to download and process new content
regularly
Apps that receive regular updates from external accessories
Apps that implement these services must declare the services they
support and use system frameworks to implement the relevant aspects of
those services
Declaring the services lets the system know which
services you use, but in some cases it is the system frameworks that
actually prevent your application from being suspended.
Link: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
Here is how to implement (taken from this answer by Ashley Mills):
func doUpdate () {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let taskID = beginBackgroundUpdateTask()
var response: NSURLResponse?, error: NSError?, request: NSURLRequest?
let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
// Do something with the result
endBackgroundUpdateTask(taskID)
})
}
func beginBackgroundUpdateTask() -> UIBackgroundTaskIdentifier {
return UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({})
}
func endBackgroundUpdateTask(taskID: UIBackgroundTaskIdentifier) {
UIApplication.sharedApplication().endBackgroundTask(taskID)
}
[Swift3] This is kind of worked for me
func doUpdate () {
DispatchQueue.global(qos: .background).async {
let taskID = self.beginBackgroundUpdateTask()
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5), execute: {
print("printing after 10 min")
})
DispatchQueue.main.async {
self.endBackgroundUpdateTask(taskID: taskID)
}
}
}
func beginBackgroundUpdateTask() -> UIBackgroundTaskIdentifier {
return UIApplication.shared.beginBackgroundTask(expirationHandler: {})
}
func endBackgroundUpdateTask(taskID: UIBackgroundTaskIdentifier) {
UIApplication.shared.endBackgroundTask(taskID)
}
Not sure what is the max time for this to complete since i see there is a expirationHandler

how to wake up iOS parent app with sendMessage from complicationController

I am trying to wake up the iOS parent app by sending a message from watchkit extension.
This though does only work when below sendMessage function is called from the watchApp / ViewController. When it is called from ComplicationController, the message is sent, but the iOS parent app does now wake up.
Any advice appreciated. (please any code reference in Swift)
Here the simplified code:
In AppDelegate and ExtensionDelegate:
override init() {
super.init()
setupWatchConnectivity()
}
private func setupWatchConnectivity() {
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
In ExtensionDelegate: (no problem here, message is successfully sent)
func sendMessage(){
let session = WCSession.defaultSession()
let applicationData:[String:AnyObject] = ["text":"test", "badgeValue": 100 ]
session.sendMessage(applicationData, replyHandler: {replyMessage in
print("reply received from iphone")
}, errorHandler: {(error ) -> Void in
// catch any errors here
print("no reply message from phone")
})
}
print("watch sent message")
}
In AppDelegate: (not received when iOS app not running / not in foreground)
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
let text = message["text"] as! String
let badgeValue = message["badgeValue"] as! Int
dispatch_async(dispatch_get_main_queue()) { () -> Void in
print("iphone received message from watch App")
self.sendNotification(text, badgeValue: badgeValue)
let applicationDict = ["wake": "nowAwake"]
replyHandler(applicationDict as [String : String])
}
}
this is how the function is called from Complication Controller (which does send the message but not awake the parent app):
func requestedUpdateDidBegin(){
dispatch_async(dispatch_get_main_queue()) { () -> Void in
let extensionDelegate = ExtensionDelegate()
extensionDelegate.loadData()
}
}
The main problem is that you're trying to include (nested) asynchronous calls within your complication data source. However, your requested update will have reached the end of its method, and no timeline update will actually take place (since you didn't reload or extend the timeline, and even if you had, no new data would have been received in time for the current update).
Since no new data would be available for the scheduled update, you'd have to perform a second update to use the new data once it was received. Performing two back-to-back updates is not only unnecessary, it wastes more of your daily complication budget.
Apple recommends that you fetch and cache the data in advance of the update, so the complication data source can directly return the requested data to the complication server.
The job of your data source class is to provide ClockKit with any requested data as quickly as possible. The implementations of your data source methods should be minimal. Do not use your data source methods to fetch data from the network, compute values, or do anything that might delay the delivery of that data. If you need to fetch or compute the data for your complication, do it in your iOS app or in other parts of your WatchKit extension, and cache the data in a place where your complication data source can access it. The only thing your data source methods should do is take the cached data and put it into the format that ClockKit requires.
How can you update the complication?
Use background updates from the phone to transfer the data to be on hand for the complication's next scheduled update. transferUserInfo and updateApplicationContext are suited for this type of update.
Use transferCurrentComplicationUserInfo to immediately transfer complication data and update your timeline.
Both of these approaches have the advantage of only needing one update to occur.

How to start GPS tracking using push notification

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.

Resources