I see pretty strange behaviour of NSRunningApplicationInstance.activate() method.
Let's imagine that I have found app named "Finder". This app at the moment is:
Not active and/or hidden
I have really simple code:
let activeOptions: NSApplication.ActivationOptions = [.activateAllWindows, .activateIgnoringOtherApps]
print("1. isActive: \(app.isActive); isHidden: \(app.isHidden)")
if (!app.isActive)
{
app.activate(options: activeOptions)
}
if ( app.isHidden )
{
app.unhide()
}
print("2. isActive: \(app.isActive); isHidden: \(app.isHidden)")
for finding needed app you can use the following code:
let app = NSWorkspace.shared.runningApplications.filter{ $0.localizedName = "NameOfApp"}
2 times runned code result:
isActive: false; isHidden: false
isActive: false; isHidden: false
isActive: true; isHidden: false
isActive: true; isHidden: false
If you will try it... :
Code is will show me menu of the app:
BUT only on second code run! (Why?)
Will not show me app window! (Why?)
And I see similar behaviour with a lot of applications, not only with Finder.
As example SourceTree app.
Can somebody explain the logic and how do display window of the ANY of runned app by some code IN ANY CASE?
Here is working Playground module. The approach is to use KVO for observable properties to be informed when exactly desired state for target application occurs. Hope it would be helpful somehow.
import Cocoa
class AppActivator: NSObject {
private var application: NSRunningApplication!
private let filterName: String
init(appName: String) {
filterName = appName
}
func activate() {
guard let app = NSWorkspace.shared.runningApplications.filter ({
return $0.localizedName == self.filterName || $0.bundleIdentifier?.contains(self.filterName) ?? false
}).first else {
print("Application \(self.filterName) not found")
return
}
guard app.activationPolicy != .prohibited else {
print("Application \(self.filterName) prohibits activation")
return
}
self.application = app
self.unhideAppIfNeeded()
self.activateAppIfNeeded()
}
private func unhideAppIfNeeded() {
if application.isHidden {
application.addObserver(self, forKeyPath: "isHidden", options: .new, context: nil)
application.unhide()
}
}
private func activateAppIfNeeded() {
if !application.isHidden && !application.isActive {
application.addObserver(self, forKeyPath: "isActive", options: .new, context: nil)
application.activate(options: .activateIgnoringOtherApps)
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "isHidden" {
application.removeObserver(self, forKeyPath: "isHidden")
activateAppIfNeeded()
} else if keyPath == "isActive" {
application.removeObserver(self, forKeyPath: "isActive")
print("Application \(application.localizedName) - ACTIVATED!")
}
}
}
let activator = AppActivator(appName: "Finder")
activator.activate()
Related
I have a UIViewController with the following code. I want to know when the value of portrait effect is changed (in control center). I have tried AVCaptureDevice.isPortraitEffectEnabled and .portraitEffectEnabled, both have the same result: observeValue() is never called. I have verified that the value itself does actually change, and the docs state that KVO is supported for this member.
What am I missing?
To test this I am toggling the value of portaitEffectEnabled by calling AVCaptureDevice.showSystemUserInterface(.videoEffects) and turning it on/off, and expecting the KVO to fire.
#objc class EventSettingsCaptureViewController : UIViewController, ... {
required init(...) {
super.init(nibName: nil, bundle: nil)
if #available(iOS 15.0, *) {
AVCaptureDevice.self.addObserver(self, forKeyPath: "portraitEffectEnabled", options: [.new], context: nil)
}
}
deinit {
if #available(iOS 15.0, *) {
AVCaptureDevice.self.removeObserver(self, forKeyPath: "portraitEffectEnabled", context: nil)
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// Breakpoint set here: never hits
if keyPath == "portraitEffectEnabled" {
guard let object = object as? AVCaptureDevice.Type else { return }
if #available(iOS 15.0, *) {
WLog("isPortraitEffectEnabled changed: \(object.isPortraitEffectEnabled)")
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
That won’t work because the AVCaptureDevice class itself doesn’t have a portraitEffectSupported property.
The issue is that the portraitEffectSupported property is an instance property.
you can always use class_copyPropertyList to double check that the property you’re trying to observe actually exists on that object. Here's an example:
import AVFoundation
func getPropertyNames(of target: AnyObject) -> [String] {
let itsClass: AnyClass = object_getClass(target)!
var count = UInt32()
guard let p = class_copyPropertyList(itsClass, &count) else {
return []
}
defer { p.deallocate() }
let properties = UnsafeBufferPointer(start: p, count: Int(count))
return properties.map { String(cString: property_getName($0)) }
}
// `AVCaptureDevice` has no class properties.
let propertiesOfTheClassItself = getPropertyNames(of: AVCaptureDevice.self)
print(propertiesOfTheClassItself) // => []
// Instances of `AVCaptureDevice` have some instance properties.
let propertiesOfASampleInstance = getPropertyNames(of: AVCaptureDevice.default(for: .video)!)
print(propertiesOfASampleInstance) // => ["transportControlsSupported", "transportControlsPlaybackMode", "transportControlsSpeed", "adjustingFocus", "adjustingExposure", "adjustingWhiteBalance"]
The UITest in question launches the app, taps a cell which pushes the Screen to be tested and then fails with a fatalError() when i make a change that i expect will call a fatalError().
How can i catch the fatalError on the UITest and use it to report that the UITest has failed?
Here is the UITest:
class ConcreteFoodScreenUITests: XCTestCase
{
let app = XCUIApplication()
override func setUpWithError() throws {
continueAfterFailure = false
app.launch()
}
func testPathToConcreteFoodScreen() throws {
//Tap Concrete Cell in FoodDashboard to go to the ConcreteFoodScreen
XCTAssertTrue(app.otherElements["FoodDashboard"].exists)
app.scrollViews.otherElements.tables.staticTexts["100 g, 100cal, P: 90g, F: 80g, C: 70g"].tap()
//ConcreteFoodScreen
XCTAssertTrue(app.otherElements["ConcreteFoodScreen"].exists)
app.tables.cells.containing(.staticText, identifier:"Scale").children(matching: .textField).element.tap()
app.keys["5"].tap() //FIXME: Crashes with a Fatal Error
}
}
Here is the code that is being triggered that i want to know about:
class ScaleCellTextField: SWDecimalTextField {
//there is more code here but not relevant
func updateFoodEntry() {
fatalError()
// if let scale = Double(self.text!) {
// concreteFood.scale = scale
// }
}
}
You can see that i commented out some code here to get it working.
I'm afraid you can't. FatalError() ends your app's process, so I'm not sure you can catch this kind of event.
EDIT:
But...
You can use our dear good old Darwin Notifications... In order to add create a communication channel between your apps : the tested app and the tester app.
You'll need to add a file to both your targets:
typealias NotificationHandler = () -> Void
enum DarwinNotification : String {
case fatalError
}
class DarwinNotificationCenter {
let center: CFNotificationCenter
let prefix: String
var handlers = [String:NotificationHandler]()
init(prefix: String = "com.stackoverflow.answer.") {
center = CFNotificationCenterGetDarwinNotifyCenter()
self.prefix = prefix
}
var unsafeSelf: UnsafeMutableRawPointer {
return Unmanaged.passUnretained(self).toOpaque()
}
deinit {
CFNotificationCenterRemoveObserver(center, unsafeSelf, nil, nil)
}
func notificationName(for identifier: String) -> CFNotificationName {
let name = prefix + identifier
return CFNotificationName(name as CFString)
}
func identifierFrom(name: String) -> String {
if let index = name.range(of: prefix)?.upperBound {
return String(name[index...])
}
else {
return name
}
}
func handleNotification(name: String) {
let identifier = identifierFrom(name: name)
if let handler = handlers[identifier] {
handler()
}
}
func postNotification(for identifier: String) {
let name = notificationName(for: identifier)
CFNotificationCenterPostNotification(center, name, nil, nil, true)
}
func registerHandler(for identifier: String, handler: #escaping NotificationHandler) {
handlers[identifier] = handler
let name = notificationName(for: identifier)
CFNotificationCenterAddObserver(center,
unsafeSelf,
{ (_, observer, name, _, _) in
if let observer = observer, let name = name {
let mySelf = Unmanaged<DarwinNotificationCenter>.fromOpaque(observer).takeUnretainedValue()
mySelf.handleNotification(name: name.rawValue as String)
}
},
name.rawValue,
nil,
.deliverImmediately)
}
func unregisterHandler(for identifier: String) {
handlers[identifier] = nil
CFNotificationCenterRemoveObserver(center, unsafeSelf, notificationName(for: identifier), nil)
}
}
extension DarwinNotificationCenter {
func postNotification(for identifier: DarwinNotification) {
postNotification(for: identifier.rawValue)
}
func registerHandler(for identifier: DarwinNotification, handler: #escaping NotificationHandler) {
registerHandler(for: identifier.rawValue, handler: handler)
}
func unregisterHandler(for identifier: DarwinNotification) {
unregisterHandler(for: identifier.rawValue)
}
}
Then, simply, in your tested application:
#IBAction func onTap(_ sender: Any) {
// ... Do what you need to do, and instead of calling fatalError()
DarwinNotificationCenter().postNotification(for: .fatalError)
}
To catch it in your test, just do the following:
// This is the variable you'll update when notified
var fatalErrorOccurred = false
// We need a dispatch group to wait for the notification to be caught
let waitingFatalGroup = DispatchGroup()
waitingFatalGroup.enter()
let darwinCenter = DarwinNotificationCenter()
// Register the notification
darwinCenter.registerHandler(for: .fatalError) {
// Update the local variable
fatalErrorOccurred = true
// Let the dispatch group you're done
waitingFatalGroup.leave()
}
// Don't forget to unregister
defer {
darwinCenter.unregisterHandler(for: .fatalError)
}
// Perform you tests, here just a tap
app.buttons["BUTTON"].tap()
// Wait for the group the be left or to time out in 3 seconds
let _ = waitingFatalGroup.wait(timeout: .now() + 3)
// Check on the variable to know whether the notification has been received
XCTAssert(fatalErrorOccurred)
And that's it...
Disclaimer: You should not use testing code within your production code, so the use of DarwinNotification should not appear in production. Usually I use compilation directive to only have it in my code in debug or tests, not in release mode.
I'm bulding a radio streaming app in swift. Currently is all working but i want to improve a little the user experience.
I have a RadioPlayer.swift class that handles my radio actions.
import Foundation
import AVFoundation
class RadioPlayer {
static let sharedInstance = RadioPlayer()
private var player = AVPlayer(URL: NSURL(string:"http://rfcmedia.streamguys1.com/classicrock.mp3")!)
private var isPlaying = false
func play() {
player = AVPlayer(URL: NSURL(string: "http://rfcmedia.streamguys1.com/classicrock.mp3")!)
player.play()
isPlaying = true
player.currentItem?.status
}
func pause() {
player.pause()
isPlaying = false
player.replaceCurrentItemWithPlayerItem(nil)
}
func toggle() {
if isPlaying == true {
pause()
} else {
play()
}
}
func currentlyPlaying() -> Bool {
return isPlaying
}
Then i have a View Controller that implement that class. My objective is that when the player is loading, send a message saying that the streaming is being prepared, so the user know that have to wait (also disable the play button).
So my question is how can achieve that, in android i used broadcasts in order to send messages but i didn't found an equivalent in swift.
You can add observers for the AVPlayer properties, e.g. in Swift 3:
player.addObserver(self, forKeyPath: "reasonForWaitingToPlay", options: .new, context: &observerContext)
Or in Swift 2, use .New:
player.addObserver(self, forKeyPath: "reasonForWaitingToPlay", options: .New, context: &observerContext)
Note, that's using a private property to identify the context:
private var observerContext = 0
And then you can add the observer method. In Swift 3:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
// look at `change![.newKey]` to see what the status is, e.g.
if keyPath == "reasonForWaitingToPlay" {
NSLog("\(keyPath): \(change![.newKey])")
}
}
Or in Swift 2:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard context == &observerContext else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
return
}
// look at `change![NSKeyValueChangeNewKey]` to see what the status is, e.g.
if keyPath == "reasonForWaitingToPlay" {
NSLog("\(keyPath): \(change![NSKeyValueChangeNewKey])")
}
}
I'm attempting to increase legibility of my KVO observeValueForKeyPath implementation by replacing the typical long string of nested if/else statements with a single switch statement.
So far, the only thing that's actually worked is:
private let application = UIApplication.sharedApplication()
switch (object!, keyPath!) {
case let (object, "delegate") where object as? UIApplication === application:
appDelegate = application.delegate
break
...
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
Which, if anything, is even harder to read than:
if object as? UIApplication === application && keyPath! == "delegate" {
}
else {
}
Does anybody have a good model for using switch in observeValueForKeyPath (and similar methods)
EDIT: Relevant to #critik's question below, here's more of the code to demonstrate the problems with just using switch (object as! NSObject, keyPath!) {:
private let application = UIApplication.sharedApplication()
private var appDelegate : UIApplicationDelegate?
private var rootWindow : UIWindow?
public override func observeValueForKeyPath(
keyPath: String?,
ofObject object: AnyObject?,
change: [String : AnyObject]?,
context: UnsafeMutablePointer<Void>) {
switch (object as! NSObject, keyPath!) {
case (application, "delegate"):
appDelegate = application.delegate
(appDelegate as? NSObject)?.addObserver(self, forKeyPath: "window", options: [.Initial], context: nil)
break
case (appDelegate, "window"):
rootWindow = appDelegate?.window?.flatMap { $0 }
break
case (rootWindow, "rootViewController"):
rebuildViewControllerList(rootWindow?.rootViewController)
break
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
How about a switch on tuples:
switch (object as! NSObject, keyPath!) {
case (application, "delegate"):
appDelegate = application.delegate
...
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
Note 1. Although I'm agains forced stuff in Swift (downcasts, unwraps, etc), you can safely force downcast to NSObject without getting a crash, as per this SO question, KVO is only available for NSObject subclasses.
Note 2. You also don't need a break in Swift, this will also shorten your code by at least one line :)
This doesn't really solve the problem of using switch in observeValueForKey but it does demonstrate how I simplified the problem space to eliminate the verbose code.
I created a utility class, KVOValueWatcher which allows me to add (and remove) KVO observation on a single property of an object:
public class KVOValueWatcher<ObjectType:NSObject, ValueType:NSObject> : NSObject {
public typealias OnValueChanged = (ValueType?, [String:AnyObject]?) -> ()
let object : ObjectType
let keyPath : String
let options : NSKeyValueObservingOptions
let onValueChanged : OnValueChanged
var engaged = false
public init(object:ObjectType, keyPath:String, options : NSKeyValueObservingOptions = [], onValueChanged: OnValueChanged) {
self.object = object
self.keyPath = keyPath
self.onValueChanged = onValueChanged
self.options = options
super.init()
engage()
}
deinit {
if(engaged) {
print("KVOValueWatcher deleted without being disengaged")
print(" object: \(object)")
print(" keyPath: \(keyPath)")
}
disengage()
}
public func engage() {
if !engaged {
self.object.addObserver(self, forKeyPath: keyPath, options: options, context: nil)
engaged = true
}
}
public func disengage() {
if engaged {
self.object.removeObserver(self, forKeyPath: keyPath)
engaged = false
}
}
override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
self.onValueChanged(((object as? NSObject)?.valueForKeyPath(keyPath!) as? ValueType), change)
}
}
My problem code then becomes:
rootWindowWatcher = KVOValueWatcher(object: applicationDelegate as! NSObject, keyPath: "window", options: [.Initial]) { window, changes in
self.rootViewWatcher?.disengage()
self.rootViewWatcher = nil
if let window = window {
self.rootViewWatcher = KVOValueWatcher(object: window, keyPath: "rootViewController", options: [.Initial]) {
[unowned self] rootViewController, changes in
self.rootViewController = rootViewController
self.rebuildActiveChildWatchers()
}
}
}
The primary reason for the change was because it was becoming a nightmare to maintain all the different observations and get them properly added and removed. Adding the wrapper class eliminates the problem and groups watching a property and the action to be taken when the property changes in the same location.
I have created an observer with .Old | .New options. In the handler method I try to fetch before after values, but compiler complains: 'NSString' is not convertible to 'NSDictionaryIndex: NSObject, AnyObject
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafeMutablePointer<Void>) {
let approvedOld = change[NSKeyValueChangeOldKey] as Bool
let approvedNew = change[NSKeyValueChangeNewKey] as Bool
iOS 11 and Swift >4.1
iOS 11 and Swift 4 brings significant changes to KVO.
The classes should adopt #objcMembers annotation in order to enable the KVO or KVO fails silently.
The variable to be observed must be declared dynamic.
Here is newer implementation,
#objcMembers
class Approval: NSObject {
dynamic var approved: Bool = false
let ApprovalObservingContext = UnsafeMutableRawPointer(bitPattern: 1)
override init() {
super.init()
addObserver(self,
forKeyPath: #keyPath(approved),
options: [.new, .old],
context: ApprovalObservingContext)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard let observingContext = context,
observingContext == ApprovalObservingContext else {
super.observeValue(forKeyPath: keyPath,
of: object,
change: change,
context: context)
return
}
guard let change = change else {
return
}
if let oldValue = change[.oldKey] {
print("Old value \(oldValue)")
}
if let newValue = change[.newKey] {
print("New value \(newValue)")
}
}
deinit {
removeObserver(self, forKeyPath: #keyPath(approved))
}
}
There is also new bock based api for KVO, which works like this,
#objcMembers
class Approval: NSObject {
dynamic var approved: Bool = false
var approvalObserver: NSKeyValueObservation!
override init() {
super.init()
approvalObserver = observe(\.approved, options: [.new, .old]) { _, change in
if let newValue = change.newValue {
print("New value is \(newValue)")
}
if let oldValue = change.oldValue {
print("Old value is \(oldValue)")
}
}
}
}
Block based api look super good and easy to use. Also, KeyValueObservation is invalidated when deinited, so there is no hard requirement for removing observer.
Swift 2.0 and iOS < 10
With Swift 2.0, here is a complete implementation for a class that uses KVO,
class Approval: NSObject {
dynamic var approved: Bool = false
let ApprovalObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
override init() {
super.init()
addObserver(self, forKeyPath: "approved", options: [.Old, .New], context: ApprovalObservingContext)
}
override func observeValueForKeyPath(keyPath: String?,
ofObject object: AnyObject?,
change: [String : AnyObject]?,
context: UnsafeMutablePointer<Void>) {
if let theChange = change as? [String: Bool] {
if let approvedOld = theChange[NSKeyValueChangeOldKey] {
print("Old value \(approvedOld)")
}
if let approvedNew = theChange[NSKeyValueChangeNewKey]{
print("New value \(approvedNew)")
}
return
}
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
deinit {
removeObserver(self, forKeyPath: "approved")
}
}
let a = Approval()
a.approved = true