Problem
I need to get a callback when at least X amount of time has passed since the date for the callback has been set.
Example 1:
This would have worked great, but it's possible to trigger an execution of the block by setting the date earlier than the correct time right now:
let responseDate = Date().advanced(by: 60) // 1 min
OperationQueue.current.schedule(after: .init(responseDate), {
print("the time is now!") // possible to set the current date 1 min before
}
On the other hand, the solution for getting a current uptime from this answer works great, but it requires timer constantly running to check if we're close to date.
Is it possible to combine these two approaches and somehow "attach" a callback to KERN_BOOTTIME, so that the OS will call my method when the boottime reaches a certain value?
I'm looking as well to alternative engineering solutions that satisfy two criterias:
It should not be possible to trigger the callback by resetting the device date to some arbitrary value in the past
If the device has been put to sleep (e.g. by pressing the on/off side button), the clock should still be "ticking", so that the method will be called back while the app is running in the background.
More details:
Backgrounding / app termination is out of scope
The main point is to prevent a bypass by switching the date backwards in the settings.
Related
I have a rule chain in ThingsBoard that does a Create Alarm when temperature is outside threshold and does a Clear Alarm otherwise. I receive a message using a Telegram bot when these events occur. That all works fine.
However when the temperature is hovering around the threshold, I can get many notifications as it goes in and out of the threshold temperature. That is somewhat annoying.
I would like to have the Clear Alarm activity only trigger if it is more than 5 minutes (say) since the last Create Alarm event was triggered.
Any tips on how to achieve this?
I finally worked out how to do this.
I added some server attributes to my device that define the temperatures that trigger alarms. I have a rule chain for controlling these alarms with the following nodes:
Enrichment - originator attributes to add the relevant attributes into the metadata associated with this message
Filter - script to detect if the temperature is outside the expected range
Filter - script to detect if the delay period has expired since the last time the alarm was triggered
Action - create alarm when script detects that temp is out of range
Action - clear alarm when script detects that delay period has expired
Transformation - script to update last alarm time attribute
Action - save attributes to persist updated alarm time attribute
Transformation - script to create a message about alarm set or cleared
Rule chain to handle sending the message to a Telegram bot
As an example, here is the script for checking if the delay period has expired before clearing the alarm:
var alarmTime = Number(metadata.ss_lastWaterTempAlarmTime);
var alarmDelay = Number(metadata.ss_clearAlarmTimeDelay);
return metadata.ts >= alarmDelay + alarmTime;
ss is the prefix added for server side attributes that have been added to metadata.
You can see the complete rule chain json in my Aquamon repo.
I am nearly new to swift Xcode and I am building an app, when the end user is near a iBeacon hi will get a local push notification.The problem I have is each time he comes near to it(if he got back and forward he will get each time he is near).
So I think to limit by time like 5 minuets of some like that.
I can not find in Swift how to limit a function to run in a time limit.(like 5 minutes)
Can some one point me in the correct direction?
Thanks for the help.
I did try to work with a timer but it did not do the job for me.
You Can create a Variable: beaconsHasBeenRecognized to turn to true when the beacon has been recognized, then the next time user goes back and forth, before triggering notification, your code should evaluate it beaconsHasBeenRecognized,its false, otherwise, if it is true, it will not trigger the notification.
Then with the timer, at the moment you set beaconsHasBeenRecognized to true, you start a timer to change beaconsHasBeenRecognized to false within the time that you want, like the 5 minutes.
In my app using iOS 9.2, Swift 2.1 I need to save some data into core data when the app goes to background. For this I registered each of the view controllers in the call path for UIApplicationDidEnterBackgroundNotification notification, with an instance method each for saving respective data.
I read on multiple places that by default the app gets about 5 seconds to finish off the execution and hence we need to use beginBackgroundTaskWithExpirationHandler to extend it to about 5 minutes. Following is an example of the selector method that responds to the above notification.
func applicationEntersBackground()
{
print("Before Extension: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
let taskID = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
print("During Extension: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
saveCoreData()
if(taskID != UIBackgroundTaskInvalid)
{
UIApplication.sharedApplication().endBackgroundTask(taskID)
}
print("After Extension: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
}
Following is the results of print() statements
Before Extension: 179.933103708318
During Extension: 179.930266333336
After Extension: 179.922843541659
My doubts are
Why is the remaining time about 180 seconds even before I requested for time extension? I tried multiple times. It is always close to 180 seconds and not the 5 seconds as suggested.
Why doesn't the call to beginBackgroundTaskWithExpirationHandler have any impact on the remaining time?
Once the applicationEntersBackground method of a VC returns, similar notification is sent to another VC's corresponding method. Suppose 180 seconds is the total extended duration and VC1 spends about 10 seconds on notification handling, does VC2 notification handler get around 170 seconds between its beginBackgroundTaskWithExpirationHandler - endBackgroundTask calls?
Between successive invocations of the notification handlers of different VCs, there is obviously a very short period where the extension request is not active. How does the timing play out in this case? Does the 5 second counter (provided it is true) come back to life as soon as an endBackgroundTask call is made, and possibly terminate the application before the next VC can get its notification?
Appreciate any help.
By looking at the documentation for backgroundTimeRemaining:
While the app is running in the foreground, the value in this property remains suitably large. If the app starts one or more long-running tasks using the beginBackgroundTaskWithExpirationHandler: method and then transitions to the background, the value of this property is adjusted to reflect the amount of time the app has left to run.
To answer your questions:
backgroundTimeRemaining stays around 180 while the application is in foreground so you can tell what time you'd have once you start a background task. This value is not an indicator of how long are you allowed to run without a background task.
beginBackgroundTaskWithExpirationHandler has an impact, as you can see, the remaining time decreased (by a small value as the method doesn't take much time)
What matters here is the time passed between the call to beginBackgroundTaskWithExpirationHandler and the one to endBackgroundTask. You can split whatever you need the time interval between your calls, providing you don't exceed the 180s limit
Once you call endBackgroundTask the application will be suspended, regardless it took 2 seconds or 179 seconds.
You can find out more details about application entering background here. I'd recommend going through the documentation, it might clarify other questions you might have on this matter.
NSRunLoop has two apis:
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate
and
- (void)acceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate
I mean they are same except return value, or there are other differences?
As #DarkDust hints at, it used to have to do with timers. See Chris Kane's discussions on the Cocoa mailing list. At one point, runMode:beforeDate: was a wrapper around limitDateForMode: and acceptInputForMode:beforeDate: (since acceptInputForMode:beforeDate: didn't fire timers). My reading of the docs, headers, and crash stacks suggests that today, they behave identically (calling CFRunLoopRunInMode(), which does fire timers). But I haven't built a test app to confirm.
If you read the original NeXT ObjC manual, acceptInputForMode:beforeDate: used to explicitly not fire timers:
Blocks awaiting input from the ports in the port list for the input mode mode until the time specified by limitDate. Use the limitDateForMode: method to calculate limitDate. If input arrives, it is processed using the NSPort delegates. This method does not check the timers associated with mode, thus it does not fire timers even if their scheduled fire dates have passed.
Timers were explicitly handled as a side effect of limitDateForMode:
Polls mode's input sources for their limit date (if any) and returns the earliest limit date for this mode. Uses the NSPort delegate method limitDateForMode: to determine the limit dates of ports. Fires timers if their limit dates have passed. Polls ports for activities appropriate for mode. Returns nil if there are no input sources for this mode.
Which is why runMode:beforeDate: was added as a convenience (see NSRunloop.h):
#interface NSRunLoop (NSRunLoopConveniences)
- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
- (void)configureAsServer NS_DEPRECATED(10_0, 10_5, 2_0, 2_0);
#endif
See also from NeXT:
The method limitDateForMode: returns the earliest limit date of all the input sources for the mode NSDefaultRunLoopMode. acceptInputForMode:beforeDate: runs the loop until that date, processing any input it receives until that time. As a convenience, you can use runMode:beforeDate: instead. It invokes acceptInputForMode:beforeDate: and limitDateForMode: with the mode you supply.
So the short answer: history.
Is it possible to execute code when a specific time is reached on an iPhone's clock. Would this be able to work after the iPhone is closed (the screen goes black) but the app was left running (the user did not return to the home screen).
For example, would it be possible for a method to be programmed to go off at 1:30:15 PM with no relation to the current time, and are there any restrictions depending on whether the phone is closed or if the app is running in the background?
I found a similar post here How to generate event on a specific time of clock in C#? but this generates a timer based on the current time to run a method later, instead of using the clock without relation to the current time.
I have created a poor way of doing this-
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
NSLog(#"TESTSTART - TEST START");
// Check
if(timeinfo->tm_hour == 12 && timeinfo->tm_min == 01 && timeinfo->tm_sec == 48)
{
NSLog(#"Run Method");
printf("the time is 12:01:48");
} else {
NSLog(#"ELSE");
[self syncTest];
}
}
This would run a method at 12:1:48, but it is most likely not an acceptable way of doing this. Does anyone know of any better ways to do this and how much strain this way puts on the cpu? Thanks
Use an NSTimer and set the fireDate for whichever date you want the timer to fire on and then catch the notification that it has fired and perform you task.