SwiftUI on macOS: Life Cycle and App Delegate source code

This is the Swift source code to accompany the article SwiftUI on macOS: Life Cycle and App Delegate.

AppKit AppDelegate approach

AppDelegate

import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {

    var theTimer: Timer?
    var theTimeInterval = 86400.0
    var theTolerance = 300.0

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let defaults = UserDefaults.standard
        dontXProCheck = defaults.bool(forKey: "dontXPR")
        if !dontXProCheck {
            let deDys = deDysfunction.init()
            if !(deDys.checkAdmin()) {
               NSApplication.shared.terminate(self)
            }
       }
       self.theTimeInterval = defaults.double(forKey: "updateCheckInt")
       self.theTolerance = defaults.double(forKey: "updateCheckTol")
        if self.theTolerance < 1.0 {
            self.theTolerance = 300.0
        }
        theSkinner = Skinner()
        theTimer = Timer.scheduledTimer(timeInterval: self.theTimeInterval, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
        theTimer?.tolerance = self.theTolerance
        theSkinner?.runChecks()
    }
   
    @objc func fireTimer() {
       theSkinner?.runChecks()
    }
    
    @IBAction func runSkintChecks(_ sender: NSMenuItem) {
       theSkinner?.runChecks()
    }
   
    func applicationWillTerminate(_ aNotification: Notification) {
        theTimer?.invalidate()
    }

SwiftUI with AppDelegate approach

AppDelegate

import Cocoa
import SwiftUI

class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
 
    var theTimer: Timer?
    var theTimeInterval = 86400.0
    var theTolerance = 300.0
    @Published var isInited = false

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let defaults = UserDefaults.standard
        dontXProCheck = defaults.bool(forKey: "dontXPR")
        if !dontXProCheck {
            let deDys = deDysfunction.init()
            if !(deDys.checkAdmin()) {
                dontXProCheck = true
            }
       }
       self.theTimeInterval = defaults.double(forKey: "updateCheckInt")
       self.theTolerance = defaults.double(forKey: "updateCheckTol")
       defaults.set(dontXProCheck, forKey: "dontXPR")
       theSkinner = Skinner()
       self.isInited = true
       theTimer = Timer.scheduledTimer(timeInterval: self.theTimeInterval, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
       theTimer?.tolerance = self.theTolerance
    }
    
    @objc func fireTimer() {
        theSkinner?.runChecks()
    }
    
    func applicationWillTerminate(_ aNotification: Notification) {
        theTimer?.invalidate()
    }

}

SwiftUIApp

import SwiftUI

@main
struct SkintSwiftUIApp: App {
    @NSApplicationDelegateAdaptor private var appDelegate: AppDelegate

    var body: some Scene {
        Window("Skint SwiftUI", id: "main") {
            ContentView()
        }
    }
}

ContentView


import SwiftUI

struct ContentView: View {
    @EnvironmentObject private var appDelegate: AppDelegate

    var body: some View {
        if appDelegate.isInited {
            VStack {
               Text(theSkinner?.theErrors ?? "??")
                    .font(.title3)
               Text("Overall error score: \(theSkinner?.theErrorScore ?? -1)")
                    .font(.title3)
                    .padding()
            }
            .background(.indigo)
        } else {
            VStack {
                Text("Waiting for results…")
                    .font(.title)
            }
            .background(.gray)
        }
    }
}

Pure SwiftUI approach

SwiftUIApp

import SwiftUI

@main
struct DeclarerTestApp: App {
    let theLSkinner = Skinner()
    var body: some Scene {
        Window("Declarer", id: "main") {
            ContentView()
        }
    }
}

ContentView

import SwiftUI

struct ContentView: View {
    var body: some View {
        if isInited {
            VStack {
                Text(theSkinner?.theErrors ?? "??")
                    .font(.title3)
                Text(theSkinner?.dateText ?? "??")
                    .font(.title2)
                Text("Overall error score: \(theSkinner?.theErrorScore ?? -1)")
                    .font(.title3)
                    .padding()
            }
            .background(.windowBackground)
        } else {
            VStack {
                Text("Waiting for results…")
                    .font(.title)
            }
            .background(.gray)
        }
    }
}

#Preview {
    ContentView()
}

Skinner

import Foundation
import SwiftUI

var theSkinner: Skinner?
var theTimer: Timer?
var theTimeInterval = 86400.0
var theTolerance = 300.0
var isInited = false

@Observable
class Skinner: NSObject {
    
    var theErrors = ""
    var theErrorScore = 0
    
    override init() {
        super.init()
        theSkinner = self
        let defaults = UserDefaults.standard
        dontXProCheck = defaults.bool(forKey: "dontXPR")
        if !dontXProCheck {
            let deDys = deDysfunction.init()
            if !(deDys.checkAdmin()) {
                dontXProCheck = true
            }
        }
        theTimeInterval = defaults.double(forKey: "updateCheckInt")
        theTolerance = defaults.double(forKey: "updateCheckTol")
         defaults.set(dontXProCheck, forKey: "dontXPR")
        isInited = true
        runChecks()
    }

    func runChecks() {
        self.theErrors = ""
        self.theErrorScore = 0
        theSkinfo = Skinfo.init()
    }
}

Scheduler

@Observable
class Sched: NSObject {
    var theTimer: Timer?
    var theTimeInterval = 1.0
    var theCount = 0

    func startTimer() {
        self.theTimer = Timer.scheduledTimer(withTimeInterval: self.theTimeInterval, repeats: true, block: { _ in
            self.fireTimer()
        })
   }
    
    func stopTimer() {
        self.theTimer?.invalidate()
   }

    @objc func fireTimer() {
        self.theCount += 1
    }

}

applicationWillTerminate

import SwiftUI

@main
struct SchedulerApp: App {
    @Environment(\.scenePhase) private var scenePhase
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase, initial: false) { oldPhase, newPhase in
            if newPhase == .background {
                let result = removeTimer()
            }
        }
    }
}

End of code supplement.