From b8a415b6fdd6a224423d3efb43c73a87805ca0ce Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 16 Jan 2022 12:32:45 -0500 Subject: [PATCH] Add AppKit bundle, fix light appearance --- Reader.xcodeproj/project.pbxproj | 151 +++++++++++++++++++++ Reader/AppDelegate.swift | 32 ++++- Reader/Preferences.swift | 20 +-- Reader/PrefsSceneDelegate.swift | 15 +- Reader/SceneDelegate.swift | 15 +- Reader/Screens/Preferences/PrefsView.swift | 6 +- ReaderMac/ReaderMac.swift | 40 ++++++ 7 files changed, 258 insertions(+), 21 deletions(-) create mode 100644 ReaderMac/ReaderMac.swift diff --git a/Reader.xcodeproj/project.pbxproj b/Reader.xcodeproj/project.pbxproj index 0311931..b001e9c 100644 --- a/Reader.xcodeproj/project.pbxproj +++ b/Reader.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ D68408E827947D0800E327D2 /* PrefsSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68408E727947D0800E327D2 /* PrefsSceneDelegate.swift */; }; D68408ED2794803D00E327D2 /* PrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68408EC2794803D00E327D2 /* PrefsView.swift */; }; D68408EF2794808E00E327D2 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68408EE2794808E00E327D2 /* Preferences.swift */; }; + D68409132794870000E327D2 /* ReaderMac.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68409122794870000E327D2 /* ReaderMac.swift */; }; + D6840914279487DC00E327D2 /* ReaderMac.bundle in Embed PlugIns */ = {isa = PBXBuildFile; fileRef = D684090D279486BF00E327D2 /* ReaderMac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D68B303627907D9200E8B3FA /* ExcerptGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68B303527907D9200E8B3FA /* ExcerptGenerator.swift */; }; D68B303D2792204B00E8B3FA /* read.js in Resources */ = {isa = PBXBuildFile; fileRef = D68B303C2792204B00E8B3FA /* read.js */; }; D68B30402792729A00E8B3FA /* AppSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68B303F2792729A00E8B3FA /* AppSplitViewController.swift */; }; @@ -58,6 +60,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + D6840915279487DC00E327D2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D6C687E0272CD27600874C10 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D684090C279486BF00E327D2; + remoteInfo = ReaderMac; + }; D6C68802272CD27700874C10 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D6C687E0272CD27600874C10 /* Project object */; @@ -82,6 +91,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + D6840917279487DD00E327D2 /* Embed PlugIns */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + D6840914279487DC00E327D2 /* ReaderMac.bundle in Embed PlugIns */, + ); + name = "Embed PlugIns"; + runOnlyForDeploymentPostprocessing = 0; + }; D6C6882E272CD2BA00874C10 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -107,6 +127,8 @@ D68408E727947D0800E327D2 /* PrefsSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsSceneDelegate.swift; sourceTree = ""; }; D68408EC2794803D00E327D2 /* PrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsView.swift; sourceTree = ""; }; D68408EE2794808E00E327D2 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; + D684090D279486BF00E327D2 /* ReaderMac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReaderMac.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + D68409122794870000E327D2 /* ReaderMac.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderMac.swift; sourceTree = ""; }; D68B3032278FDD1A00E8B3FA /* Reader-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Reader-Bridging-Header.h"; sourceTree = ""; }; D68B303527907D9200E8B3FA /* ExcerptGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExcerptGenerator.swift; sourceTree = ""; }; D68B3037279099FD00E8B3FA /* liblolhtml.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblolhtml.a; path = "lol-html/c-api/target/aarch64-apple-ios-sim/release/liblolhtml.a"; sourceTree = ""; }; @@ -152,6 +174,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + D684090A279486BF00E327D2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D6C687E5272CD27600874C10 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -224,6 +253,14 @@ path = Preferences; sourceTree = ""; }; + D6840911279486C400E327D2 /* ReaderMac */ = { + isa = PBXGroup; + children = ( + D68409122794870000E327D2 /* ReaderMac.swift */, + ); + path = ReaderMac; + sourceTree = ""; + }; D68B302E278FDCE200E8B3FA /* Frameworks */ = { isa = PBXGroup; children = ( @@ -253,6 +290,7 @@ D6C687EA272CD27600874C10 /* Reader */, D6C68804272CD27700874C10 /* ReaderTests */, D6C6880E272CD27700874C10 /* ReaderUITests */, + D6840911279486C400E327D2 /* ReaderMac */, D6C68824272CD2BA00874C10 /* Fervor */, D6C687E9272CD27600874C10 /* Products */, D68B302E278FDCE200E8B3FA /* Frameworks */, @@ -266,6 +304,7 @@ D6C68801272CD27700874C10 /* ReaderTests.xctest */, D6C6880B272CD27700874C10 /* ReaderUITests.xctest */, D6C68823272CD2BA00874C10 /* Fervor.framework */, + D684090D279486BF00E327D2 /* ReaderMac.bundle */, ); name = Products; sourceTree = ""; @@ -362,6 +401,23 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + D684090C279486BF00E327D2 /* ReaderMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = D6840910279486BF00E327D2 /* Build configuration list for PBXNativeTarget "ReaderMac" */; + buildPhases = ( + D6840909279486BF00E327D2 /* Sources */, + D684090A279486BF00E327D2 /* Frameworks */, + D684090B279486BF00E327D2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ReaderMac; + productName = ReaderMac; + productReference = D684090D279486BF00E327D2 /* ReaderMac.bundle */; + productType = "com.apple.product-type.bundle"; + }; D6C687E7272CD27600874C10 /* Reader */ = { isa = PBXNativeTarget; buildConfigurationList = D6C68815272CD27700874C10 /* Build configuration list for PBXNativeTarget "Reader" */; @@ -371,11 +427,13 @@ D6C687E5272CD27600874C10 /* Frameworks */, D6C687E6272CD27600874C10 /* Resources */, D6C6882E272CD2BA00874C10 /* Embed Frameworks */, + D6840917279487DD00E327D2 /* Embed PlugIns */, ); buildRules = ( ); dependencies = ( D6C68828272CD2BA00874C10 /* PBXTargetDependency */, + D6840916279487DC00E327D2 /* PBXTargetDependency */, ); name = Reader; packageProductDependencies = ( @@ -449,6 +507,10 @@ LastSwiftUpdateCheck = 1320; LastUpgradeCheck = 1320; TargetAttributes = { + D684090C279486BF00E327D2 = { + CreatedOnToolsVersion = 13.2; + LastSwiftMigration = 1320; + }; D6C687E7272CD27600874C10 = { CreatedOnToolsVersion = 13.2; }; @@ -486,11 +548,19 @@ D6C68800272CD27700874C10 /* ReaderTests */, D6C6880A272CD27700874C10 /* ReaderUITests */, D6C68822272CD2BA00874C10 /* Fervor */, + D684090C279486BF00E327D2 /* ReaderMac */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + D684090B279486BF00E327D2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D6C687E6272CD27600874C10 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -548,6 +618,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + D6840909279486BF00E327D2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D68409132794870000E327D2 /* ReaderMac.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D6C687E4272CD27600874C10 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -619,6 +697,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + D6840916279487DC00E327D2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D684090C279486BF00E327D2 /* ReaderMac */; + targetProxy = D6840915279487DC00E327D2 /* PBXContainerItemProxy */; + }; D6C68803272CD27700874C10 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D6C687E7272CD27600874C10 /* Reader */; @@ -648,6 +731,65 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + D684090E279486BF00E327D2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ZPBBSK8L8B; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPrincipalClass = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.Reader.ReaderMac; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + D684090F279486BF00E327D2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ZPBBSK8L8B; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSPrincipalClass = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 12.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.Reader.ReaderMac; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; D6C68813272CD27700874C10 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -996,6 +1138,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + D6840910279486BF00E327D2 /* Build configuration list for PBXNativeTarget "ReaderMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D684090E279486BF00E327D2 /* Debug */, + D684090F279486BF00E327D2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; D6C687E3272CD27600874C10 /* Build configuration list for PBXProject "Reader" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Reader/AppDelegate.swift b/Reader/AppDelegate.swift index bf80c01..8355c24 100644 --- a/Reader/AppDelegate.swift +++ b/Reader/AppDelegate.swift @@ -14,6 +14,10 @@ import Combine class AppDelegate: UIResponder, UIApplicationDelegate { private var cancellables = Set() + + #if targetEnvironment(macCatalyst) + private var readerMac: NSObject! + #endif func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { swizzleWKWebView() @@ -25,6 +29,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } .store(in: &cancellables) + #if targetEnvironment(macCatalyst) + let macBundleURL = Bundle.main.builtInPlugInsURL!.appendingPathComponent("ReaderMac.bundle") + let bundle = Bundle(url: macBundleURL)! + do { + try bundle.loadAndReturnError() + + let clazz = NSClassFromString("ReaderMac.ReaderMac")! as! NSObject.Type + readerMac = clazz.init() + readerMac.perform(Selector(("setup"))) + } catch { + print("Unable to load ReaderMac bundle: \(error)") + } + #endif + + NotificationCenter.default.addObserver(self, selector: #selector(updateAppearance), name: .appearanceChanged, object: nil) + updateAppearance() + return true } @@ -112,7 +133,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } @objc private func showPreferences() { - UIApplication.shared.requestSceneSessionActivation(nil, userActivity: .preferences(), options: nil, errorHandler: nil) + let existing = UIApplication.shared.connectedScenes.first { + $0.session.configuration.name == "prefs" + } + UIApplication.shared.requestSceneSessionActivation(existing?.session, userActivity: .preferences(), options: nil, errorHandler: nil) + } + + @objc private func updateAppearance() { + #if targetEnvironment(macCatalyst) + readerMac.perform(Selector(("updateAppearance:")), with: Preferences.shared.appearance.rawValue) + #endif } } diff --git a/Reader/Preferences.swift b/Reader/Preferences.swift index 56a9d4c..a79b701 100644 --- a/Reader/Preferences.swift +++ b/Reader/Preferences.swift @@ -5,7 +5,7 @@ // Created by Shadowfacts on 1/16/22. // -import UIKit +import Foundation class Preferences: Codable, ObservableObject { @@ -36,7 +36,7 @@ class Preferences: Codable, ObservableObject { required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.appearance = try container.decode(UIUserInterfaceStyle.self, forKey: .appearance) + self.appearance = try container.decode(Appearance.self, forKey: .appearance) } func encode(to encoder: Encoder) throws { @@ -44,12 +44,11 @@ class Preferences: Codable, ObservableObject { try container.encode(appearance, forKey: .appearance) } - var appearance = UIUserInterfaceStyle.unspecified { - willSet { - objectWillChange.send() - } + @Published var appearance = Appearance.unspecified { didSet { - NotificationCenter.default.post(name: .userInterfaceStyleChanged, object: nil) + NotificationCenter.default.post(name: .appearanceChanged, object: nil, userInfo: [ + "appearance": appearance.rawValue + ]) } } @@ -59,9 +58,12 @@ class Preferences: Codable, ObservableObject { } -extension UIUserInterfaceStyle: Codable { +enum Appearance: Int, Codable { + case unspecified = 0 + case light = 1 + case dark = 2 } extension Notification.Name { - static let userInterfaceStyleChanged = Notification.Name("userInterfaceStyleChanged") + static let appearanceChanged = Notification.Name("appearanceChanged") } diff --git a/Reader/PrefsSceneDelegate.swift b/Reader/PrefsSceneDelegate.swift index a54dda1..32d77b5 100644 --- a/Reader/PrefsSceneDelegate.swift +++ b/Reader/PrefsSceneDelegate.swift @@ -34,12 +34,19 @@ class PrefsSceneDelegate: UIResponder, UIWindowSceneDelegate { window!.makeKeyAndVisible() - NotificationCenter.default.addObserver(self, selector: #selector(updateUserInterfaceStyle), name: .userInterfaceStyleChanged, object: nil) - updateUserInterfaceStyle() + NotificationCenter.default.addObserver(self, selector: #selector(updateAppearance), name: .appearanceChanged, object: nil) + updateAppearance() } - @objc private func updateUserInterfaceStyle() { - window?.overrideUserInterfaceStyle = Preferences.shared.appearance + @objc private func updateAppearance() { + switch Preferences.shared.appearance { + case .unspecified: + window!.overrideUserInterfaceStyle = .unspecified + case .light: + window!.overrideUserInterfaceStyle = .light + case .dark: + window!.overrideUserInterfaceStyle = .dark + } } } diff --git a/Reader/SceneDelegate.swift b/Reader/SceneDelegate.swift index e65c309..3dc35db 100644 --- a/Reader/SceneDelegate.swift +++ b/Reader/SceneDelegate.swift @@ -55,8 +55,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window!.makeKeyAndVisible() - NotificationCenter.default.addObserver(self, selector: #selector(updateUserInterfaceStyle), name: .userInterfaceStyleChanged, object: nil) - updateUserInterfaceStyle() + NotificationCenter.default.addObserver(self, selector: #selector(updateAppearance), name: .appearanceChanged, object: nil) + updateAppearance() } func sceneDidDisconnect(_ scene: UIScene) { @@ -120,8 +120,15 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - @objc private func updateUserInterfaceStyle() { - window?.overrideUserInterfaceStyle = Preferences.shared.appearance + @objc private func updateAppearance() { + switch Preferences.shared.appearance { + case .unspecified: + window!.overrideUserInterfaceStyle = .unspecified + case .light: + window!.overrideUserInterfaceStyle = .light + case .dark: + window!.overrideUserInterfaceStyle = .dark + } } } diff --git a/Reader/Screens/Preferences/PrefsView.swift b/Reader/Screens/Preferences/PrefsView.swift index 5e2382e..5b82ec0 100644 --- a/Reader/Screens/Preferences/PrefsView.swift +++ b/Reader/Screens/Preferences/PrefsView.swift @@ -24,9 +24,9 @@ struct PrefsView: View { private var appearance: some View { Picker("Appearance", selection: $preferences.appearance) { - Text("System").tag(UIUserInterfaceStyle.unspecified) - Text("Dark").tag(UIUserInterfaceStyle.dark) - Text("Light").tag(UIUserInterfaceStyle.light) + Text("System").tag(Appearance.unspecified) + Text("Dark").tag(Appearance.dark) + Text("Light").tag(Appearance.light) } } } diff --git a/ReaderMac/ReaderMac.swift b/ReaderMac/ReaderMac.swift new file mode 100644 index 0000000..a577004 --- /dev/null +++ b/ReaderMac/ReaderMac.swift @@ -0,0 +1,40 @@ +// +// ReaderMac.swift +// ReaderMac +// +// Created by Shadowfacts on 1/16/22. +// + +import AppKit + +class ReaderMac: NSObject { + + private(set) static var shared: ReaderMac! + + override init() { + if ReaderMac.shared != nil { + fatalError() + } + + super.init() + + ReaderMac.shared = self + } + + @objc func setup() { + } + + @objc func updateAppearance(_ appearance: NSNumber) { + switch appearance { + case 0: + NSApp.appearance = nil + case 1: + NSApp.appearance = NSAppearance(named: .aqua) + case 2: + NSApp.appearance = NSAppearance(named: .darkAqua) + default: + fatalError("unexpected appeaerance value: \(appearance)") + } + } + +}