Quellcode durchsuchen

Support Mac Catalyst

Kishikawa Katsumi vor 5 Jahren
Ursprung
Commit
9923b1ba09
27 geänderte Dateien mit 1110 neuen und 43 gelöschten Zeilen
  1. 3 0
      .travis.yml
  2. 3 0
      KeychainAccess.xcworkspace/contents.xcworkspacedata
  3. 2 0
      Lib/Configurations/KeychainAccess.xcconfig
  4. 1 0
      Lib/Configurations/TestHost.xcconfig
  5. 2 0
      Lib/Configurations/Tests.xcconfig
  6. 10 0
      Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/KeychainAccess.xcscheme
  7. 29 16
      Lib/KeychainAccess/Keychain.swift
  8. 4 1
      Lib/KeychainAccessTests/EnumTests.swift
  9. 0 1
      Lib/KeychainAccessTests/ErrorTypeTests.swift
  10. 106 20
      Lib/KeychainAccessTests/KeychainAccessTests.swift
  11. 0 1
      Lib/KeychainAccessTests/SharedCredentialTests.swift
  12. 1 0
      Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/EnumTests.swift
  13. 1 0
      Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/ErrorTypeTests.swift
  14. 22 0
      Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/Info.plist
  15. 1 0
      Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/KeychainAccessTests.swift
  16. 516 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/project.pbxproj
  17. 78 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/xcshareddata/xcschemes/TestHost-MacCatalyst.xcscheme
  18. 37 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/AppDelegate.swift
  19. 98 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/AppIcon.appiconset/Contents.json
  20. 6 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/Contents.json
  21. 25 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/LaunchScreen.storyboard
  22. 24 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/Main.storyboard
  23. 64 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Info.plist
  24. 30 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/SceneDelegate.swift
  25. 15 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements
  26. 32 0
      Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/ViewController.swift
  27. 0 4
      Lib/TestHost/AppDelegate.swift

+ 3 - 0
.travis.yml

@@ -21,6 +21,9 @@ matrix:
     env: ACTION='test:appletvsimulator'
   - osx_image: xcode11
     env: ACTION='test:macosx'
+  # Mac Catalyst requires macOS Catalina. But no CI service supports running tests on Catalina yet.
+  # - osx_image: xcode11
+  #   script: xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -configuration Debug -enableCodeCoverage YES -derivedDataPath build -hideShellScriptEnvironment -destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst' -only-testing:KeychainAccessTests-MacCatalyst SWIFT_VERSION=5.1 GCC_SYMBOLS_PRIVATE_EXTERN=NO
 
   - osx_image: xcode10.3
     env: ACTION=build

+ 3 - 0
KeychainAccess.xcworkspace/contents.xcworkspacedata

@@ -4,6 +4,9 @@
    <FileRef
       location = "group:Lib/KeychainAccess.xcodeproj">
    </FileRef>
+   <FileRef
+      location = "group:Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj">
+   </FileRef>
    <FileRef
       location = "group:Examples/Example-iOS/Example-iOS.xcodeproj">
    </FileRef>

+ 2 - 0
Lib/Configurations/KeychainAccess.xcconfig

@@ -1,5 +1,7 @@
 SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator;
 TARGETED_DEVICE_FAMILY = 1,2,3,4;
+SUPPORTS_MACCATALYST = YES;
+DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
 
 COMBINE_HIDPI_IMAGES = YES;
 PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.$(PLATFORM_NAME).$(PRODUCT_NAME:rfc1034identifier);

+ 1 - 0
Lib/Configurations/TestHost.xcconfig

@@ -1,5 +1,6 @@
 SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator;
 TARGETED_DEVICE_FAMILY = 1,2,3;
+SUPPORTS_MACCATALYST = YES;
 
 COMBINE_HIDPI_IMAGES = YES;
 COPY_PHASE_STRIP = NO;

+ 2 - 0
Lib/Configurations/Tests.xcconfig

@@ -1,4 +1,6 @@
 SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator;
+TARGETED_DEVICE_FAMILY = 1,2;
+SUPPORTS_MACCATALYST = YES;
 
 COMBINE_HIDPI_IMAGES = YES;
 PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier);

+ 10 - 0
Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/KeychainAccess.xcscheme

@@ -61,6 +61,16 @@
                ReferencedContainer = "container:KeychainAccess.xcodeproj">
             </BuildableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "14A98C79235D292000BBB893"
+               BuildableName = "KeychainAccessTests-MacCatalyst.xctest"
+               BlueprintName = "KeychainAccessTests-MacCatalyst"
+               ReferencedContainer = "container:TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
       </Testables>
    </TestAction>
    <LaunchAction

+ 29 - 16
Lib/KeychainAccess/Keychain.swift

@@ -106,6 +106,7 @@ public enum Accessibility {
      for anything except system use. Items with this attribute will migrate
      to a new device when using encrypted backups.
      */
+    @available(macCatalyst, unavailable)
     case always
 
     /**
@@ -148,6 +149,7 @@ public enum Accessibility {
      attribute will never migrate to a new device, so after a backup is
      restored to a new device, these items will be missing.
      */
+    @available(macCatalyst, unavailable)
     case alwaysThisDeviceOnly
 }
 
@@ -890,7 +892,7 @@ public final class Keychain {
         return type(of: self).prettify(itemClass: itemClass, items: items())
     }
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public func getSharedPassword(_ completion: @escaping (_ account: String?, _ password: String?, _ error: Error?) -> () = { account, password, error -> () in }) {
         if let domain = server.host {
@@ -910,7 +912,7 @@ public final class Keychain {
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public func getSharedPassword(_ account: String, completion: @escaping (_ password: String?, _ error: Error?) -> () = { password, error -> () in }) {
         if let domain = server.host {
@@ -932,14 +934,14 @@ public final class Keychain {
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public func setSharedPassword(_ password: String, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) {
         setSharedPassword(password as String?, account: account, completion: completion)
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     fileprivate func setSharedPassword(_ password: String?, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) {
         if let domain = server.host {
@@ -957,35 +959,35 @@ public final class Keychain {
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public func removeSharedPassword(_ account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) {
         setSharedPassword(nil, account: account, completion: completion)
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public class func requestSharedWebCredential(_ completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) {
         requestSharedWebCredential(domain: nil, account: nil, completion: completion)
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public class func requestSharedWebCredential(domain: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) {
         requestSharedWebCredential(domain: domain, account: nil, completion: completion)
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     public class func requestSharedWebCredential(domain: String, account: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) {
         requestSharedWebCredential(domain: Optional(domain), account: Optional(account)!, completion: completion)
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     @available(iOS 8.0, *)
     fileprivate class func requestSharedWebCredential(domain: String?, account: String?, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> ()) {
         SecRequestSharedWebCredential(domain as CFString?, account as CFString?) { (credentials, error) -> () in
@@ -1020,7 +1022,7 @@ public final class Keychain {
     }
     #endif
 
-    #if os(iOS)
+    #if os(iOS) && !targetEnvironment(macCatalyst)
     /**
      @abstract Returns a randomly generated password.
      @return String password in the form xxx-xxx-xxx-xxx where x is taken from the sets "abcdefghkmnopqrstuvwxy", "ABCDEFGHJKLMNPQRSTUVWXYZ", "3456789" with at least one character from each set being present.
@@ -1229,7 +1231,7 @@ private let UseAuthenticationUIFail = String(kSecUseAuthenticationUIFail)
 @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *)
 private let UseAuthenticationUISkip = String(kSecUseAuthenticationUISkip)
 
-#if os(iOS)
+#if os(iOS) && !targetEnvironment(macCatalyst)
 /** Credential Key Constants */
 private let SharedPassword = String(kSecSharedPassword)
 #endif
@@ -1255,7 +1257,6 @@ extension Keychain: CustomStringConvertible, CustomDebugStringConvertible {
 }
 
 extension Options {
-
     func query(ignoringAttributeSynchronizable: Bool = true) -> [String: Any] {
         var query = [String: Any]()
 
@@ -1352,7 +1353,6 @@ extension Attributes: CustomStringConvertible, CustomDebugStringConvertible {
 }
 
 extension ItemClass: RawRepresentable, CustomStringConvertible {
-
     public init?(rawValue: String) {
         switch rawValue {
         case String(kSecClassGenericPassword):
@@ -1384,7 +1384,6 @@ extension ItemClass: RawRepresentable, CustomStringConvertible {
 }
 
 extension ProtocolType: RawRepresentable, CustomStringConvertible {
-
     public init?(rawValue: String) {
         switch rawValue {
         case String(kSecAttrProtocolFTP):
@@ -1590,7 +1589,6 @@ extension ProtocolType: RawRepresentable, CustomStringConvertible {
 }
 
 extension AuthenticationType: RawRepresentable, CustomStringConvertible {
-
     public init?(rawValue: String) {
         switch rawValue {
         case String(kSecAttrAuthenticationTypeNTLM):
@@ -1658,7 +1656,6 @@ extension AuthenticationType: RawRepresentable, CustomStringConvertible {
 }
 
 extension Accessibility: RawRepresentable, CustomStringConvertible {
-
     public init?(rawValue: String) {
         if #available(OSX 10.10, *) {
             switch rawValue {
@@ -1666,16 +1663,20 @@ extension Accessibility: RawRepresentable, CustomStringConvertible {
                 self = .whenUnlocked
             case String(kSecAttrAccessibleAfterFirstUnlock):
                 self = .afterFirstUnlock
+            #if !targetEnvironment(macCatalyst)
             case String(kSecAttrAccessibleAlways):
                 self = .always
+            #endif
             case String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly):
                 self = .whenPasscodeSetThisDeviceOnly
             case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly):
                 self = .whenUnlockedThisDeviceOnly
             case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly):
                 self = .afterFirstUnlockThisDeviceOnly
+            #if !targetEnvironment(macCatalyst)
             case String(kSecAttrAccessibleAlwaysThisDeviceOnly):
                 self = .alwaysThisDeviceOnly
+            #endif
             default:
                 return nil
             }
@@ -1685,14 +1686,18 @@ extension Accessibility: RawRepresentable, CustomStringConvertible {
                 self = .whenUnlocked
             case String(kSecAttrAccessibleAfterFirstUnlock):
                 self = .afterFirstUnlock
+            #if !targetEnvironment(macCatalyst)
             case String(kSecAttrAccessibleAlways):
                 self = .always
+            #endif
             case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly):
                 self = .whenUnlockedThisDeviceOnly
             case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly):
                 self = .afterFirstUnlockThisDeviceOnly
+            #if !targetEnvironment(macCatalyst)
             case String(kSecAttrAccessibleAlwaysThisDeviceOnly):
                 self = .alwaysThisDeviceOnly
+            #endif
             default:
                 return nil
             }
@@ -1705,8 +1710,10 @@ extension Accessibility: RawRepresentable, CustomStringConvertible {
             return String(kSecAttrAccessibleWhenUnlocked)
         case .afterFirstUnlock:
             return String(kSecAttrAccessibleAfterFirstUnlock)
+        #if !targetEnvironment(macCatalyst)
         case .always:
             return String(kSecAttrAccessibleAlways)
+        #endif
         case .whenPasscodeSetThisDeviceOnly:
             if #available(OSX 10.10, *) {
                 return String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)
@@ -1717,8 +1724,10 @@ extension Accessibility: RawRepresentable, CustomStringConvertible {
             return String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
         case .afterFirstUnlockThisDeviceOnly:
             return String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
+        #if !targetEnvironment(macCatalyst)
         case .alwaysThisDeviceOnly:
             return String(kSecAttrAccessibleAlwaysThisDeviceOnly)
+        #endif
         }
     }
 
@@ -1728,16 +1737,20 @@ extension Accessibility: RawRepresentable, CustomStringConvertible {
             return "WhenUnlocked"
         case .afterFirstUnlock:
             return "AfterFirstUnlock"
+        #if !targetEnvironment(macCatalyst)
         case .always:
             return "Always"
+        #endif
         case .whenPasscodeSetThisDeviceOnly:
             return "WhenPasscodeSetThisDeviceOnly"
         case .whenUnlockedThisDeviceOnly:
             return "WhenUnlockedThisDeviceOnly"
         case .afterFirstUnlockThisDeviceOnly:
             return "AfterFirstUnlockThisDeviceOnly"
+        #if !targetEnvironment(macCatalyst)
         case .alwaysThisDeviceOnly:
             return "AlwaysThisDeviceOnly"
+        #endif
         }
     }
 }

+ 4 - 1
Lib/KeychainAccessTests/EnumTests.swift

@@ -27,7 +27,6 @@ import XCTest
 import KeychainAccess
 
 class EnumTests: XCTestCase {
-
     override func setUp() {
         super.setUp()
     }
@@ -276,11 +275,13 @@ class EnumTests: XCTestCase {
             XCTAssertEqual(accessibility, .afterFirstUnlock)
             XCTAssertEqual(accessibility?.description, "AfterFirstUnlock")
         }
+        #if !targetEnvironment(macCatalyst)
         do {
             let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlways as String)
             XCTAssertEqual(accessibility, .always)
             XCTAssertEqual(accessibility?.description, "Always")
         }
+        #endif
         do {
             let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as String)
             XCTAssertEqual(accessibility, .whenPasscodeSetThisDeviceOnly)
@@ -296,10 +297,12 @@ class EnumTests: XCTestCase {
             XCTAssertEqual(accessibility, .afterFirstUnlockThisDeviceOnly)
             XCTAssertEqual(accessibility?.description, "AfterFirstUnlockThisDeviceOnly")
         }
+        #if !targetEnvironment(macCatalyst)
         do {
             let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlwaysThisDeviceOnly as String)
             XCTAssertEqual(accessibility, .alwaysThisDeviceOnly)
             XCTAssertEqual(accessibility?.description, "AlwaysThisDeviceOnly")
         }
+        #endif
     }
 }

+ 0 - 1
Lib/KeychainAccessTests/ErrorTypeTests.swift

@@ -27,7 +27,6 @@ import XCTest
 import KeychainAccess
 
 class ErrorTypeTests: XCTestCase {
-
     override func setUp() {
         super.setUp()
     }

+ 106 - 20
Lib/KeychainAccessTests/KeychainAccessTests.swift

@@ -25,11 +25,9 @@
 
 import Foundation
 import XCTest
-
 import KeychainAccess
 
 class KeychainAccessTests: XCTestCase {
-
     override func setUp() {
         super.setUp()
 
@@ -458,7 +456,13 @@ class KeychainAccessTests: XCTestCase {
     func testDefaultInitializer() {
         let keychain = Keychain()
         XCTAssertEqual(keychain.service, Bundle.main.bundleIdentifier)
-        XCTAssertEqual(keychain.service, "com.kishikawakatsumi.KeychainAccess.TestHost")
+        let service: String
+        #if targetEnvironment(macCatalyst)
+        service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+        #else
+        service = "com.kishikawakatsumi.KeychainAccess.TestHost"
+        #endif
+        XCTAssertEqual(keychain.service, service)
         XCTAssertNil(keychain.accessGroup)
     }
 
@@ -470,7 +474,13 @@ class KeychainAccessTests: XCTestCase {
 
     func testInitializerWithAccessGroup() {
         let keychain = Keychain(accessGroup: "27AEDK3C9F.shared")
-        XCTAssertEqual(keychain.service, "com.kishikawakatsumi.KeychainAccess.TestHost")
+        let service: String
+        #if targetEnvironment(macCatalyst)
+        service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+        #else
+        service = "com.kishikawakatsumi.KeychainAccess.TestHost"
+        #endif
+        XCTAssertEqual(keychain.service, service)
         XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared")
     }
 
@@ -745,12 +755,22 @@ class KeychainAccessTests: XCTestCase {
                 XCTAssertNil(attributes?.ref)
                 XCTAssertNotNil(attributes?.persistentRef)
                 XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue)
+                #if targetEnvironment(macCatalyst)
+                XCTAssertNotNil(attributes?.accessControl)
+                #else
                 if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 11, minorVersion: 3, patchVersion: 0)) {
                     XCTAssertNotNil(attributes?.accessControl)
                 } else {
                     XCTAssertNil(attributes?.accessControl)
                 }
-                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+                #endif
+                let accessGroup: String
+                #if targetEnvironment(macCatalyst)
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+                #else
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost"
+                #endif
+                XCTAssertEqual(attributes?.accessGroup, accessGroup)
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -826,7 +846,13 @@ class KeychainAccessTests: XCTestCase {
                     XCTAssertNil(attributes?.accessControl)
                 }
                 #endif
-                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+                let accessGroup: String
+                #if targetEnvironment(macCatalyst)
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+                #else
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost"
+                #endif
+                XCTAssertEqual(attributes?.accessGroup, accessGroup)
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -878,7 +904,13 @@ class KeychainAccessTests: XCTestCase {
                     XCTAssertNil(attributes?.accessControl)
                 }
                 #endif
-                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+                let accessGroup: String
+                #if targetEnvironment(macCatalyst)
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+                #else
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost"
+                #endif
+                XCTAssertEqual(attributes?.accessGroup, accessGroup)
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -931,7 +963,13 @@ class KeychainAccessTests: XCTestCase {
                     XCTAssertNil(attributes?.accessControl)
                 }
                 #endif
-                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+                let accessGroup: String
+                #if targetEnvironment(macCatalyst)
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+                #else
+                accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost"
+                #endif
+                XCTAssertEqual(attributes?.accessGroup, accessGroup)
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -1320,25 +1358,36 @@ class KeychainAccessTests: XCTestCase {
             }
 
             #if !os(OSX)
-            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+
+            let service: String
+            let accessGroup: String
+            #if targetEnvironment(macCatalyst)
+            service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+            accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+            #else
+            service = "com.kishikawakatsumi.KeychainAccess.TestHost"
+            accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost"
+            #endif
+
+            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup)
             XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false")
-            XCTAssertEqual(sortedItems[0]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedItems[0]["service"] as? String, service)
             XCTAssertEqual(sortedItems[0]["value"] as? String, "value1")
             XCTAssertEqual(sortedItems[0]["key"] as? String, "key1")
             XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlock")
 
-            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup)
             XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false")
-            XCTAssertEqual(sortedItems[1]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedItems[1]["service"] as? String, service)
             XCTAssertEqual(sortedItems[1]["value"] as? String, "value2")
             XCTAssertEqual(sortedItems[1]["key"] as? String, "key2")
             XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock")
 
-            XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, accessGroup)
             XCTAssertEqual(sortedItems[2]["synchronizable"] as? String, "false")
-            XCTAssertEqual(sortedItems[2]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedItems[2]["service"] as? String, service)
             XCTAssertEqual(sortedItems[2]["value"] as? String, "value3")
             XCTAssertEqual(sortedItems[2]["key"] as? String, "key3")
             XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword")
@@ -1383,7 +1432,15 @@ class KeychainAccessTests: XCTestCase {
             }
 
             #if !os(OSX)
-            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+
+            let accessGroup: String
+            #if targetEnvironment(macCatalyst)
+            accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+            #else
+            accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost"
+            #endif
+
+            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup)
             XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "true")
             XCTAssertEqual(sortedItems[0]["service"] as? String, "service1")
             XCTAssertEqual(sortedItems[0]["value"] as? String, "service1_value1")
@@ -1391,7 +1448,7 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "WhenUnlockedThisDeviceOnly")
 
-            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup)
             XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false")
             XCTAssertEqual(sortedItems[1]["service"] as? String, "service1")
             XCTAssertEqual(sortedItems[1]["value"] as? String, "service1_value2")
@@ -1410,15 +1467,29 @@ class KeychainAccessTests: XCTestCase {
         }
         do {
             let keychain = Keychain(server: "https://google.com", protocolType: .https)
+            #if !targetEnvironment(macCatalyst)
             try! keychain
                 .synchronizable(false)
                 .accessibility(.alwaysThisDeviceOnly)
                 .set("google.com_value1", key: "google.com_key1")
+            #else
+            try! keychain
+                .synchronizable(false)
+                .accessibility(.afterFirstUnlockThisDeviceOnly)
+                .set("google.com_value1", key: "google.com_key1")
+            #endif
 
+            #if !targetEnvironment(macCatalyst)
             try! keychain
                 .synchronizable(true)
                 .accessibility(.always)
                 .set("google.com_value2", key: "google.com_key2")
+            #else
+            try! keychain
+                .synchronizable(true)
+                .accessibility(.afterFirstUnlock)
+                .set("google.com_value2", key: "google.com_key2")
+            #endif
 
             let allKeys = keychain.allKeys()
             XCTAssertEqual(allKeys.count, 2)
@@ -1441,8 +1512,11 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword")
             XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default")
             XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS")
+            #if targetEnvironment(macCatalyst)
+            XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly")
+            #else
             XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AlwaysThisDeviceOnly")
-
+            #endif
             XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "true")
             XCTAssertEqual(sortedItems[1]["value"] as? String, "google.com_value2")
             XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2")
@@ -1450,7 +1524,11 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword")
             XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default")
             XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS")
+            #if targetEnvironment(macCatalyst)
+            XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock")
+            #else
             XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "Always")
+            #endif
             #else
             XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1")
             XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com")
@@ -1474,11 +1552,19 @@ class KeychainAccessTests: XCTestCase {
             let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in
                 return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame
             }
-            XCTAssertEqual(sortedKeys[0].0, "com.kishikawakatsumi.KeychainAccess.TestHost")
+
+            let service: String
+            #if targetEnvironment(macCatalyst)
+            service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"
+            #else
+            service = "com.kishikawakatsumi.KeychainAccess.TestHost"
+            #endif
+
+            XCTAssertEqual(sortedKeys[0].0, service)
             XCTAssertEqual(sortedKeys[0].1, "key1")
-            XCTAssertEqual(sortedKeys[1].0, "com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedKeys[1].0, service)
             XCTAssertEqual(sortedKeys[1].1, "key2")
-            XCTAssertEqual(sortedKeys[2].0, "com.kishikawakatsumi.KeychainAccess.TestHost")
+            XCTAssertEqual(sortedKeys[2].0, service)
             XCTAssertEqual(sortedKeys[2].1, "key3")
             XCTAssertEqual(sortedKeys[3].0, "service1")
             XCTAssertEqual(sortedKeys[3].1, "service1_key1")

+ 0 - 1
Lib/KeychainAccessTests/SharedCredentialTests.swift

@@ -27,7 +27,6 @@ import XCTest
 import KeychainAccess
 
 class SharedCredentialTests: XCTestCase {
-
     override func setUp() {
         super.setUp()
     }

+ 1 - 0
Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/EnumTests.swift

@@ -0,0 +1 @@
+../../KeychainAccessTests/EnumTests.swift

+ 1 - 0
Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/ErrorTypeTests.swift

@@ -0,0 +1 @@
+../../KeychainAccessTests/ErrorTypeTests.swift

+ 22 - 0
Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/Info.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 1 - 0
Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/KeychainAccessTests.swift

@@ -0,0 +1 @@
+../../KeychainAccessTests/KeychainAccessTests.swift

+ 516 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/project.pbxproj

@@ -0,0 +1,516 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 50;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		14A98C4F235D284F00BBB893 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C4E235D284F00BBB893 /* AppDelegate.swift */; };
+		14A98C51235D284F00BBB893 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C50235D284F00BBB893 /* SceneDelegate.swift */; };
+		14A98C53235D284F00BBB893 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C52235D284F00BBB893 /* ViewController.swift */; };
+		14A98C56235D284F00BBB893 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C54235D284F00BBB893 /* Main.storyboard */; };
+		14A98C58235D285000BBB893 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C57235D285000BBB893 /* Assets.xcassets */; };
+		14A98C5B235D285000BBB893 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */; };
+		14A98C65235D286B00BBB893 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14A98C64235D286B00BBB893 /* KeychainAccess.framework */; };
+		14A98C66235D286B00BBB893 /* KeychainAccess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 14A98C64235D286B00BBB893 /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		14A98C88235D2AFA00BBB893 /* ErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */; };
+		14A98C8A235D2AFA00BBB893 /* KeychainAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */; };
+		14A98C8B235D2AFA00BBB893 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C87235D2AFA00BBB893 /* EnumTests.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		14A98C82235D293300BBB893 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 14A98C43235D284F00BBB893 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 14A98C4A235D284F00BBB893;
+			remoteInfo = "TestHost-MacCatalyst";
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		14A98C67235D286B00BBB893 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				14A98C66235D286B00BBB893 /* KeychainAccess.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TestHost-MacCatalyst.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		14A98C4E235D284F00BBB893 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		14A98C50235D284F00BBB893 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
+		14A98C52235D284F00BBB893 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
+		14A98C55235D284F00BBB893 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		14A98C57235D285000BBB893 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		14A98C5A235D285000BBB893 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		14A98C5C235D285000BBB893 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		14A98C62235D286500BBB893 /* TestHost-MacCatalyst.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "TestHost-MacCatalyst.entitlements"; sourceTree = "<group>"; };
+		14A98C64235D286B00BBB893 /* KeychainAccess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeychainAccess.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KeychainAccessTests-MacCatalyst.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		14A98C7E235D292000BBB893 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTypeTests.swift; sourceTree = "<group>"; };
+		14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainAccessTests.swift; sourceTree = "<group>"; };
+		14A98C87235D2AFA00BBB893 /* EnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		14A98C48235D284F00BBB893 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14A98C65235D286B00BBB893 /* KeychainAccess.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		14A98C77235D292000BBB893 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		14A98C42235D284F00BBB893 = {
+			isa = PBXGroup;
+			children = (
+				14A98C4D235D284F00BBB893 /* TestHost-MacCatalyst */,
+				14A98C7B235D292000BBB893 /* KeychainAccessTests-MacCatalyst */,
+				14A98C4C235D284F00BBB893 /* Products */,
+				14A98C63235D286B00BBB893 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		14A98C4C235D284F00BBB893 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */,
+				14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		14A98C4D235D284F00BBB893 /* TestHost-MacCatalyst */ = {
+			isa = PBXGroup;
+			children = (
+				14A98C4E235D284F00BBB893 /* AppDelegate.swift */,
+				14A98C50235D284F00BBB893 /* SceneDelegate.swift */,
+				14A98C52235D284F00BBB893 /* ViewController.swift */,
+				14A98C54235D284F00BBB893 /* Main.storyboard */,
+				14A98C59235D285000BBB893 /* LaunchScreen.storyboard */,
+				14A98C57235D285000BBB893 /* Assets.xcassets */,
+				14A98C5C235D285000BBB893 /* Info.plist */,
+				14A98C62235D286500BBB893 /* TestHost-MacCatalyst.entitlements */,
+			);
+			path = "TestHost-MacCatalyst";
+			sourceTree = "<group>";
+		};
+		14A98C63235D286B00BBB893 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				14A98C64235D286B00BBB893 /* KeychainAccess.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		14A98C7B235D292000BBB893 /* KeychainAccessTests-MacCatalyst */ = {
+			isa = PBXGroup;
+			children = (
+				14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */,
+				14A98C87235D2AFA00BBB893 /* EnumTests.swift */,
+				14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */,
+				14A98C7E235D292000BBB893 /* Info.plist */,
+			);
+			path = "KeychainAccessTests-MacCatalyst";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 14A98C5F235D285000BBB893 /* Build configuration list for PBXNativeTarget "TestHost-MacCatalyst" */;
+			buildPhases = (
+				14A98C47235D284F00BBB893 /* Sources */,
+				14A98C48235D284F00BBB893 /* Frameworks */,
+				14A98C49235D284F00BBB893 /* Resources */,
+				14A98C67235D286B00BBB893 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "TestHost-MacCatalyst";
+			productName = "TestHost-MacCatalyst";
+			productReference = 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */;
+			productType = "com.apple.product-type.application";
+		};
+		14A98C79235D292000BBB893 /* KeychainAccessTests-MacCatalyst */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 14A98C7F235D292000BBB893 /* Build configuration list for PBXNativeTarget "KeychainAccessTests-MacCatalyst" */;
+			buildPhases = (
+				14A98C76235D292000BBB893 /* Sources */,
+				14A98C77235D292000BBB893 /* Frameworks */,
+				14A98C78235D292000BBB893 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				14A98C83235D293300BBB893 /* PBXTargetDependency */,
+			);
+			name = "KeychainAccessTests-MacCatalyst";
+			productName = "KeychainAccessTests-MacCatalyst";
+			productReference = 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		14A98C43235D284F00BBB893 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 1100;
+				LastUpgradeCheck = 1100;
+				ORGANIZATIONNAME = "Kishikawa Katsumi";
+				TargetAttributes = {
+					14A98C4A235D284F00BBB893 = {
+						CreatedOnToolsVersion = 11.0;
+					};
+					14A98C79235D292000BBB893 = {
+						CreatedOnToolsVersion = 11.0;
+						LastSwiftMigration = 1100;
+						TestTargetID = 14A98C4A235D284F00BBB893;
+					};
+				};
+			};
+			buildConfigurationList = 14A98C46235D284F00BBB893 /* Build configuration list for PBXProject "TestHost-MacCatalyst" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 14A98C42235D284F00BBB893;
+			productRefGroup = 14A98C4C235D284F00BBB893 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */,
+				14A98C79235D292000BBB893 /* KeychainAccessTests-MacCatalyst */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		14A98C49235D284F00BBB893 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14A98C5B235D285000BBB893 /* LaunchScreen.storyboard in Resources */,
+				14A98C58235D285000BBB893 /* Assets.xcassets in Resources */,
+				14A98C56235D284F00BBB893 /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		14A98C78235D292000BBB893 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		14A98C47235D284F00BBB893 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14A98C53235D284F00BBB893 /* ViewController.swift in Sources */,
+				14A98C4F235D284F00BBB893 /* AppDelegate.swift in Sources */,
+				14A98C51235D284F00BBB893 /* SceneDelegate.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		14A98C76235D292000BBB893 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14A98C88235D2AFA00BBB893 /* ErrorTypeTests.swift in Sources */,
+				14A98C8B235D2AFA00BBB893 /* EnumTests.swift in Sources */,
+				14A98C8A235D2AFA00BBB893 /* KeychainAccessTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		14A98C83235D293300BBB893 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */;
+			targetProxy = 14A98C82235D293300BBB893 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		14A98C54235D284F00BBB893 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				14A98C55235D284F00BBB893 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		14A98C59235D285000BBB893 /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				14A98C5A235D285000BBB893 /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		14A98C5D235D285000BBB893 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		14A98C5E235D285000BBB893 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				MTL_FAST_MATH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		14A98C60235D285000BBB893 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_ENTITLEMENTS = "TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements";
+				CODE_SIGN_STYLE = Automatic;
+				DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
+				DEVELOPMENT_TEAM = 27AEDK3C9F;
+				INFOPLIST_FILE = "TestHost-MacCatalyst/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SUPPORTS_MACCATALYST = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		14A98C61235D285000BBB893 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_ENTITLEMENTS = "TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements";
+				CODE_SIGN_STYLE = Automatic;
+				DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
+				DEVELOPMENT_TEAM = 27AEDK3C9F;
+				INFOPLIST_FILE = "TestHost-MacCatalyst/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SUPPORTS_MACCATALYST = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+		14A98C80235D292000BBB893 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = 27AEDK3C9F;
+				INFOPLIST_FILE = "KeychainAccessTests-MacCatalyst/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccessTests-MacCatalyst";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHost-MacCatalyst.app/TestHost-MacCatalyst";
+			};
+			name = Debug;
+		};
+		14A98C81235D292000BBB893 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = 27AEDK3C9F;
+				INFOPLIST_FILE = "KeychainAccessTests-MacCatalyst/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccessTests-MacCatalyst";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHost-MacCatalyst.app/TestHost-MacCatalyst";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		14A98C46235D284F00BBB893 /* Build configuration list for PBXProject "TestHost-MacCatalyst" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				14A98C5D235D285000BBB893 /* Debug */,
+				14A98C5E235D285000BBB893 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		14A98C5F235D285000BBB893 /* Build configuration list for PBXNativeTarget "TestHost-MacCatalyst" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				14A98C60235D285000BBB893 /* Debug */,
+				14A98C61235D285000BBB893 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		14A98C7F235D292000BBB893 /* Build configuration list for PBXNativeTarget "KeychainAccessTests-MacCatalyst" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				14A98C80235D292000BBB893 /* Debug */,
+				14A98C81235D292000BBB893 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 14A98C43235D284F00BBB893 /* Project object */;
+}

+ 78 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/xcshareddata/xcschemes/TestHost-MacCatalyst.xcscheme

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1100"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "14A98C4A235D284F00BBB893"
+               BuildableName = "TestHost-MacCatalyst.app"
+               BlueprintName = "TestHost-MacCatalyst"
+               ReferencedContainer = "container:TestHost-MacCatalyst.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "14A98C4A235D284F00BBB893"
+            BuildableName = "TestHost-MacCatalyst.app"
+            BlueprintName = "TestHost-MacCatalyst"
+            ReferencedContainer = "container:TestHost-MacCatalyst.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "14A98C4A235D284F00BBB893"
+            BuildableName = "TestHost-MacCatalyst.app"
+            BlueprintName = "TestHost-MacCatalyst"
+            ReferencedContainer = "container:TestHost-MacCatalyst.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 37 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/AppDelegate.swift

@@ -0,0 +1,37 @@
+//
+//  AppDelegate.swift
+//  TestHost-MacCatalyst
+//
+//  Created by Kishikawa Katsumi on 2019/10/21.
+//  Copyright © 2019 Kishikawa Katsumi. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+        return true
+    }
+
+    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
+    }
+}

+ 98 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,98 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "83.5x83.5",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "size" : "1024x1024",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 6 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 25 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/LaunchScreen.storyboard

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>

+ 24 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/Main.storyboard

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>

+ 64 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Info.plist

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UIApplicationSceneManifest</key>
+	<dict>
+		<key>UIApplicationSupportsMultipleScenes</key>
+		<false/>
+		<key>UISceneConfigurations</key>
+		<dict>
+			<key>UIWindowSceneSessionRoleApplication</key>
+			<array>
+				<dict>
+					<key>UISceneConfigurationName</key>
+					<string>Default Configuration</string>
+					<key>UISceneDelegateClassName</key>
+					<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
+					<key>UISceneStoryboardFile</key>
+					<string>Main</string>
+				</dict>
+			</array>
+		</dict>
+	</dict>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>

+ 30 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/SceneDelegate.swift

@@ -0,0 +1,30 @@
+//
+//  SceneDelegate.swift
+//  TestHost-MacCatalyst
+//
+//  Created by Kishikawa Katsumi on 2019/10/21.
+//  Copyright © 2019 Kishikawa Katsumi. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import UIKit
+
+class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+    var window: UIWindow?
+}

+ 15 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
+	<key>com.apple.security.network.client</key>
+	<true/>
+	<key>keychain-access-groups</key>
+	<array>
+		<string>$(AppIdentifierPrefix)com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst</string>
+		<string>$(AppIdentifierPrefix)shared</string>
+	</array>
+</dict>
+</plist>

+ 32 - 0
Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/ViewController.swift

@@ -0,0 +1,32 @@
+//
+//  ViewController.swift
+//  TestHost-MacCatalyst
+//
+//  Created by Kishikawa Katsumi on 2019/10/21.
+//  Copyright © 2019 Kishikawa Katsumi. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import UIKit
+
+class ViewController: UIViewController {
+    override func viewDidLoad() {
+        super.viewDidLoad()
+    }
+}

+ 0 - 4
Lib/TestHost/AppDelegate.swift

@@ -28,18 +28,14 @@ import Cocoa
 
 @NSApplicationMain
 class AppDelegate: NSObject, NSApplicationDelegate {
-
     @IBOutlet weak var window: NSWindow!
-
     func applicationDidFinishLaunching(aNotification: NSNotification) {}
-
 }
 #else
 import UIKit
 
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate {
-
     var window: UIWindow?
 
     #if swift(>=4.2)