diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift index 2d0d41fd..6dfdf71a 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift @@ -81,8 +81,8 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol { fatalError("init(coder:) has not been implemented") } - open var candidateFont = NSFont.systemFont( - ofSize: min(196, max(12, Double(UserDefaults.standard.integer(forKey: "CandidateListTextSize")))) + open var candidateFont: NSFont = NSFont.systemFont( + ofSize: min(196, max(12, Double(UserDefaults.current.integer(forKey: "CandidateListTextSize")))) ) @discardableResult open func showNextLine() -> Bool { diff --git a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_Misc.swift b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_Misc.swift index 1ad06116..a4e533c2 100644 --- a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_Misc.swift +++ b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_Misc.swift @@ -133,7 +133,7 @@ public extension NSApplication { let appearanceDescription = NSApp.effectiveAppearance.debugDescription .lowercased() return appearanceDescription.contains("dark") - } else if let appleInterfaceStyle = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") { + } else if let appleInterfaceStyle = UserDefaults.current.string(forKey: "AppleInterfaceStyle") { return appleInterfaceStyle.lowercased().contains("dark") } return false diff --git a/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift b/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift index b33dc69d..19358781 100644 --- a/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift +++ b/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift @@ -49,14 +49,6 @@ public enum IMKHelper { "org.unknown.keylayout.vChewingMiTAC", ] - public static var currentBasicKeyboardLayout: String { - UserDefaults.standard.string(forKey: "BasicKeyboardLayout") ?? "" - } - - public static var isDynamicBasicKeyboardLayoutEnabled: Bool { - Self.arrDynamicBasicKeyLayouts.contains(currentBasicKeyboardLayout) || !currentBasicKeyboardLayout.isEmpty - } - public static var allowedAlphanumericalTISInputSources: [TISInputSource] { arrWhitelistedKeyLayoutsASCII.compactMap { TISInputSource.generate(from: $0) } } diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/LMMgr_Utilities.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/LMMgr_Utilities.swift index ad2e7bf2..de8f883d 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/LMMgr_Utilities.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/LMMgr_Utilities.swift @@ -230,22 +230,22 @@ public extension LMMgr { { return userDictPathDefault } - if UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified.rawValue) != nil { + if UserDefaults.current.object(forKey: UserDef.kUserDataFolderSpecified.rawValue) != nil { BookmarkManager.shared.loadBookmarks() if Self.checkIfSpecifiedUserDataFolderValid(userDictPathSpecified) { return userDictPathSpecified } - UserDefaults.standard.removeObject(forKey: UserDef.kUserDataFolderSpecified.rawValue) + UserDefaults.current.removeObject(forKey: UserDef.kUserDataFolderSpecified.rawValue) } return userDictPathDefault } static func cassettePath() -> String { let rawCassettePath = PrefMgr.shared.cassettePath - if UserDefaults.standard.object(forKey: UserDef.kCassettePath.rawValue) != nil { + if UserDefaults.current.object(forKey: UserDef.kCassettePath.rawValue) != nil { BookmarkManager.shared.loadBookmarks() if Self.checkCassettePathValidity(rawCassettePath) { return rawCassettePath } - UserDefaults.standard.removeObject(forKey: UserDef.kCassettePath.rawValue) + UserDefaults.current.removeObject(forKey: UserDef.kCassettePath.rawValue) } return "" } @@ -253,12 +253,12 @@ public extension LMMgr { // MARK: - 重設使用者語彙檔案目錄 static func resetSpecifiedUserDataFolder() { - UserDefaults.standard.set(dataFolderPath(isDefaultFolder: true), forKey: UserDef.kUserDataFolderSpecified.rawValue) + UserDefaults.current.set(dataFolderPath(isDefaultFolder: true), forKey: UserDef.kUserDataFolderSpecified.rawValue) Self.initUserLangModels() } static func resetCassettePath() { - UserDefaults.standard.set("", forKey: UserDef.kCassettePath.rawValue) + UserDefaults.current.set("", forKey: UserDef.kCassettePath.rawValue) Self.loadCassetteData() } diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Core.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Core.swift index 3dcaa10b..e9c705eb 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Core.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Core.swift @@ -236,11 +236,11 @@ public class PrefMgr: PrefMgrProtocol { // 康熙轉換與 JIS 轉換不能同時開啟,否則會出現某些奇奇怪怪的情況 if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled { shiftJISShinjitaiOutputEnabled.toggle() - UserDefaults.standard.set( + UserDefaults.current.set( shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue ) } - UserDefaults.standard.set( + UserDefaults.current.set( chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled.rawValue ) } @@ -252,11 +252,11 @@ public class PrefMgr: PrefMgrProtocol { // 康熙轉換與 JIS 轉換不能同時開啟,否則會出現某些奇奇怪怪的情況 if shiftJISShinjitaiOutputEnabled, chineseConversionEnabled { chineseConversionEnabled.toggle() - UserDefaults.standard.set( + UserDefaults.current.set( chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled.rawValue ) } - UserDefaults.standard.set( + UserDefaults.current.set( shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue ) } diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Extension.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Extension.swift index 2d8d4663..583200f8 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Extension.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/PrefMgr_Extension.swift @@ -25,15 +25,15 @@ public extension PrefMgr { showNotificationsWhenTogglingCapsLock = false } if appleLanguages.isEmpty { - UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages.rawValue) + UserDefaults.current.removeObject(forKey: UserDef.kAppleLanguages.rawValue) } // 自動糾正選字鍵 (利用其 didSet 特性) candidateKeys = candidateKeys // 客體黑名單資料類型升級。 - if let clients = UserDefaults.standard.object( + if let clients = UserDefaults.current.object( forKey: UserDef.kClientsIMKTextInputIncapable.rawValue ) as? [String] { - UserDefaults.standard.removeObject(forKey: UserDef.kClientsIMKTextInputIncapable.rawValue) + UserDefaults.current.removeObject(forKey: UserDef.kClientsIMKTextInputIncapable.rawValue) clients.forEach { neta in guard !clientsIMKTextInputIncapable.keys.contains(neta) else { return } clientsIMKTextInputIncapable[neta] = true diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift index f80d0fcf..eea6aee0 100644 --- a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift @@ -2,6 +2,14 @@ import XCTest final class MainAssemblyTests: XCTestCase { - func testExample() throws { + override func setUpWithError() throws { + UserDefaults.unitTests = .init(suiteName: "org.atelierInmu.vChewing.MainAssembly.UnitTests") + UserDefaults.pendingUnitTests = true } + + override func tearDownWithError() throws { + UserDefaults.unitTests?.removeSuite(named: "org.atelierInmu.vChewing.MainAssembly.UnitTests") + } + + func testExample() throws {} } diff --git a/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift b/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift index 702ab4f2..e1c3cc69 100644 --- a/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift +++ b/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift @@ -16,7 +16,7 @@ import SwiftUI import SwiftUIBackports private let loc: String = - (UserDefaults.standard.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"])[0] + (UserDefaults.current.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"])[0] @available(macOS 10.15, *) extension VwrPhraseEditorUI { @@ -31,7 +31,7 @@ public struct VwrPhraseEditorUI: View { ) @Binding public var txtContent: String @ObservedObject public var fileChangeIndicator = FileObserveProject.shared - @State private var selAutoReloadExternalModifications: Bool = UserDefaults.standard.bool( + @State private var selAutoReloadExternalModifications: Bool = UserDefaults.current.bool( forKey: UserDef.kPhraseEditorAutoReloadExternalModifications.rawValue) @State var lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 @State var lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 diff --git a/Packages/vChewing_Shared/Sources/Shared/IMKUtilsImpl.swift b/Packages/vChewing_Shared/Sources/Shared/IMKUtilsImpl.swift new file mode 100644 index 00000000..33db6c20 --- /dev/null +++ b/Packages/vChewing_Shared/Sources/Shared/IMKUtilsImpl.swift @@ -0,0 +1,22 @@ +// (c) 2022 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import AppKit +import IMKUtils + +// MARK: - IMKHelper Extension + +public extension IMKHelper { + static var currentBasicKeyboardLayout: String { + UserDefaults.current.string(forKey: "BasicKeyboardLayout") ?? "" + } + + static var isDynamicBasicKeyboardLayoutEnabled: Bool { + Self.arrDynamicBasicKeyLayouts.contains(currentBasicKeyboardLayout) || !currentBasicKeyboardLayout.isEmpty + } +} diff --git a/Packages/vChewing_Shared/Sources/Shared/Shared.swift b/Packages/vChewing_Shared/Sources/Shared/Shared.swift index 249b22ad..8a4e90de 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Shared.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Shared.swift @@ -98,7 +98,7 @@ public enum UserDef: String, CaseIterable { public static func resetAll() { UserDef.allCases.forEach { - UserDefaults.standard.removeObject(forKey: $0.rawValue) + UserDefaults.current.removeObject(forKey: $0.rawValue) } } @@ -106,7 +106,7 @@ public enum UserDef: String, CaseIterable { let data = snapshot.data guard !data.isEmpty else { return } UserDef.allCases.forEach { - UserDefaults.standard.set(data[$0.rawValue], forKey: $0.rawValue) + UserDefaults.current.set(data[$0.rawValue], forKey: $0.rawValue) } } @@ -114,7 +114,7 @@ public enum UserDef: String, CaseIterable { public var data: [String: Any] = [:] public init() { UserDef.allCases.forEach { - data[$0.rawValue] = UserDefaults.standard.object(forKey: $0.rawValue) + data[$0.rawValue] = UserDefaults.current.object(forKey: $0.rawValue) } } } @@ -310,7 +310,7 @@ public enum CandidateKey { } public func vCLog(_ strPrint: StringLiteralType) { - if UserDefaults.standard.bool(forKey: "_DebugMode") { + if UserDefaults.current.bool(forKey: "_DebugMode") { NSLog("vChewingDebug: %@", strPrint) } } diff --git a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift index fc738707..cbadb025 100644 --- a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift +++ b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift @@ -119,6 +119,19 @@ public extension Bool { } } +// MARK: - User Defaults Storage + +public extension UserDefaults { + // 內部標記,看輸入法是否處於測試模式。 + static var pendingUnitTests = false + + static var unitTests = UserDefaults(suiteName: "UnitTests") + + static var current: UserDefaults { + pendingUnitTests ? .unitTests ?? .standard : .standard + } +} + // MARK: - Property Wrapper // Ref: https://www.avanderlee.com/swift/property-wrappers/ @@ -127,7 +140,7 @@ public extension Bool { public struct AppProperty { public let key: String public let defaultValue: Value - public var container: UserDefaults = .standard + public var container: UserDefaults { .current } public init(key: String, defaultValue: Value) { self.key = key self.defaultValue = defaultValue