Run a task at fixed time everyday - ios

I would like to run a function (make an api call) at fixed time everyday, say 10 am and 10 pm daily. What would be the cronjob equivalent in swift?
I tried implementing Timer as:
var dateComponents = DateComponents()
dateComponents.timeZone = TimeZone(abbreviation: "NPT")
dateComponents.hour = 10
dateComponents.minute = 00
let userCalendar = Calendar.current
let myTime = userCalendar.date(from: dateComponents)
let timer = Timer(fireAt: myTime!, interval: 0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
The selector function:
#objc func updateTimer() {
print("Hello")
}
Instead of being run at the specified time, the selector function gets executed everytime I run the app and not at the specified time. How can I solve this?
Edit: I’ll be needing this to be run from the background as well. I’ll be using location service in my app.

There is no way to achieve your goals without using push notifications. Timer's are using run loops and hence aren't working in the background. There's no background mode for making API calls at regular intervals either.
The only option is to send out push notifications from your server at the specified times every day and in response to receiving the push notification in your app, make the API call. Of course you'll need internet connection for push notifications to work, but since you want to make API calls, this won't make a difference as you'd need internet connection for the API calls to succeed anyways.

Related

How to limit number of requests to a database

I have an app with a settings page where the settings of each user are stored in a MySQL database. I was wondering what is the best way to update the database for every setting the user changes while sending the minimal number of requests as I'm worried that it will crash if it sends too many( it has happened before).
I was thinking about setting a timer for ~5 seconds when the user first changes a setting, and then reset the timer to 5 seconds again if another setting is changed. Once that timer is finished it will send a request to the server to update all the settings at once. It would also constantly store the new values locally to the app, so if the user closes the app before the 5 seconds are up it will send the request once/if the app loads up again.
Is this viable/what's the best way to go about this?
You need to make some logic functions in your app, so i will try make an pseudo codes below. Hope it will give you an idea. I don`t know the MySQL details but i am trying to explain native Swift way.
First of all you should fetch data partly, I mean if you try to fetch all data at the same time your app can work very slow.. That is why we are doing pagination in the app.
As well as pagination you want to refresh the data fresh 5 seconds so i will use Timer object, which will trigger every 5 seconds the API function and catch data based on pagination. Check your below codes and implement step by step to your project. Happy Coding.
var timer: Timer?
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "loadNewDataAutomatically" with the interval of 5 seconds
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.loadNewDataAutomatically), userInfo: nil, repeats: true)
}
#objc func loadNewDataAutomatically(_ pageNumber: Int, _ pageSize: Int, onSuccess: ((Bool) -> Void)?, onError: ((Error) -> Void)?){
// Call your api here
// Send true in onSuccess in case new data exists, sending false will disable pagination
// If page number is first, reset the list
if pageNumber == 1 { self.list = [YourDataModel]() }
// else append the data to list
self.list.append(apiResponseList)
// If Api responds with error
onError?(apiError)
// Else end success with flag true if more data available
let moreDataAvailable = !apiResponseList.isEmpty
onSuccess?(moreDataAvailable)
}
Assuming that the Database (MySQL) is on a server
You can try using WorkManager for this requirement.
When the user changes their settings, save them locally (which you are already doing)
enqueue a Unique Periodic Work Request using WorkManager & set up the time at what interval should the response be sent to the server.
Minimum time interval is around 15 min. but not guaranteed at exactly 15 minutes,
the system fires it when it seems fit, also according to the Constraints that you set on your Work Request.
You can also use a OneTimeWorkRequest, if you don't need periodic, with an Initial Delay of whatever time you need.
Edit: This question was later edited and ios, swift tags were added where previously it was for android.
If anyone comes here searching for something similar for Android, this would work.

Question on how to proceed with UNUserNotificationCenter 64 limit

I am trying to make a reminder app and all my notification repeat is set to true
Example :
var dateComponents = DateComponents()
dateComponents.weekday = 1
dateComponents.hour = Int(hour)
dateComponents.minute = Int(minute)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let requestIdentifier = "\(timeArrayToSaveInMobile[indexTime].alarmId!)Sunday"
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
I've browse around here and some people mentioned that the system keeps the soonest-firing 64 notifications.
So, if I have already reached the limit but I set another notification that will fire earlier than some of the notification in the (64 list) it should replace one of the notification right? Since it will fire earlier that some of the pre-set notification in the list.
My problem is similar to this ios 10 swift 3 add more than 64 local notifications does not keep soonest
Notifications will reset when you open the app, thus you can set/send another 64 after every time you close the application.
The system discards scheduled notifications in excess of this limit, keeping only the 64 notifications that will fire the soonest. Recurring notifications are treated as a single notification.
in your example you have set one notification for Sunday which is only count one for each Sunday.

How can I run a method after n minutes I received a geofence event in iOS?

I'm working on a Geofence based iOS application and I would to know when the user stays more than 5 minutes inside a place.
Now, the geofence part is already done and working, I get the "enter" and "exit" events, but I want to execute some methods 5 minutes after I entered in a geofenced area, if I don't left it.
The main problem here is that NSTimers will not work with the app closed and I don't know how to focus this.
Any ideas?
Thank you for your time!
P.S: CLVisit class is not valid on this case because the events for this class are not "in real time" and we cannot set a time-inside.
You can use NSTimer when your app is Active like this :
let timer = NSTimer(timeInterval: 1.0, target: self, selector: #selector(self.printStr), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
and also if your app did receive UIApplicationWillTerminateNotification event you can save the current date to e.g userDefaults and use it after the user run the application again. Another way is to use UILocalNotification after you receive AppWillTerminate event, you should schedule the UILocalNotificaiton and trigger it after 5 minutes.
Hope it helps you

Local Notification Not Firing

I'm trying to use local notification, this is my code:
appdelegate
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [UIUserNotificationType.Sound, UIUserNotificationType.Alert, UIUserNotificationType.Badge], categories: nil))
notificationViewController
let localNotification:UILocalNotification = UILocalNotification()
var BDate = friend.birthday.componentsSeparatedByString("/")
let date = NSDate.date(year: 2015, month: Int(BDate[1])!, day: Int(BDate[0])! - daysBefore, hour: hour, minute: min, second: 0)
localNotification.soundName = "notificationSound.mp3"
localNotification.alertBody = friend.fullName + " has a birthday today!"
localNotification.fireDate = date
localNotification.timeZone = NSTimeZone.localTimeZone()
localNotification.repeatInterval = NSCalendarUnit.Year
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
friend.birthday is a string - "DD/MM/YYYY"
I'm calling the setNotification function for every friend in a friends array. When there are only one or two friends I get the notification but one there are ~100+ I no longer get the notification.
I know the fireDate is correct, I checked it.
Why the code isn't working?
Each app on a device is limited to 64 scheduled local notifications. The system discards scheduled notifications in excess of this limit, keeping only the 64 notifications that will fire the soonest. Recurring notifications are treated as a single notification.
You can find more detail here
Looks like you're really exceeded the notifications limit. There's several tips and guidelines that might help:
Try to check your date versus NSDate() and schedule notification only if date.timeIntervalSinceReferenceDate > NSDate().timeIntervalSinceReferenceDate (There is a compare method in NSDate, but I prefer to compare raw values)
Scheduling local notifications one-by-one is very expensive for application performance, so you can form an array of notifications and schedule them all at once. Use UIApplication.sharedApplication().scheduledLocalNotifications property for that purpose.
This will allow you effectively reschedule all notifications on every application launch. For example in applicationDidFinishLaunching() delegate method.
And also, this will give you another advantage: you can easily check for notifications limit and, if necessary, add/replace 64-th notification with prompt to user to launch your app to allow it to schedule more notifications. This is a common practice for many applications to deal with Apple's limitations.

Local Notification every 24 hours in Swift

I'm looking for a way to fire a local notification once every 24 hours from a certain time.
For example:
I have a date picker.
I set the date picker time to 3.02pm.
The next time it's 3.02pm fire a local notification.
Repeat this over and over.
I have managed to successfully call a local notification at a time chosen by a date picker but how do I go about repeating this every 24 hours?
You can repeat a local notification using the repeatInterval property.
To repeat a local notification every day, add this line to your code
myLocalNotification.repeatInterval = NSCalendarUnit.CalendarUnitDay
UILocalNotification is deprecated in iOS 10. Use UNNotificationRequest instead.
myNotificationRequest.trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60*60*24, repeats: false)
In Swift 3
myLocalNotification.repeatInterval = NSCalendar.Unit.day

Resources