Show Local Notifications When App is Killed or in Background - ios

I have an HQ Answer bot that is in the form of an iOS app. Basically, I have the entire bot completed and it runs once the HQ Trivia websocket opens, however I am having issues with the last step, which is to send the best answer from the bot to the user in the form of a push notification, since they will be using the HQ app when the game is live and the bot is running. In my viewDidLoad I have the following code:
override func viewDidLoad() {
super.viewDidLoad()
getSocketURL()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
}
getSocketURL() checks to see if the game is live and if so it runs the bot, however I want this to operate when the user has the app killed or it is in the background.
Finally here is where the notification should be sent locally when the bot finds the correct answer:
private func getMatches()
{
let options = [answer1, answer2, answer3]
AnswerController.answer(for: question, answers: options) { answer in
print("Predicted correct answer: \(answer.correctAnswer)")
print("-----")
self.bestAnswer = answer.correctAnswer
self.sendNotification(question: self.question, answer: "Best Answer: \(self.bestAnswer)") // Send the user a notification
self.updateLabels()
}
}
To sum up, I am looking for a way that will let the bot run whenever the game is live without the user having the app open and in the foreground, since they will be in the HQ app. Is there any way to do this with local notifications and if no what steps would I have to take to do something like this using something like Firebase, that is if I still want the bot to run locally on the user's phone.

Related

Swift - register and re-register push notifications

Im trying to find a way to de-authorise users for push notifications when they log out from an app Ive found this function UIApplication.shared.unregisterForRemoteNotifications() which aparently works however I never see notifications being disabled, I also read in the documentation that it should not be used often or something to that effect, I basically want to have a toggle button in my app where the user clicks it one way and gets the standard enable notifications popup and another way to disable notifications on the fly, Im not a native swift developer so any pointers welcome
Also is it possible to attach a callback to this to know if it executes successfully, Im trying the following but get the error Argument passed to call that takes no arguments
UIApplication.shared.unregisterForRemoteNotifications() { (result, error) in
if let error = error {
call.error("Error", error)
} else if let result = result {
call.success([
"deregister": true
])
}
}
Edit: I found this which says its not possible to toggle on and off Change push notifications programmatically in Swift
With that in mind does this mean that the standard for devices is that:
1) when a user log out of their account they can still receive notifications.
2) When a user creates a new account on the same app it uses the same token and so receives notifications from the old account ?
3) when a user sells their phone and another guy/gal downloads the same app that they will receive notifications from that other users account (in terms of 3rd party push service one signal, aparently you dont need to refresh the player id)

How to detect if user navigates away from my app in iOS?

I am trying to develop an app which helps people focus. Essentially, users start a session in which they would like to focus. This session lasts as long as the user does not open another app (for example, the user cannot open Facebook). If the user does so, the session is marked as a failure. Users try to last as long as possible.
The issue I see is that iOS does not allow apps to run for more than 3 minutes once the screen turns off. That means that a user can start a session, then put the phone down for 3 or more minutes, and when they unlock the phone, they are presented with their home screen. The app has been killed. A user is then free to roam around and look at any other app, defeating the purpose of my app.
I could use a timer running on a server to maintain overall session duration, and resume the session once the user reopens the app, but this does not solve the issue of users being able to roam before reopening the app.
Are there any ways to get around iOS killing my app? The behavior I want is:
1) User starts session.
2) Session timer begins.
3) If user navigates away from app, session terminates after 30 second warning.
4) If user locks phone, app still runs.
5) When user unlocks phone, app is displayed (not homescreen). Thus, the termination logic is the same.
Thanks!
You can use an observer (add it to your ViewController's viewDidLoad):
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive(notification:)), name: UIApplication.willResignActiveNotification, object: nil)
And the function will look like this:
#objc func applicationWillResignActive(notification: NSNotification) {
// do something
}

Determine when user returns from deep-linked iOS Settings app?

I use deep-linking to let my users navigate to my app's page in the iOS Settings app, where I allow the user to set how many Core Data backups they want to store.
Settings is kind enough to provide a link back to the app, which is awesome, but I'd like to know when the user comes back specifically from Settings so that I can then prune the Core Data backups.
Is there a notification I can observe, or some other way I can tell when the app comes to the foreground specifically after leaving Settings?
I'm programming in Swift 4.2. Thanks!
As suggested by #mschmidt, the answer was simple; I just needed to register an observer for UserDefaults.didChangeNotification. Something like the following:
NotificationCenter.default.addObserver(
self,
selector: #selector(userDefaultsDidChange),
name: UserDefaults.didChangeNotification,
object: nil
)
#objc private func userDefaultsDidChange() {
coreData.pruneBackups()
}

EAAccessoryDidConnectNotification doesn't fire

I'm trying to learn about CoreBluetooth and External Accessories on iOS.
First, I tried to see a list of devices connect to my phone via Bluetooth via print(EAAccessoryManager.sharedAccessoryManager().connectedAccessories) ... despite having 3 devices connected (according to the Settings app), I'm given an empty array.
Next, I tried registering for connect / disconnect notifications:
import UIKit
import ExternalAccessory
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "somethingConnected:",
name: EAAccessoryDidConnectNotification,
object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "somethingDisconnected:",
name: EAAccessoryDidDisconnectNotification,
object: nil)
EAAccessoryManager.sharedAccessoryManager().registerForLocalNotifications()
}
func somethingConnected(name: EAAccessory) {
print("here")
}
func somethingDisconnected(name: EAAccessory) {
print("there")
}
}
... I receive nothing when I turn off/on (and thus disconnect/connect) a simple Bluetooth speaker I have.
I am seeing this issue (notifications not delivering until after the completion block of showBluetoothAccessoryPickerWithNameFilter() executes), but, generally, it seems like either:
A) Something with iOS isn't working correctly
B) I'm doing something wrong (the more likely of the two).
Do I need to have a special MFI certificate installed to see a list of connected Accessories? Why aren't notifications delivering?
Any recommendations / code examples are greatly appreciated.
Update
Most importantly: Still don't know why connectedAccessories doesn't work, so advice on this piece is greatly desired.
That said, re-reading the Apple Developer documentation, I don't believe that it's correct / possible to use NSNotificationCenter.defaultCenter().addObserver with these types of notifications.
Specifically, the documentation states that EA notifications will not be delivered until showBluetoothAccessoryPickerWithNameFilter() is called -- e.g. the EAAccessoryDidConnectNotification and EAAccessoryDidDisconnectNotification are meant to inform the app about what the User did with the picker dialogue. It doesn't seem that they are system-level notifications that can be picked up by NSNotificationCenter.
Please correct me if this is an incorrect reading.
you should change
selector: "somethingConnected:"
into
selector: #selector(somethingConnected:)
,than it will fire.
For more details, please see the following website:
Why does EAAccessoryDidConnectNotification occur twice?

iOS - How can I schedule something once a day?

I know there is NSTimer.scheduledTimerWithInterval
Which is used like this:
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
// Something cool
}
But I assume that the view where var timer is living must be living too, ie: can't close that view (am I right?)
How can I schedule something once a day in iOS, eg: everyday at 8pm send a notification?
In Android I've achieved this the following way:
I've used a thing called AlarmManager, that is similar to the iOS scheduledTimerWithInterval but for large intervals, which is running inside a background service.
At the startup (boot), there is another background service that setups the alarm again. This covers the case when the Android device is shut down (and so the background service is shut down too)
So, in iOS, is there something like scheduledTimerWithInterval for large intervals?
Will I need to set again the interval if the iPhone/iPad is rebooted?
Yes, to use NSTimer, the app has to be running either in foreground or background. But Apple is quite particular only allowing certain types of apps to continue to run in the background (in an effort to make sure we don't have apps randomly running on their own prerogative and killing our batteries in the process and/or affecting our performance while using the device).
When you say "notification", do you really mean notifying the user of something?
In that case, the alternative here is to create a UILocalNotification, which is a user notification (assuming they've granted your app permission to perform notifications), which is presented even when your app is not running.
For example, to register for local notifications:
let application = UIApplication.sharedApplication()
let notificationTypes: UIUserNotificationType = .Badge | .Sound | .Alert
let notificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)
application.registerUserNotificationSettings(notificationSettings)
And then to schedule the repeating notification:
let notification = UILocalNotification()
notification.fireDate = ...
notification.alertTitle = ...
notification.alertBody = ...
notification.repeatInterval = .CalendarUnitDay
application.scheduleLocalNotification(notification)
For more information, see the Local and Remote Notification Programming Guide.
Or do you mean initiating some process, such as fetching data from a remote server.
If you want the app to fetch data even if your app isn't running, you can use background fetch. See Fetching Small Amounts of Content Opportunistically in the App Programming Guide for iOS.
Note, with background fetch, you don't specify when data is to be retrieved, but rather the system will check for data at a time of its own choosing. It reportedly factors in considerations ranging from how often the user uses the app, how often requests to see if there is data result in there actually being new data to retrieve, etc. You have no direct control over the timing of these background fetches.

Resources