Hi I want to fetch some data using Alamofire and a NSTimer
The app works fine on foreground, the timer is set to call the GetEstado function each 30 seconds but when the app enters background the timer is paused and if my server updates something it does not actually updates on the app
Here's my code
func GetEstado(){
Alamofire.request(.POST, "https://myurl/getestado.php", parameters: ["id": id])
.responseString { rta in
if let dataFromString = rta.result.value!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
for (key,subJson):(String, JSON) in json {
if(key=="e"){
if(subJson.stringValue=="1"){
self.Timer.invalidate()
print("ACTUALIZACION")
let notification = UILocalNotification()
notification.fireDate = NSDate(timeIntervalSinceNow: 5)
notification.alertBody = "UPDATED"
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(notification)
self.performSegueWithIdentifier("UPDATED", sender: self)
}
else{
print("UPS")
}
}
}
}
}
}
Thanks
I think #Paulw11 answer your question in his comment, you can't use NSTimer in background, Apple has a known list of the task that can be produced in background, you can read more here.
But let think for a moment what you're trying to achieve, let suppose you can do it using NSTimer, then you have your app in background consuming memory and battery in your device constantly to try to get the updates from your server, this is not recommended at all.
It's for this that Apple/Google introduced Apple Push Notifications (Google: Android Push Notifications) to handle that the server notifies the app when it's in background or inactive regarding any task that is programmed in the server, lets say a new mail, a new message in the case of messaging apps, etc.
Then, the option you have is use the service proposed by Apple, you can read more in the following docs:
Notifications
Apple Push Notification Service
I hope this help you.
Related
The core of the app I am building is that it sends a push notification to the user twice a week with a number from a website, at a specific time twice a week (example of the Swift code I'm using for this below).
I did some research about background execution, but I think I will not be able to use it because it's so limited (only for location, limited time, etc.).
Using a server is another option. I do not intend to make money with this app so it has to be a free option. That's why I was looking into Firebase.
I'm learning Swift now, it's the first programming language I'm learning.
Does anyone have an opinion on how to go about doing this?
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = URL(string: "https://store.nike.com/be/nl_nl/pd/air-vapormax-flyknit-hardloopschoen-heren/pid-11384993/pgid-12169774")!
let request = NSMutableURLRequest(url: url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var message = ""
if error != nil {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "exp-pdp-local-price js-pdpLocalPrice"
if let contentArray = dataString?.components(separatedBy: stringSeperator) {
if contentArray.count > 0 {
stringSeperator = "€"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
message = newContentArray[0]
print(message)
}
}
}
}
}
if message == "" {
message = "The jackpot couldn't be found. Please try again."
}
DispatchQueue.main.sync(execute: {
//De text in het label van de weer app = message
//Gebruik 'self.' om naar de viewcontroller te verwijzen, want hier zit je in een closure en niet in de viewcontroller zelf.
})
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Push notifications are not complicated.
Basically you have to register your device to APNS ( Apple push notification system ), once registered device, APNS will return you a token (each token is device unique). When u get the token from APNS you have to send the token to the server which will provide your devices with push notifications. Once the server app receives a token from device, you can save it in database.
For example :
username1 mobile_token1, mobile_token2.
Now, you can get tokens from database for specific username and send push notifications from the server to the devices.
When you send push notifications from server to devices, you have to send the token and data content to APNS, APNS then will send notification to specific device.
So the answer for your question is, if you want to send push notifications you have to send them from backend to devices, the moment you will send push notifications is when the web content changes, you get the tokens from the database, and send notifications to APNS.
The logic is same when you use firebase instead APNS.
Here is a nice tutorial for client side
https://www.raywenderlich.com/156966/push-notifications-tutorial-getting-started
It seems you don't need a push notification (also called remote notification) but a local one (see apple documentation).
You can configure when during week you want this local notification to be fired.
Edit: I did not catch that content of notifications depends on your previous piece of code.
So you can still use a local notification. It will be triggered by what is called a background fetch.
See this tutorial for example (search 'Background fetch' on the page).
I want to receive a local notification in my app (swift) when I'm not connected to Internet and I have some information registered in my Local Data base.
Is it possible to do that please?
Local Notification doesnot requires internet.
About Local Notification from apple developer site
Local notifications give you a way to alert the user at times when your app might not be running. You schedule local notifications at a time when your app is running either in the foreground or background. After scheduling a notification, the system takes on the responsibility of delivering the notification to the user at the appropriate time. Your app does not need to be running for the system to deliver the notification.
For more info check this link. You can also check this link for tutorial.
do like this :
public func presentNotification(_ notifAction: String, notifBody: String) {
let application = UIApplication.shared
let applicationState = application.applicationState
if applicationState == UIApplicationState.background {
let localNotification = UILocalNotification()
localNotification.alertBody = notifBody
localNotification.alertAction = notifAction
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.applicationIconBadgeNumber += 1
application.presentLocalNotificationNow(localNotification)
}
}
UIApplicationState has these states :
case active
case inactive
case background
I am working on alarm application, i need to schedule alarm on specific time, I use scheduleLocalNotification for scheduling alarms and it's working fine as i want. BUT I need to run to a request to my API server before triggering alarm. In that request I want to check some parameters returning from API server, If that satisfies some condition.
If any one have a method that run on a particular date - time in swift
Please help me for that
func addAlarm (newAlarm: Alarm) {
// Create persistent dictionary of data
var alarmDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey(ALARMS_KEY) ?? Dictionary()
// Copy alarm object into persistent data
alarmDictionary[newAlarm.UUID] = newAlarm.toDictionary()
// Save or overwrite data
NSUserDefaults.standardUserDefaults().setObject(alarmDictionary, forKey: ALARMS_KEY)
scheduleNotification(newAlarm, category: "ALARM_CATEGORY")
scheduleNotification(newAlarm, category: "FOLLOWUP_CATEGORY")
}
/* NOTIFICATION FUNCTIONS */
func scheduleNotification (alarm: Alarm, category: String) {
let notification = UILocalNotification()
notification.category = category
notification.repeatInterval = NSCalendarUnit.Day
switch category {
case "ALARM_CATEGORY":
notification.userInfo = ["UUID": alarm.UUID]
notification.alertBody = "Time to wake up!"
notification.fireDate = alarm.wakeup
notification.timeZone = NSTimeZone.localTimeZone()
notification.soundName = "loud_alarm.caf"
break
case "FOLLOWUP_CATEGORY":
notification.userInfo = ["UUID": alarm.followupID]
notification.alertBody = "Did you arrive yet?"
notification.fireDate = alarm.arrival
notification.timeZone = NSTimeZone.localTimeZone()
notification.soundName = UILocalNotificationDefaultSoundName
break
default:
print("ERROR SCHEDULING NOTIFICATION")
return
}
print("Notification=\(notification)")
// For debugging purposes
if alarm.isActive {
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
}
Waking up an app through a local notification is not possible, this is available only for remote notifications. According to the Notification Programming Guide:
When a remote notification arrives, the system handles user
interactions normally when the app is in the background. It also
delivers the notification payload to the
application:didReceiveRemoteNotification:fetchCompletionHandler:
method of the app delegate in iOS and tvOS
But there is still a catch; even then it is not guaranteed that the app will be launched since, according to didReceiveRemoteNotification:fetchCompletionHandler: documentation:
However, the system does not automatically launch your app if the user
has force-quit it. In that situation, the user must relaunch your app
or restart the device before the system attempts to launch your app
automatically again.
I don't think there is a guaranteed way to schedule a block for execution in some later moment, independently from the state of the app at that time. Depending on your specific requirements and frequency, you could perhaps register for the fetch background mode and implement application:performFetchWithCompletionHandler: to opportunistically fetch and validate server data. Last note: make sure that you are a responsible background app (from my experience Apple takes this requirement seriously)
I am developing apple watch application. when i run the app it is working fine. Now my problem is when the app goes to background mode, the app on the apple watch app will closing automatically. I am writing small code in iPhone app:
func viewDidLoad() {
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
// In your WatchKit extension, the value of this property is true when the paired iPhone is reachable via Bluetooth.
// On iOS, the value is true when the paired Apple Watch is reachable via Bluetooth and the associated Watch app is running in the foreground.
// In all other cases, the value is false.
if session.reachable {
lblStatus.text = "Reachable"
}
else
{
lblStatus.text = "Not Reachable"
}
func sessionReachabilityDidChange(session: WCSession)
{
if session.reachable {
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.text = "Reachable"
})
}
else
{
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.text = "Not Reachable"
})
}
}
}
}
in WatchExtention Code is
func someFunc() {
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
if session.reachable {
ispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Reachable")
})
}
else
{
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Not Reachable")
})
}
func sessionReachabilityDidChange(session: WCSession)
{
if session.reachable {
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Reachable")
})
}
else
{
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Not Reachable")
})
}
}
}
}
Now when enter to background in apple Watch the iPhone app showing Not reachable why ?
It's the default behavior of the AppleWatch, mainly to spare with resources like battery.
session.reachable property is true only when Apple Watch is reachable via Bluetooth and the associated Watch app is running in the
foreground in all other cases, the value is false.
In your case the second option which caused the problem I suppose the bluetooth connection is working.
Anyway the question is what do you like to reach.
Actually the simple rule is that you couldn't wake up the Watch from the iPhone but you could wake up the iPhone app from the Watch.
Two ways with three options to reach the watch when it's counterpart is in the background: send a complication update or send a message (2 options) in the background which will be available for the Watch when it will awake again.
All of them are part of the WCSession Class.
The two options for sending messages are:
- updateApplicationContext:error:
You can use this method to transfer a dictionary of data to the counterpart Watch app.iPhone sends context data when the opportunity arises, means when the Watch app arises.The counterpart’s session on the Watch gets the data with the session:didReceiveUpdate: method or from the receivedApplicationContext property.
You may call this method when the watch is not currently reachable.
The other option is sending data in the background like
- transferUserInfo:
You can use this method when you want to send a dictionary of data to the Watch and ensure that it is delivered. Dictionaries sent using this method are queued on the other device and delivered in the order in which they were sent. After a transfer begins, the transfer operation continues even if the app is suspended.
BUT true for both methods that they can only be called while the session is active. Calling any of these methods for an inactive or deactivated session is a programmer error.
The complication solution is a little bit different but belongs to the same WCSession Class as the earliers.
-transferCurrentComplicationUserInfo:
This method is specifically designed for transferring complication user info to the watch with the aim to be shown on the watch face immediately.
Of course it's available only for iOS, and using of this method counts against your complication’s time budget, so it's availability is limited.
The complication user info is placed at the front of the queue, so the watch wakes up the extension in the background to receive the info, and then the transfer happens immediately.
All messages received by your watch app are delivered to the session delegate serially on a background thread, so you have to switch to the main queue in case you'd like to use or presenting them for UI.
The WWDC talk on WatchConnectivity discusses "reachability" and its nuances in quite a lot of detail, so you should definitely give it a watch.
TL;DR: reachable on the watch is for the most part only going to be true/YES when the watch app's UI is visible on the screen.
I have an app that has a very rich network layer and my apple watch app depends on all the models. Unfortunately the app is not modular enough to make this layer available in the watch app.
I solved this problem by using openParentApplication: to wake up the iPhone app, perform the request and give back the results.
In watchOS 2 this method is gone and I should use WatchConnectivity. The best way to use this would be by sending userInfo dictionaries.
But how can I wake up the iPhone app to handle my requests? To get notifications about new userInfos I have to use the WCSessionDelegate and for that I need a WCSession object. But when should I create that? And how to wake up the app?
I asked an Apple Engineer about this and got the following tip: The iOS-App should be started in a background-task. So the following worked for me pretty well:
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier identifier = UIBackgroundTaskInvalid;
dispatch_block_t endBlock = ^ {
if (identifier != UIBackgroundTaskInvalid) {
[application endBackgroundTask:identifier];
}
identifier = UIBackgroundTaskInvalid;
};
identifier = [application beginBackgroundTaskWithExpirationHandler:endBlock];
Add this to your session:didReceiveMessage: or session:didReceiveMessageData: method to start a background task with a three minute timeout.
Swift version of Benjamin Herzog's suggestion below. Of note, while I did choose to push the work initiated by the Watch to a background task, as long as my app was in the background, the system woke it up just fine. Doing the work in a background task did not appear to be required, but is best practice.
override init() {
super.init()
if WCSession.isSupported() {
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let taskID = self.beginBackgroundUpdateTask()
//Do work here...
self.endBackgroundUpdateTask(taskID)
})
var replyValues = Dictionary<String, AnyObject>()
let status = "\(NSDate()): iPhone message: App received and processed a message: \(message)."
print(status)
replyValues["status"] = status
replyHandler(replyValues)
}
func beginBackgroundUpdateTask() -> UIBackgroundTaskIdentifier {
return UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({})
}
func endBackgroundUpdateTask(taskID: UIBackgroundTaskIdentifier) {
UIApplication.sharedApplication().endBackgroundTask(taskID)
}
In the WatchKit extension you will want to use the WatchConnectivity WCSession sendMessage APIs. In the extension check that the iOS app is reachable, and then send the message:
let session = WCSession.defaultSession();
if session.reachable {
let message = ["foo":"bar"]
session.sendMessage(message, replyHandler: nil, errorHandler: { (error) -> Void in
print("send failed with error \(error)")
})
}
This message will cause the system to wake the iOS app in the background, so make sure to set up the WCSession in a piece of the iOS app code that gets called when running in the background (as an example: you don't want to put it in a UIViewController's subclass's viewDidLoad) so that the message is received. Since you will be requesting some information you might want to take advantage of the reply block.
So this is how you accomplish launching the iOS app in the background, though the WatchConnectivity WWDC session recommended trying to use the "background" transfer methods if possible. If your watch app is read-only then you should be able to queue up any changes on the iOS device using the background transfers and then they will be delivered to the watch app next time it runs.