Activate Application on Screen Unlock - swift

I'm writing an application for macOS and I want it to detect when the screen is unlocked and then make itself become the active application.
I'm trying to use "com.apple.screenIsUnlocked", but it doesn't seem to work (the function doesn't even run). I also tried using NSWorkspaceDidWakeNotification where I got the function to run but the app didn't actually activate (presumably because the screen was still locked).
Here is what I currently have (I'm using Xcode 9.2 and Swift 4):
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(AppDelegate.screenDidUnlock), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
#objc func screenDidUnlock() {
NSApplication.shared.activate(ignoringOtherApps: true)
print("Did Run")
}

The com.apple.screenIsUnlocked notification is posted to the DistributedNotificationCenter rather than the NSWorkspace's notificationCenter, so the observer should be added like this:
DistributedNotificationCenter.default().addObserver(self, selector: #selector(AppDelegate.screenDidUnlock), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil)enter code here

I don't think apple allows you to run any application in the background.

Related

Swift Macos GameController not showing. Controller.count return zero

I'm developing an app which includes Gamecontroller. I'm trying to show the connected controller count, but it´s not working.
Here is my simple code:
import Cocoa
import GameController
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
// - MARK: Controllers
let controllers = GCController.controllers()
print(controllers.count)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
it always prints 0
What am I doing wrong?
EDIT:
The problem was i mised to add:
let ctr = NotificationCenter.default
ctr.removeObserver(self, name: .GCControllerDidConnect, object: nil)
ctr.removeObserver(self, name: .GCControllerDidDisconnect, object: nil)
To handle when a controller is connected
Fixed using this tutorial: tvOS Games, Part 1: Using the Game Controller Framework
The next problem is to find a unique identifier for the device...
EDIT:
The problem was i mised to add:
let ctr = NotificationCenter.default
ctr.removeObserver(self, name: .GCControllerDidConnect, object: nil)
ctr.removeObserver(self, name: .GCControllerDidDisconnect, object: nil)
To handle when a controller is connected

Can you do test setup before application loads in Swift?

I am a Swift/MacOS newbie working on a MacOS application does some setup involving keychain access and API calls in the viewDidLoad() methods in the main ViewController.
I am working on unit tests for my models, so I don't need and in fact do not want the code in viewDidLoad() to run. However, from what I can tell, the app gets loaded and those methods run before the test case setup() method, so I don't know how I could do any mocking or other actions.
I am using Xcode 11.5 and Swift 5.
One way to do it would be to have an separate NSApplicationMain for when unit tests are run vs one for "normal" runs.
First remove the #NSApplicationMain annotation from your current AppDelegate class. It should end up looking something like this:
AppDelegate.swift
import AppKit
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
print("Debug/Production run")
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
Now create a new file called AppDelegateUnitTesting.swift and it's source should look like this:
AppDelegateUnitTesting.swift
import Foundation
import Cocoa
class AppDelegateTesting: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
print("Unit Testing Run")
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
Now add a new file called main.swift this file will determine in which environment our app is running, the source should be something like this:
main.swift
import Foundation
import Cocoa
let isRunningTests = NSClassFromString("XCTestCase") != nil &&
ProcessInfo.processInfo.arguments.contains("-XCUnitTests")
fileprivate var delegate: NSApplicationDelegate?
if !isRunningTests {
delegate = AppDelegate()
NSApplication.shared.delegate = delegate
// See this Answer to initialize the Windows programmatically
// https://stackoverflow.com/a/44604229/496351
} else {
delegate = AppDelegateTesting()
NSApplication.shared.delegate = delegate
}
NSApplication.shared.run()
To determine whether it's running in a Unit Test environment it checks if it can load the XCTestClass (which is only injected when testing) and it checks for the presence of the -XCUnitTest command line argument, we have to set this argument ourselves as part of the Scheme's Test action as shown in the image below
After doing all of this, you should see the message "Debug/Production run" printed when you press the play button and you should see the message "Unit Testing Run" printed whenever you run your unit tests.
You'll most likely have to add code to load the initial window programmatically this other answer shows how to do it:
how to Load initial window controller from storyboard?

Preventing macOS App Nap in Swift 4

I'm using Swift 4 and Xcode 9.2 to build a timer app and I'm running into issues with macOS App Nap. I know that the issues are caused by App Nap since when I disable App Nap globally the issues go away. However I only want to disable App Nap for this particular app when I need to for it to run properly. I know that similar questions have been answered before and I have seen them, but either I don't understand how to use them in my case or they do not work anymore (possibly because of changes to the Swift language).
Here is a small example of code that produces the issues I'm having:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var timer = Timer()
var seconds = 300
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
#IBAction func goButton(_ sender: NSButton) {
if !timer.isValid {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.ticker), userInfo: nil, repeats: true)
}
}
#objc func ticker() {
seconds -= 1
if seconds == 0 {
timer.invalidate()
print("Timer Ended.")
}
}
}
Any help is greatly appreciated.

AppDelegate.swift crashes macOS App

When I try to launch my app, it simply crashes. The error it gives is unrecognized selector sent to instance 0x608000000ac0 and the error shows up at my AppDelegate.swift file on line 4. Here is the full AppDelegate.swift file:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { //This is where I get the error
#IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
How do I fix this error? Thanks in advance,
Aaronjam
After trying to fix the problem for over an hour, I deleted my target (after backing up my code, of course) and made a new one. Thanks for your help, #OverD!

applicationWillHide not called

I've implemented four functions at my appDelegate:
func applicationWillHide(_ notification: Notification)
func applicationWillUnhide(_ notification: Notification)
func applicationWillResignActive(_ notification: Notification)
func applicationDidBecomeActive(_ notification: Notification)
I tried to hide the app/minimize it, but none of them is getting called.
I also tried to add a observer for the notification - don't think it's needed but tried it anyway - so for example this is one of them:
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.applicationWillUnhide(_:)), name: NSNotification.Name.NSApplicationWillUnhide, object: nil)
but still nothing.
All of this is done at the appDelegate.swift file.
(maybe needless to say, but the function applicationDidFinishLaunching which is also declared at NSApplicationDelegate is getting called)
Anyone has a clue on this?
While cleaning the project was helpful, only applicationWillResignActive and applicationDidBecomeActive were getting called.
So I ended up implementing NSWindowController that comfort to NSWindowDelegate, and there I have windowWillMiniaturize, windowWillClose and windowDidDeminiaturize