OS X agent app with NSTimer in the background is not working after deep sleep - cocoa

I have a OS X agent app (which only runs from the icon in the menu bar). My app creates a NSTimer with random intervals to play a sound.
func setNewTimer(timeInterval: NSTimeInterval) {
self.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: "playSound", userInfo: nil, repeats: false)
NSRunLoop.currentRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
NSLog("Timer created for interval: \(timeInterval)")
}
The app works all fine after I start it and keep doing other work in other apps. It plays the sound at random times as expected.
If the computer goes to sleep for a short period and comes back the app will keep playing sounds at random times as expected.
However, if my computer goes to sleep for a long time (e.g. throughout the night), the app will not play sounds anymore.
It may be possible that the problem is that the timer may be disabled if the computer goes to deep sleep? or (preferably) Is there a way to detect that the computer awoke from sleep so I can reset my timer?
Note: Every time I call this function I first self.timer.invalidate() and recalculate the timeInterval. At sleep time (e.g. 23:00 to 08:00 ) the timer will not run, but instead will create an interval from 23:00 to 08:00 so that it 'fires' the next day in the morning.

I've came up with a solution myself after I found that there was no reply for some time. The solution was pretty simple as I only needed to register for sleep and wake notifications (I added the code updated to Swift 3):
// App should get notifified when it goes to sleep
func receiveSleepNotification(_ notification: Notification) {
NSLog("Sleep nottification received: \(notification.name)")
// do invalidation work
}
/// App should get notified when the PC wakes up from sleep
func receiveWakeNotification(_ notification: Notification) {
NSLog("Wake nottification received: \(notification.name)")
// Reset/Restart tasks
}
func registerForNotitications() {
//These notifications are filed on NSWorkspace's notification center, not the default
// notification center. You will not receive sleep/wake notifications if you file
//with the default notification center.
NSWorkspace.shared().notificationCenter.addObserver(self, selector: #selector(AppDelegate.receiveSleepNotification(_:)), name: NSNotification.Name.NSWorkspaceWillSleep, object: nil)
NSWorkspace.shared().notificationCenter.addObserver(self, selector: #selector(AppDelegate.receiveWakeNotification(_:)), name: NSNotification.Name.NSWorkspaceDidWake, object: nil)
}
func deRegisterFromNotifications() {
NSWorkspace.shared().notificationCenter.removeObserver(self)
}

Related

iOS15 / iPhone12 setContentOffset animate Issue

It occurs on iOS15/iPhone12 series of devices.
Multiple CollectionViews are paged with each timer, but paging does not work normally only on iOS15/iPhone12 devices.
The paging is stuttering and the animation is not working normally.
I tried implementing a timer using Rx, but the symptoms are the same.
It operates normally on other devices, other iOS versions.
Has anyone experienced the same issue as me?
// CollectionView has the following timer code and performs timer operation independently.
// each CollectionView's paging interval is 1.0 seconds 1.3 seconds and 1.6 seconds.
func startTimer(interval: CGFloat) {
let timer1 = Timer.scheduledTimer(timeInterval: interval,
target: self,
selector: #selector(rolling1),
userInfo: nil,
repeats: true)
}
#objc func rolling1() {
DispatchQueue.main.async {
self.offset1 = CGPoint.init(x: self.offset1.x + UIScreen.main.bounds.width, y: self.offset1.y)
self.collectionView?.setContentOffset(self.offset1, animated: true)
}
}
The motion screen is as follows.
It works normally on all iOS 14 devices.
It also works normally on iOS15 devices except iPhone 12 series.
There is only a problem with iPhone 12 series on iOS15.
It is fixed in iOS 15.1 Thank for Apple..

macOS Key Event Slow Repeat

I'm trying to create a small WASD demo game in macOS. I'm using NSEvent for handling the key events. To detect the key presses, I'm searching for keyDown events. Here's what I have:
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
(keyEvent) -> NSEvent? in
if self.keyDown(with: keyEvent) {
return nil
} else {
return keyEvent
}
}
func keyDown(with event: NSEvent) -> Bool {
userInt.keyDown(key: event.characters)
return true
}
So here, I'm holding the keys down (as you'd expect in a game), and I'm getting some very slow movement. Like, when I'm holding it down, it's very janky. Upon further inspection, I saw that the key repeat interval was 0.1s, which was set in my system preferences. This means that it's skipping frames. However, in a game, I don't want this setting to affect the movement. So how can I detect a key holding event without being held up by the key repeat interval?
You should ignore key-repeat events (with isARepeat true). Instead, when you get a key-down event, start a timer that fires however often you want to advance your game state. Advance the game state in that timer's firing code. When you get a key-up event, stop the timer.

Swift: Keep NSTimer Running While Computer is Asleep (OSX)

I am writing an OS X app that includes a generic stopwatch timer. I'm using NSTimer. I'd like the user to be able to start the timer and come back to it after a long time (say, 30 minutes), and the timer would still be running. The problem is that my timer does not continue running while the computer is closed or asleep, and I don't want to keep my computer open and on for really long periods of time. There are several threads about this problem concerning iOS apps, but none (at least that I've found) pertaining to OS X. Does anyone know of a workaround for this issue? As an example, I'm trying to mimic the "Stopwatch" functionality of the "Clock" app that comes with iOS, except with a laptop instead of a phone. The stopwatch in the "clock" app will continue running even when the phone is off for extended periods of time.
The way I figured out to do this was not to actually run the NSTimer in the background, but rather to find out how much time had elapsed between when the app goes into the background and when it comes back into focus. Using the delegate methods applicationWillResignActive: and applicationWillBecomeActive: of NSApplicationDelegate:
let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(self), userInfo: nil, repeats: true)
var resignDate: NSDate?
var stopwatch = 0
func update() {
stopwatch += 1
}
func applicationWillResignActive(notification: NSNotification) {
timer.invalidate()
resignDate = NSDate() // save current time
}
func applicationWillBecomeActive(notification: NSNotification) {
if resignDate != nil {
let timeSinceResign = NSDate().timeIntervalSinceDate(resignDate!))
stopwatch += Int(timeSinceResign)
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(self), userInfo: nil, repeats: true)
resignDate = nil
}
}
applicationWillResignActive: will get called every time the app goes out of focus. When this happens, I save the current date (NSDate()) in a variable called resignDate. Then, when the application is reactivated (after who knows how long; it doesn't matter) applicationWillBecomeActive: is called. Then, I take another NSDate value that is the current time, and I find the amount of time between the current time and resignDate. After adding this amount of time to my time value, I can revalidate and the NSTimer so it keeps going.

XCTest sleep() function?

I am testing an app which samples data. Part of the test I am setting up requires a few data points to be stored. I would like to do this by having XCTest execute the acquisition method followed by a sleep() function and yet another call to the acquisition method.
Though there are methods to wait for an expectation with timeout, there doesn't seem to be a simple wait()/sleep() method that simply pauses execution for specified amount of time. Any idea how I can do this using Xcode 6 and Swift?
You can use NSTimer to space out your data calls instead of locking up the app with sleep
func dataCall(timer : NSTimer) {
// get data
}
let myTimer : NSTimer = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("dataCall:"), userInfo: nil, repeats: false)
and of course you can alter those parameters to your liking and needs.

Using NSURLSession from a Swift command line program

I'm trying to test a little proof-of-concept command line app prior to integrating it into a larger app. What I'm trying to do is download some data using NSURLSession using this example. However it appears that if I use the examples given in a simple OS X command line app then the app exits prior to the data being retrieved.
How can I download data from a stand-alone command line app using NSURLSession? What I've read about is using NSRunLoop however I've not yet found a clear example in Swift so if NSRunLoop is actually the way to go then any examples would be appreciated.
Any other strategies for downloading data from a URL for a Swift command line app is also welcome (infinite while loop?).
You can use a semaphore to block the current thread and wait for your URL session to finish.
Create the semaphore, kick off your URL session, then wait on the semaphore. From your URL session completion callback, signal the semaphore.
You could use a global flag (declare a volatile boolean variable) and poll that from a while loop, but that is less optimal. For one thing, you're burning CPU cycles unnecessarily.
Here's a quick example I did using a playground:
import Foundation
var sema = DispatchSemaphore( value: 0 )
class Delegate : NSObject, URLSessionDataDelegate
{
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
{
print("got data \(String(data: data, encoding: .utf8 ) ?? "<empty>")");
sema.signal()
}
}
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: Delegate(), delegateQueue: nil )
guard let url = URL( string:"http://apple.com" ) else { fatalError("Could not create URL object") }
session.dataTask( with: url ).resume()
sema.wait()
Try this
let sema = DispatchSemaphore( value: 0)
let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg")!;
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
print("after image is downloaded");
sema.signal(); // signals the process to continue
};
task.resume();
sema.wait(); // sets the process to wait
For proof of concept(s) or tryouts/testing purposes, you can simplify asynchronous complexity by hard coding some timeout period until your stuff finishes. (see notes below)
SWIFT 5
//...your magic here
// add a little 🤓iness to make it fun at least...
RunLoop.main.run(until: Date() + 0x10) //oh boi, default init && hex craze 🤗
// yeah, 16 seconds timeout
// or even worse (!)
RunLoop.main.run(until: .distantFuture)
SWIFT 3 or earlier
//...your stuff here
RunLoop.main.run(until: Date(timeIntervalSinceNow: 15)) //will execute things on main loop for 15 seconds
NOTES :
DO NOT USE THIS IN PRODUCTION
respect the first rule
This is very quick and dirty way to overcome serious concerns of parallelism. Explore better and more complex solutions described in other answers of this question.

Resources