watchOS SwiftUI app not founding applicationWillTerminate() function - swift

I am trying to add a function applicationWillTerminate() in my independent watchOS app. With searching from the internet, I found that you can find application life cycle function delegates with using WKExtensionDelegate. But I am unable to find any function or method to detect that app is going to kill. So my main concern is to find a method that will called before independent watchOS app is terminated.
import Foundation
import SwiftUI
import WatchKit
final class ExtensionDelegate: NSObject, ObservableObject, WKExtensionDelegate {
func applicationDidFinishLaunching() {
NSLog("App launched")
}
func applicationWillTerminate() {
NSLog("App will terminate")
// trying to call this function but is not declared WKExtensionDelegate
}
func applicationDidEnterBackground() {
NSLog("App deactivated")
NotificationCenter.default.post(name: .appDeactivated, object: nil)
}
}

Related

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?

Play local sound on watchOS button tap

I'm new to swift development and building a simple app for the apple watch. I'd like a short sound to play when a button is tapped. Is this possible?
From what I understand the best way to do this is with AVFoundation and AVAudioPlayer. There seem to have been a lot of updates for this in the last few releases and I'm finding conflicting advice. Based on a few tutorials I've put this simple test together, but I'm getting a "Thread 1:Fatal error: Unexpectedly found nil when unwrapping an Optional value" Here is my code:
import WatchKit
import Foundation
import AVFoundation
var dogSound = AVAudioPlayer()
class InterfaceController: WKInterfaceController {
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
#IBAction func playTapped() {
let path = Bundle.main.path(forResource: "Dog", ofType: ".mp3")!
let url = URL(fileURLWithPath: path)
do {
dogSound = try AVAudioPlayer(contentsOf: url)
dogSound.play()
} catch {
//couldn't load file :(
}
}
}
My audio file is an mp3 named Dog in the Assets.xcassets folder in WatchKit Extension and I have added AVFoundation.framework to the Link Binary with Libraries
What am I doing wrong and is there a tutorial for the right way to implement this? Thanks a lot!

Get audio metadata from current playing audio in iTunes on OS X

I am writing an app in Swift on OS X. Is there a way to get the current playing audio in iTunes? I would like to get details such as title, artist, etc.
What I've found already:
Here is an Objective C example, but not all those methods are available with Swift.
This example uses AppleScript, which I might have to do. But I'd rather use Swift if possible.
Here's a simple example how to get the properties from the current track:
Tested on (El Capitan v10.11.5, iTunes 12.4 and Xcode 7.3).
import Cocoa
import ScriptingBridge
#objc protocol iTunesApplication {
optional func currentTrack()-> AnyObject
optional var properties: NSDictionary {get}
//if you need another object or method from the iTunes.h, you must add it here
}
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
let iTunesApp: AnyObject = SBApplication(bundleIdentifier: "com.apple.iTunes")!
let trackDict = iTunesApp.currentTrack!().properties as Dictionary
if (trackDict["name"] != nil) {// if nil then no current track
print(trackDict["name"]!) // print the title
print(trackDict["artist"]!)
print(trackDict["album"]!)
print(trackDict["playedCount"]!)
// print(trackDict) // print the dictionary
}
}
}

Sending replyToApplicationShouldTerminate to NSApplication in Swift

Here is my AppDelegate.swift. I implement the applicationShouldTerminate protocol from NSApplication. Which answer I give depends on the status of is.Started in the mainWindowController. (This is the SpeakLine example from Cocoa Programming for OS X: The Big Nerd Ranch Guide 5/e—I'm trying to take the example one step further and keep the program from being allowed to quit while the talking is going on.)
What I want to do is change TerminateReply.TerminateCancel to TerminateReply.TermianteLater and then send NSApplication the replyToApplicationShouldTerminate(true) signal when the talking is done. As it stands now in the MainControllerWindow.swift class I have a function set up to handle state changes in the Speech Synthesizer and that's where I want to call it.
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindowController: MainWindowController?
func applicationDidFinishLaunching(aNotification: NSNotification) {
let mainWindowController = MainWindowController()
mainWindowController.showWindow(self)
self.mainWindowController = mainWindowController
}
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply {
if (mainWindowController!.isStarted) {
return NSApplicationTerminateReply.TerminateCancel
} else {
return NSApplicationTerminateReply.TerminateNow
}
}
}
The trouble is, when I put it here, I get an error.
var isStarted: Bool = false {
didSet {
updateButtons()
NSApplication.replyToApplicationShouldTerminate(true)
}
}
it tells me I can't use a bool. It also tells me I can't use an Objective C bool when I try to put YES. How do I tell NSApplication it's OK to quit now?
I believe you should change
NSApplication.replyToApplicationShouldTerminate(true)
to
NSApplication.sharedApplication().replyToApplicationShouldTerminate(true)
since replyToApplicationShouldTerminate is a instance method rather then a class method.
my tow cents for swift 5.x:
NSApplication.shared.reply(toApplicationShouldTerminate: true)

Handle browser's Notification in Swift webview application

I have very little knowledge about Swift, xCode or Objective-C but I have read/watch a lot of tutorials and my only intention is to create a webview with notification in webkit wrapper such as this Cocoa application provided by xCode.
Currently I have my code that can pretty much run when I build it.
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var webView: WebView!
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://irccloud.com"
self.webView.mainFrame.loadRequest(NSURLRequest(URL: NSURL(string: urlString)!))
}
override func webView(sender: WebView!, runJavaScriptAlertPanelWithMessage message: String!, initiatedByFrame frame: WebFrame!) {
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Now, if I access irccloud url directly I'm able to receive both sound and banner notification, but I'm wondering how can I make this notification able to display like native Mac app written in Swift?
This app able to do that, but it's in Objective-C https://github.com/jnordberg/irccloudapp