diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index ae0a21d9..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "Frameworks/Haxcessibility"]
- path = Frameworks/Haxcessibility
- url = git://github.com/numist/Haxcessibility.git
-[submodule "Frameworks/ReactiveCocoa"]
- path = Frameworks/ReactiveCocoa
- url = git://github.com/numist/ReactiveCocoa.git
diff --git a/.ruby-version b/.ruby-version
deleted file mode 100644
index 93045150..00000000
--- a/.ruby-version
+++ /dev/null
@@ -1 +0,0 @@
-ruby-2.1.0
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 23354af4..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-language: objective-c
-xcode_project: Switch.xcodeproj
-xcode_scheme: Switch
-osx_image: xcode9
-
-before_script:
- - export LANG=en_US.UTF-8
-
-install:
- - gem install rake
- - rake deps
-
-script:
- - rake app
- - rake test
diff --git a/Dependencies/Defaults.gitcheckout b/Dependencies/Defaults.gitcheckout
new file mode 100644
index 00000000..b377c8b7
--- /dev/null
+++ b/Dependencies/Defaults.gitcheckout
@@ -0,0 +1 @@
+master:93fbdf7f09a0887ca49d35752ebc31735f987c7a
diff --git a/Dependencies/Defaults.giturl b/Dependencies/Defaults.giturl
new file mode 100644
index 00000000..e7b808a9
--- /dev/null
+++ b/Dependencies/Defaults.giturl
@@ -0,0 +1 @@
+git@github.com:sindresorhus/Defaults.git
\ No newline at end of file
diff --git a/Dependencies/Defaults/.editorconfig b/Dependencies/Defaults/.editorconfig
new file mode 100644
index 00000000..aaac3258
--- /dev/null
+++ b/Dependencies/Defaults/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/Dependencies/Defaults/.gitattributes b/Dependencies/Defaults/.gitattributes
new file mode 100644
index 00000000..6313b56c
--- /dev/null
+++ b/Dependencies/Defaults/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/Dependencies/Defaults/.github/funding.yml b/Dependencies/Defaults/.github/funding.yml
new file mode 100644
index 00000000..15edf6e2
--- /dev/null
+++ b/Dependencies/Defaults/.github/funding.yml
@@ -0,0 +1,4 @@
+github: sindresorhus
+open_collective: sindresorhus
+patreon: sindresorhus
+custom: https://sindresorhus.com/donate
diff --git a/Dependencies/Defaults/.gitignore b/Dependencies/Defaults/.gitignore
new file mode 100644
index 00000000..0854237e
--- /dev/null
+++ b/Dependencies/Defaults/.gitignore
@@ -0,0 +1,4 @@
+/.build
+/Packages
+xcuserdata
+project.xcworkspace
diff --git a/Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Dependencies/Defaults/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
similarity index 72%
rename from Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename to Dependencies/Defaults/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
index 911cb93c..919434a6 100644
--- a/Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/Dependencies/Defaults/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/Dependencies/Defaults/.travis.yml b/Dependencies/Defaults/.travis.yml
new file mode 100644
index 00000000..3087b646
--- /dev/null
+++ b/Dependencies/Defaults/.travis.yml
@@ -0,0 +1,3 @@
+language: swift
+osx_image: xcode11.4
+script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS
diff --git a/Dependencies/Defaults/Configs/Defaults.plist b/Dependencies/Defaults/Configs/Defaults.plist
new file mode 100644
index 00000000..e49db7f5
--- /dev/null
+++ b/Dependencies/Defaults/Configs/Defaults.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ NSHumanReadableCopyright
+ MIT License © Sindre Sorhus
+
+
diff --git a/SwitchTests/SwitchTests-Info.plist b/Dependencies/Defaults/Configs/DefaultsTests.plist
similarity index 66%
rename from SwitchTests/SwitchTests-Info.plist
rename to Dependencies/Defaults/Configs/DefaultsTests.plist
index d6837796..5ad566df 100644
--- a/SwitchTests/SwitchTests-Info.plist
+++ b/Dependencies/Defaults/Configs/DefaultsTests.plist
@@ -2,20 +2,18 @@
- CFBundleDevelopmentRegion
- en
CFBundleExecutable
- ${EXECUTABLE_NAME}
+ $(EXECUTABLE_NAME)
CFBundleIdentifier
- net.numist.${PRODUCT_NAME:rfc1034identifier}
+ $(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
+ CFBundleName
+ $(PRODUCT_NAME)
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.0
- CFBundleSignature
- ????
+ 0.0.0
CFBundleVersion
1
diff --git a/Dependencies/Defaults/Defaults.podspec b/Dependencies/Defaults/Defaults.podspec
new file mode 100644
index 00000000..82d8a4f4
--- /dev/null
+++ b/Dependencies/Defaults/Defaults.podspec
@@ -0,0 +1,17 @@
+Pod::Spec.new do |s|
+ s.name = 'Defaults'
+ s.version = '4.1.0'
+ s.summary = 'Swifty and modern UserDefaults'
+ s.license = 'MIT'
+ s.homepage = 'https://github.com/sindresorhus/Defaults'
+ s.social_media_url = 'https://twitter.com/sindresorhus'
+ s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' }
+ s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" }
+ s.source_files = 'Sources/**/*.swift'
+ s.swift_version = '5.3'
+ s.macos.deployment_target = '10.12'
+ s.ios.deployment_target = '10.0'
+ s.tvos.deployment_target = '10.0'
+ s.watchos.deployment_target = '3.0'
+ s.weak_framework = 'Combine'
+end
diff --git a/Dependencies/Defaults/Defaults.xcodeproj/project.pbxproj b/Dependencies/Defaults/Defaults.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..966867de
--- /dev/null
+++ b/Dependencies/Defaults/Defaults.xcodeproj/project.pbxproj
@@ -0,0 +1,1168 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 52D6D9871BEFF229002C0205 /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Defaults.framework */; };
+ 8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; };
+ 8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; };
+ 8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; };
+ 8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; };
+ 8933C78E1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; };
+ 8933C78F1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; };
+ 8933C7901EB5B82D000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; };
+ DD7502881C68FEDE006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Defaults.framework */; };
+ DD7502921C690C7A006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */; };
+ E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
+ E286D0C823B8D54C00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
+ E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
+ E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
+ E339B3B32449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; };
+ E339B3B42449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; };
+ E339B3B52449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; };
+ E339B3B62449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; };
+ E339B3B82449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; };
+ E339B3B92449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; };
+ E339B3BA2449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; };
+ E339B3BB2449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; };
+ E38C9F27244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
+ E38C9F28244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
+ E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
+ E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
+ E3EB3E33216505920033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
+ E3EB3E35216507AE0033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
+ E3EB3E36216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
+ E3EB3E37216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
+ E3EB3E38216507B60033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
+ E3EB3E39216507C30033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
+ E3EB3E3A216507C40033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
+ E3EB3E3B216507C40033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 52D6D97B1BEFF229002C0205;
+ remoteInfo = Defaults;
+ };
+ DD7502801C68FCFC006590AF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 52D6DA0E1BF000BD002C0205;
+ remoteInfo = "Defaults-macOS";
+ };
+ DD7502931C690C7A006590AF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 52D6D9EF1BEFFFBE002C0205;
+ remoteInfo = "Defaults-tvOS";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 52D6D97C1BEFF229002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 52D6D9861BEFF229002C0205 /* Defaults-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 52D6D9E21BEFFF6E002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 52D6DA0F1BF000BD002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6614F6E222FC6E1C00B0C9CE /* readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; lineEnding = 0; path = readme.md; sourceTree = ""; usesTabs = 1; };
+ 8933C7841EB5B820000D00A4 /* Defaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Defaults.swift; sourceTree = ""; usesTabs = 1; };
+ 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DefaultsTests.swift; sourceTree = ""; usesTabs = 1; };
+ AD2FAA261CD0B6D800659CF4 /* Defaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = ""; };
+ AD2FAA281CD0B6E100659CF4 /* DefaultsTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DefaultsTests.plist; sourceTree = ""; };
+ DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ E286D0C623B8D51100570D1E /* Observation+Combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Observation+Combine.swift"; sourceTree = ""; usesTabs = 1; };
+ E339B3B22449ED2000E7A40A /* Reset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Reset.swift; sourceTree = ""; usesTabs = 1; };
+ E339B3B72449F10D00E7A40A /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = ""; usesTabs = 1; };
+ E38C9F26244ADA2F00A6737A /* SwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SwiftUI.swift; sourceTree = ""; usesTabs = 1; };
+ E3EB3E32216505920033B089 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Utilities.swift; sourceTree = ""; usesTabs = 1; };
+ E3EB3E34216507AE0033B089 /* Observation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observation.swift; sourceTree = ""; usesTabs = 1; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 52D6D9781BEFF229002C0205 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9831BEFF229002C0205 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 52D6D9871BEFF229002C0205 /* Defaults.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9DE1BEFFF6E002C0205 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9EC1BEFFFBE002C0205 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6DA0B1BF000BD002C0205 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DD7502771C68FCFC006590AF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD7502881C68FEDE006590AF /* Defaults.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DD75028A1C690C7A006590AF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD7502921C690C7A006590AF /* Defaults.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 52D6D9721BEFF229002C0205 = {
+ isa = PBXGroup;
+ children = (
+ 6614F6E222FC6E1C00B0C9CE /* readme.md */,
+ 8933C7811EB5B7E0000D00A4 /* Sources */,
+ 8933C7831EB5B7EB000D00A4 /* Tests */,
+ 52D6D99C1BEFF38C002C0205 /* Configs */,
+ 52D6D97D1BEFF229002C0205 /* Products */,
+ );
+ sourceTree = "";
+ usesTabs = 1;
+ };
+ 52D6D97D1BEFF229002C0205 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 52D6D97C1BEFF229002C0205 /* Defaults.framework */,
+ 52D6D9861BEFF229002C0205 /* Defaults-iOS Tests.xctest */,
+ 52D6D9E21BEFFF6E002C0205 /* Defaults.framework */,
+ 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */,
+ 52D6DA0F1BF000BD002C0205 /* Defaults.framework */,
+ DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */,
+ DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 52D6D99C1BEFF38C002C0205 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ DD7502721C68FC1B006590AF /* Frameworks */,
+ DD7502731C68FC20006590AF /* Tests */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 8933C7811EB5B7E0000D00A4 /* Sources */ = {
+ isa = PBXGroup;
+ children = (
+ E30E93D822E9425E00530C8F /* Defaults */,
+ );
+ path = Sources;
+ sourceTree = "";
+ };
+ 8933C7831EB5B7EB000D00A4 /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */,
+ );
+ name = Tests;
+ path = Tests/DefaultsTests;
+ sourceTree = "";
+ };
+ DD7502721C68FC1B006590AF /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ AD2FAA261CD0B6D800659CF4 /* Defaults.plist */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ DD7502731C68FC20006590AF /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ AD2FAA281CD0B6E100659CF4 /* DefaultsTests.plist */,
+ );
+ name = Tests;
+ sourceTree = "";
+ };
+ E30E93D822E9425E00530C8F /* Defaults */ = {
+ isa = PBXGroup;
+ children = (
+ 8933C7841EB5B820000D00A4 /* Defaults.swift */,
+ E339B3B72449F10D00E7A40A /* UserDefaults.swift */,
+ E339B3B22449ED2000E7A40A /* Reset.swift */,
+ E3EB3E34216507AE0033B089 /* Observation.swift */,
+ E286D0C623B8D51100570D1E /* Observation+Combine.swift */,
+ E38C9F26244ADA2F00A6737A /* SwiftUI.swift */,
+ E3EB3E32216505920033B089 /* Utilities.swift */,
+ );
+ path = Defaults;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 52D6D9791BEFF229002C0205 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9DF1BEFFF6E002C0205 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9ED1BEFFFBE002C0205 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6DA0C1BF000BD002C0205 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 52D6D97B1BEFF229002C0205 /* Defaults-iOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS" */;
+ buildPhases = (
+ 52D6D9771BEFF229002C0205 /* Sources */,
+ 52D6D9781BEFF229002C0205 /* Frameworks */,
+ 52D6D9791BEFF229002C0205 /* Headers */,
+ 52D6D97A1BEFF229002C0205 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Defaults-iOS";
+ productName = Defaults;
+ productReference = 52D6D97C1BEFF229002C0205 /* Defaults.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 52D6D9851BEFF229002C0205 /* Defaults-iOS Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS Tests" */;
+ buildPhases = (
+ 52D6D9821BEFF229002C0205 /* Sources */,
+ 52D6D9831BEFF229002C0205 /* Frameworks */,
+ 52D6D9841BEFF229002C0205 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 52D6D9891BEFF229002C0205 /* PBXTargetDependency */,
+ );
+ name = "Defaults-iOS Tests";
+ productName = DefaultsTests;
+ productReference = 52D6D9861BEFF229002C0205 /* Defaults-iOS Tests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 52D6D9E11BEFFF6E002C0205 /* Defaults-watchOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Defaults-watchOS" */;
+ buildPhases = (
+ 52D6D9DD1BEFFF6E002C0205 /* Sources */,
+ 52D6D9DE1BEFFF6E002C0205 /* Frameworks */,
+ 52D6D9DF1BEFFF6E002C0205 /* Headers */,
+ 52D6D9E01BEFFF6E002C0205 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Defaults-watchOS";
+ productName = "Defaults-watchOS";
+ productReference = 52D6D9E21BEFFF6E002C0205 /* Defaults.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 52D6D9EF1BEFFFBE002C0205 /* Defaults-tvOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Defaults-tvOS" */;
+ buildPhases = (
+ 52D6D9EB1BEFFFBE002C0205 /* Sources */,
+ 52D6D9EC1BEFFFBE002C0205 /* Frameworks */,
+ 52D6D9ED1BEFFFBE002C0205 /* Headers */,
+ 52D6D9EE1BEFFFBE002C0205 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Defaults-tvOS";
+ productName = "Defaults-tvOS";
+ productReference = 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 52D6DA0E1BF000BD002C0205 /* Defaults-macOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Defaults-macOS" */;
+ buildPhases = (
+ 52D6DA0A1BF000BD002C0205 /* Sources */,
+ 52D6DA0B1BF000BD002C0205 /* Frameworks */,
+ 52D6DA0C1BF000BD002C0205 /* Headers */,
+ 52D6DA0D1BF000BD002C0205 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Defaults-macOS";
+ productName = "Defaults-macOS";
+ productReference = 52D6DA0F1BF000BD002C0205 /* Defaults.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ DD7502791C68FCFC006590AF /* Defaults-macOS Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Defaults-macOS Tests" */;
+ buildPhases = (
+ DD7502761C68FCFC006590AF /* Sources */,
+ DD7502771C68FCFC006590AF /* Frameworks */,
+ DD7502781C68FCFC006590AF /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DD7502811C68FCFC006590AF /* PBXTargetDependency */,
+ );
+ name = "Defaults-macOS Tests";
+ productName = "Defaults-OS Tests";
+ productReference = DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ DD75028C1C690C7A006590AF /* Defaults-tvOS Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Defaults-tvOS Tests" */;
+ buildPhases = (
+ DD7502891C690C7A006590AF /* Sources */,
+ DD75028A1C690C7A006590AF /* Frameworks */,
+ DD75028B1C690C7A006590AF /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DD7502941C690C7A006590AF /* PBXTargetDependency */,
+ );
+ name = "Defaults-tvOS Tests";
+ productName = "Defaults-tvOS Tests";
+ productReference = DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 52D6D9731BEFF229002C0205 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0720;
+ LastUpgradeCheck = 1200;
+ ORGANIZATIONNAME = Defaults;
+ TargetAttributes = {
+ 52D6D97B1BEFF229002C0205 = {
+ CreatedOnToolsVersion = 7.1;
+ LastSwiftMigration = 1020;
+ };
+ 52D6D9851BEFF229002C0205 = {
+ CreatedOnToolsVersion = 7.1;
+ LastSwiftMigration = 1020;
+ };
+ 52D6D9E11BEFFF6E002C0205 = {
+ CreatedOnToolsVersion = 7.1;
+ LastSwiftMigration = 1020;
+ };
+ 52D6D9EF1BEFFFBE002C0205 = {
+ CreatedOnToolsVersion = 7.1;
+ LastSwiftMigration = 1020;
+ };
+ 52D6DA0E1BF000BD002C0205 = {
+ CreatedOnToolsVersion = 7.1;
+ LastSwiftMigration = 1020;
+ };
+ DD7502791C68FCFC006590AF = {
+ CreatedOnToolsVersion = 7.2.1;
+ LastSwiftMigration = 1020;
+ };
+ DD75028C1C690C7A006590AF = {
+ CreatedOnToolsVersion = 7.2.1;
+ LastSwiftMigration = 1020;
+ };
+ };
+ };
+ buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Defaults" */;
+ compatibilityVersion = "Xcode 12.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 52D6D9721BEFF229002C0205;
+ productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 52D6DA0E1BF000BD002C0205 /* Defaults-macOS */,
+ 52D6D97B1BEFF229002C0205 /* Defaults-iOS */,
+ 52D6D9EF1BEFFFBE002C0205 /* Defaults-tvOS */,
+ 52D6D9E11BEFFF6E002C0205 /* Defaults-watchOS */,
+ 52D6D9851BEFF229002C0205 /* Defaults-iOS Tests */,
+ DD7502791C68FCFC006590AF /* Defaults-macOS Tests */,
+ DD75028C1C690C7A006590AF /* Defaults-tvOS Tests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 52D6D97A1BEFF229002C0205 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9841BEFF229002C0205 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9E01BEFFF6E002C0205 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9EE1BEFFFBE002C0205 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6DA0D1BF000BD002C0205 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DD7502781C68FCFC006590AF /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DD75028B1C690C7A006590AF /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 52D6D9771BEFF229002C0205 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E286D0C823B8D54C00570D1E /* Observation+Combine.swift in Sources */,
+ E38C9F28244ADA2F00A6737A /* SwiftUI.swift in Sources */,
+ 8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */,
+ E339B3B92449F10D00E7A40A /* UserDefaults.swift in Sources */,
+ E3EB3E35216507AE0033B089 /* Observation.swift in Sources */,
+ E3EB3E33216505920033B089 /* Utilities.swift in Sources */,
+ E339B3B42449ED2000E7A40A /* Reset.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9821BEFF229002C0205 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8933C7901EB5B82D000D00A4 /* DefaultsTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9DD1BEFFF6E002C0205 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */,
+ E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */,
+ E3EB3E3A216507C40033B089 /* Utilities.swift in Sources */,
+ E339B3BB2449F10D00E7A40A /* UserDefaults.swift in Sources */,
+ E3EB3E37216507B50033B089 /* Observation.swift in Sources */,
+ 8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */,
+ E339B3B62449ED2000E7A40A /* Reset.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6D9EB1BEFFFBE002C0205 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */,
+ E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */,
+ E3EB3E3B216507C40033B089 /* Utilities.swift in Sources */,
+ E339B3BA2449F10D00E7A40A /* UserDefaults.swift in Sources */,
+ E3EB3E38216507B60033B089 /* Observation.swift in Sources */,
+ 8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */,
+ E339B3B52449ED2000E7A40A /* Reset.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 52D6DA0A1BF000BD002C0205 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */,
+ E38C9F27244ADA2F00A6737A /* SwiftUI.swift in Sources */,
+ E3EB3E39216507C30033B089 /* Utilities.swift in Sources */,
+ E339B3B82449F10D00E7A40A /* UserDefaults.swift in Sources */,
+ E3EB3E36216507B50033B089 /* Observation.swift in Sources */,
+ 8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */,
+ E339B3B32449ED2000E7A40A /* Reset.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DD7502761C68FCFC006590AF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8933C78F1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DD7502891C690C7A006590AF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8933C78E1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 52D6D9891BEFF229002C0205 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 52D6D97B1BEFF229002C0205 /* Defaults-iOS */;
+ targetProxy = 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */;
+ };
+ DD7502811C68FCFC006590AF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 52D6DA0E1BF000BD002C0205 /* Defaults-macOS */;
+ targetProxy = DD7502801C68FCFC006590AF /* PBXContainerItemProxy */;
+ };
+ DD7502941C690C7A006590AF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 52D6D9EF1BEFFFBE002C0205 /* Defaults-tvOS */;
+ targetProxy = DD7502931C690C7A006590AF /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 52D6D98E1BEFF229002C0205 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ 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 = 12.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_LDFLAGS = (
+ "-weak_framework",
+ Combine,
+ );
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = singlefile;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ 52D6D98F1BEFF229002C0205 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ 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 = 12.0;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_LDFLAGS = (
+ "-weak_framework",
+ Combine,
+ );
+ SDKROOT = iphoneos;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ 52D6D9911BEFF229002C0205 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_IDENTITY = "";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+ CODE_SIGN_STYLE = Manual;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 4.0.0;
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS";
+ PRODUCT_NAME = Defaults;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_COMPILATION_MODE = singlefile;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 52D6D9921BEFF229002C0205 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_IDENTITY = "";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+ CODE_SIGN_STYLE = Manual;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 4.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS";
+ PRODUCT_NAME = Defaults;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = NO;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 52D6D9941BEFF229002C0205 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
+ INFOPLIST_FILE = Configs/DefaultsTests.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_COMPILATION_MODE = singlefile;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 52D6D9951BEFF229002C0205 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
+ INFOPLIST_FILE = Configs/DefaultsTests.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 52D6D9E81BEFFF6E002C0205 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 4.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-watchOS";
+ PRODUCT_NAME = Defaults;
+ SDKROOT = watchos;
+ SKIP_INSTALL = YES;
+ TARGETED_DEVICE_FAMILY = 4;
+ WATCHOS_DEPLOYMENT_TARGET = 3.0;
+ };
+ name = Debug;
+ };
+ 52D6D9E91BEFFF6E002C0205 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 4.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-watchOS";
+ PRODUCT_NAME = Defaults;
+ SDKROOT = watchos;
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = 4;
+ WATCHOS_DEPLOYMENT_TARGET = 3.0;
+ };
+ name = Release;
+ };
+ 52D6DA021BEFFFBE002C0205 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 4.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS";
+ PRODUCT_NAME = Defaults;
+ SDKROOT = appletvos;
+ SKIP_INSTALL = YES;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 12.0;
+ };
+ name = Debug;
+ };
+ 52D6DA031BEFFFBE002C0205 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 4.0.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS";
+ PRODUCT_NAME = Defaults;
+ SDKROOT = appletvos;
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 12.0;
+ };
+ name = Release;
+ };
+ 52D6DA211BF000BD002C0205 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COMBINE_HIDPI_IMAGES = YES;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MARKETING_VERSION = 4.1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS";
+ PRODUCT_NAME = Defaults;
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ 52D6DA221BF000BD002C0205 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COMBINE_HIDPI_IMAGES = YES;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = Configs/Defaults.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MARKETING_VERSION = 4.1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS";
+ PRODUCT_NAME = Defaults;
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ DD7502831C68FCFC006590AF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Configs/DefaultsTests.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ "@loader_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ DD7502841C68FCFC006590AF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Configs/DefaultsTests.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ "@loader_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ DD7502961C690C7A006590AF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
+ INFOPLIST_FILE = Configs/DefaultsTests.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = appletvos;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 12.0;
+ };
+ name = Debug;
+ };
+ DD7502971C690C7A006590AF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
+ INFOPLIST_FILE = Configs/DefaultsTests.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = appletvos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 12.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Defaults" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52D6D98E1BEFF229002C0205 /* Debug */,
+ 52D6D98F1BEFF229002C0205 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52D6D9911BEFF229002C0205 /* Debug */,
+ 52D6D9921BEFF229002C0205 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52D6D9941BEFF229002C0205 /* Debug */,
+ 52D6D9951BEFF229002C0205 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Defaults-watchOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52D6D9E81BEFFF6E002C0205 /* Debug */,
+ 52D6D9E91BEFFF6E002C0205 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Defaults-tvOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52D6DA021BEFFFBE002C0205 /* Debug */,
+ 52D6DA031BEFFFBE002C0205 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Defaults-macOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52D6DA211BF000BD002C0205 /* Debug */,
+ 52D6DA221BF000BD002C0205 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Defaults-macOS Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DD7502831C68FCFC006590AF /* Debug */,
+ DD7502841C68FCFC006590AF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Defaults-tvOS Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DD7502961C690C7A006590AF /* Debug */,
+ DD7502971C690C7A006590AF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 52D6D9731BEFF229002C0205 /* Project object */;
+}
diff --git a/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-iOS.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-iOS.xcscheme
new file mode 100644
index 00000000..dfc0e457
--- /dev/null
+++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-iOS.xcscheme
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-macOS.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-macOS.xcscheme
new file mode 100644
index 00000000..10c1e989
--- /dev/null
+++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-macOS.xcscheme
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-tvOS.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-tvOS.xcscheme
new file mode 100644
index 00000000..1a5ea11f
--- /dev/null
+++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-tvOS.xcscheme
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Frameworks/NNKit/NNKit.xcodeproj/xcshareddata/xcschemes/NNKit.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-watchOS.xcscheme
similarity index 65%
rename from Frameworks/NNKit/NNKit.xcodeproj/xcshareddata/xcschemes/NNKit.xcscheme
rename to Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-watchOS.xcscheme
index 20643589..09c9bf2a 100644
--- a/Frameworks/NNKit/NNKit.xcodeproj/xcshareddata/xcschemes/NNKit.xcscheme
+++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-watchOS.xcscheme
@@ -1,6 +1,6 @@
+ BlueprintIdentifier = "52D6D9E11BEFFF6E002C0205"
+ BuildableName = "Defaults.framework"
+ BlueprintName = "Defaults-watchOS"
+ ReferencedContainer = "container:Defaults.xcodeproj">
+ codeCoverageEnabled = "YES">
-
-
-
-
+ BlueprintIdentifier = "52D6D9E11BEFFF6E002C0205"
+ BuildableName = "Defaults.framework"
+ BlueprintName = "Defaults-watchOS"
+ ReferencedContainer = "container:Defaults.xcodeproj">
-
-
+
+
+
+
diff --git a/Dependencies/Defaults/Package.swift b/Dependencies/Defaults/Package.swift
new file mode 100644
index 00000000..cf586e5c
--- /dev/null
+++ b/Dependencies/Defaults/Package.swift
@@ -0,0 +1,31 @@
+// swift-tools-version:5.2
+import PackageDescription
+
+let package = Package(
+ name: "Defaults",
+ platforms: [
+ .macOS(.v10_12),
+ .iOS(.v10),
+ .tvOS(.v10),
+ .watchOS(.v3)
+ ],
+ products: [
+ .library(
+ name: "Defaults",
+ targets: [
+ "Defaults"
+ ]
+ )
+ ],
+ targets: [
+ .target(
+ name: "Defaults"
+ ),
+ .testTarget(
+ name: "DefaultsTests",
+ dependencies: [
+ "Defaults"
+ ]
+ )
+ ]
+)
diff --git a/Dependencies/Defaults/Sources/Defaults/Defaults.swift b/Dependencies/Defaults/Sources/Defaults/Defaults.swift
new file mode 100644
index 00000000..227c9bb4
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/Defaults.swift
@@ -0,0 +1,142 @@
+// MIT License © Sindre Sorhus
+import Foundation
+
+public protocol DefaultsBaseKey: Defaults.AnyKey {
+ var name: String { get }
+ var suite: UserDefaults { get }
+}
+
+extension DefaultsBaseKey {
+ /// Reset the item back to its default value.
+ public func reset() {
+ suite.removeObject(forKey: name)
+ }
+}
+
+public enum Defaults {
+ public typealias BaseKey = DefaultsBaseKey
+ public typealias AnyKey = Keys
+
+ public class Keys: BaseKey {
+ public typealias Key = Defaults.Key
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public typealias NSSecureCodingKey = Defaults.NSSecureCodingKey
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public typealias NSSecureCodingOptionalKey = Defaults.NSSecureCodingOptionalKey
+
+ public let name: String
+ public let suite: UserDefaults
+
+ fileprivate init(name: String, suite: UserDefaults) {
+ self.name = name
+ self.suite = suite
+ }
+ }
+
+ public final class Key: AnyKey {
+ public let defaultValue: Value
+
+ /// Create a defaults key.
+ /// The `default` parameter can be left out if the `Value` type is an optional.
+ public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) {
+ self.defaultValue = defaultValue
+
+ super.init(name: key, suite: suite)
+
+ if (defaultValue as? _DefaultsOptionalType)?.isNil == true {
+ return
+ }
+
+ // Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding.
+ if UserDefaults.isNativelySupportedType(Value.self) {
+ suite.register(defaults: [key: defaultValue])
+ } else if let value = suite._encode(defaultValue) {
+ suite.register(defaults: [key: value])
+ }
+ }
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public final class NSSecureCodingKey: AnyKey {
+ public let defaultValue: Value
+
+ /// Create a defaults key.
+ /// The `default` parameter can be left out if the `Value` type is an optional.
+ public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) {
+ self.defaultValue = defaultValue
+
+ super.init(name: key, suite: suite)
+
+ if (defaultValue as? _DefaultsOptionalType)?.isNil == true {
+ return
+ }
+
+ // Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding.
+ if UserDefaults.isNativelySupportedType(Value.self) {
+ suite.register(defaults: [key: defaultValue])
+ } else if let value = try? NSKeyedArchiver.archivedData(withRootObject: defaultValue, requiringSecureCoding: true) {
+ suite.register(defaults: [key: value])
+ }
+ }
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public final class NSSecureCodingOptionalKey: AnyKey {
+ /// Create an optional defaults key.
+ public init(_ key: String, suite: UserDefaults = .standard) {
+ super.init(name: key, suite: suite)
+ }
+ }
+
+ /// Access a defaults value using a `Defaults.Key`.
+ public static subscript(key: Key) -> Value {
+ get { key.suite[key] }
+ set {
+ key.suite[key] = newValue
+ }
+ }
+
+ /// Access a defaults value using a `Defaults.NSSecureCodingKey`.
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public static subscript(key: NSSecureCodingKey) -> Value {
+ get { key.suite[key] }
+ set {
+ key.suite[key] = newValue
+ }
+ }
+
+ /// Access a defaults value using a `Defaults.NSSecureCodingOptionalKey`.
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public static subscript(key: NSSecureCodingOptionalKey) -> Value? {
+ get { key.suite[key] }
+ set {
+ key.suite[key] = newValue
+ }
+ }
+}
+
+extension Defaults {
+ /**
+ Remove all entries from the given `UserDefaults` suite.
+
+ - Note: This only removes user-defined entries. System-defined entries will remain.
+ */
+ public static func removeAll(suite: UserDefaults = .standard) {
+ suite.removeAll()
+ }
+}
+
+extension Defaults.Key {
+ public convenience init(_ key: String, suite: UserDefaults = .standard) where Value == T? {
+ self.init(key, default: nil, suite: suite)
+ }
+}
+
+@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+extension Defaults.NSSecureCodingKey where Value: _DefaultsOptionalType {
+ public convenience init(_ key: String, suite: UserDefaults = .standard) {
+ self.init(key, default: nil, suite: suite)
+ }
+}
diff --git a/Dependencies/Defaults/Sources/Defaults/Observation+Combine.swift b/Dependencies/Defaults/Sources/Defaults/Observation+Combine.swift
new file mode 100644
index 00000000..201c8705
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/Observation+Combine.swift
@@ -0,0 +1,152 @@
+#if canImport(Combine)
+import Foundation
+import Combine
+
+extension Defaults {
+ /**
+ Custom `Subscription` for `UserDefaults` key observation.
+ */
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ final class DefaultsSubscription: Subscription where SubscriberType.Input == BaseChange {
+ private var subscriber: SubscriberType?
+ private var observation: UserDefaultsKeyObservation?
+ private let options: ObservationOptions
+
+ init(subscriber: SubscriberType, suite: UserDefaults, key: String, options: ObservationOptions) {
+ self.subscriber = subscriber
+ self.options = options
+ self.observation = UserDefaultsKeyObservation(
+ object: suite,
+ key: key,
+ callback: observationCallback(_:)
+ )
+ }
+
+ func request(_ demand: Subscribers.Demand) {
+ // Nothing as we send events only when they occur.
+ }
+
+ func cancel() {
+ observation?.invalidate()
+ observation = nil
+ subscriber = nil
+ }
+
+ func start() {
+ observation?.start(options: options)
+ }
+
+ private func observationCallback(_ change: BaseChange) {
+ _ = subscriber?.receive(change)
+ }
+ }
+
+ /**
+ Custom Publisher, which is using DefaultsSubscription.
+ */
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ struct DefaultsPublisher: Publisher {
+ typealias Output = BaseChange
+ typealias Failure = Never
+
+ private let suite: UserDefaults
+ private let key: String
+ private let options: ObservationOptions
+
+ init(suite: UserDefaults, key: String, options: ObservationOptions) {
+ self.suite = suite
+ self.key = key
+ self.options = options
+ }
+
+ func receive(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
+ let subscription = DefaultsSubscription(
+ subscriber: subscriber,
+ suite: suite,
+ key: key,
+ options: options
+ )
+
+ subscriber.receive(subscription: subscription)
+ subscription.start()
+ }
+ }
+
+ /**
+ Returns a type-erased `Publisher` that publishes changes related to the given key.
+
+ ```
+ extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+ }
+
+ let publisher = Defaults.publisher(.isUnicornMode).map(\.newValue)
+
+ let cancellable = publisher.sink { value in
+ print(value)
+ //=> false
+ }
+ ```
+ */
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ public static func publisher(
+ _ key: Key,
+ options: ObservationOptions = [.initial]
+ ) -> AnyPublisher, Never> {
+ let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
+ .map { KeyChange(change: $0, defaultValue: key.defaultValue) }
+
+ return AnyPublisher(publisher)
+ }
+
+ /**
+ Returns a type-erased `Publisher` that publishes changes related to the given key.
+ */
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ public static func publisher(
+ _ key: NSSecureCodingKey,
+ options: ObservationOptions = [.initial]
+ ) -> AnyPublisher, Never> {
+ let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
+ .map { NSSecureCodingKeyChange(change: $0, defaultValue: key.defaultValue) }
+
+ return AnyPublisher(publisher)
+ }
+
+ /**
+ Returns a type-erased `Publisher` that publishes changes related to the given optional key.
+ */
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ public static func publisher(
+ _ key: NSSecureCodingOptionalKey,
+ options: ObservationOptions = [.initial]
+ ) -> AnyPublisher, Never> {
+ let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
+ .map { NSSecureCodingOptionalKeyChange(change: $0) }
+
+ return AnyPublisher(publisher)
+ }
+
+ /**
+ Publisher for multiple `Key` observation, but without specific information about changes.
+ */
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ public static func publisher(
+ keys: AnyKey...,
+ options: ObservationOptions = [.initial]
+ ) -> AnyPublisher {
+ let initial = Empty(completeImmediately: false).eraseToAnyPublisher()
+
+ let combinedPublisher =
+ keys.map { key in
+ DefaultsPublisher(suite: key.suite, key: key.name, options: options)
+ .map { _ in () }
+ .eraseToAnyPublisher()
+ }.reduce(initial) { combined, keyPublisher in
+ combined.merge(with: keyPublisher).eraseToAnyPublisher()
+ }
+
+ return combinedPublisher
+ }
+}
+#endif
diff --git a/Dependencies/Defaults/Sources/Defaults/Observation.swift b/Dependencies/Defaults/Sources/Defaults/Observation.swift
new file mode 100644
index 00000000..f0ef82a9
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/Observation.swift
@@ -0,0 +1,446 @@
+import Foundation
+
+public protocol DefaultsObservation: AnyObject {
+ func invalidate()
+
+ /**
+ Keep this observation alive for as long as, and no longer than, another object exists.
+
+ ```
+ Defaults.observe(.xyz) { [unowned self] change in
+ self.xyz = change.newValue
+ }.tieToLifetime(of: self)
+ ```
+ */
+ @discardableResult
+ func tieToLifetime(of weaklyHeldObject: AnyObject) -> Self
+
+ /**
+ Break the lifetime tie created by `tieToLifetime(of:)`, if one exists.
+
+ - Postcondition: The effects of any call to `tieToLifetime(of:)` are reversed.
+ - Note: If the tied-to object has already died, then self is considered to be invalidated, and this method has no logical effect.
+ */
+ func removeLifetimeTie()
+}
+
+extension Defaults {
+ public typealias Observation = DefaultsObservation
+
+ public enum ObservationOption {
+ /// Whether a notification should be sent to the observer immediately, before the observer registration method even returns.
+ case initial
+
+ /// Whether separate notifications should be sent to the observer before and after each change, instead of a single notification after the change.
+ case prior
+ }
+
+ public typealias ObservationOptions = Set
+
+ private static func deserialize(_ value: Any?, to type: Value.Type) -> Value? {
+ guard
+ let value = value,
+ !(value is NSNull)
+ else {
+ return nil
+ }
+
+ // This handles the case where the value was a plist value using `isNativelySupportedType`
+ if let value = value as? Value {
+ return value
+ }
+
+ // Using the array trick as done below in `UserDefaults#_set()`
+ return [Value].init(jsonString: "\([value])")?.first
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ private static func deserialize(_ value: Any?, to type: Value.Type) -> Value? {
+ guard
+ let value = value,
+ !(value is NSNull)
+ else {
+ return nil
+ }
+
+ // This handles the case where the value was a plist value using `isNativelySupportedType`
+ if let value = value as? Value {
+ return value
+ }
+
+ guard let dataValue = value as? Data else {
+ return nil
+ }
+
+ return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataValue) as? Value
+ }
+
+ struct BaseChange {
+ let kind: NSKeyValueChange
+ let indexes: IndexSet?
+ let isPrior: Bool
+ let newValue: Any?
+ let oldValue: Any?
+
+ init(change: [NSKeyValueChangeKey: Any]) {
+ self.kind = NSKeyValueChange(rawValue: change[.kindKey] as! UInt)!
+ self.indexes = change[.indexesKey] as? IndexSet
+ self.isPrior = change[.notificationIsPriorKey] as? Bool ?? false
+ self.oldValue = change[.oldKey]
+ self.newValue = change[.newKey]
+ }
+ }
+
+ public struct KeyChange {
+ public let kind: NSKeyValueChange
+ public let indexes: IndexSet?
+ public let isPrior: Bool
+ public let newValue: Value
+ public let oldValue: Value
+
+ init(change: BaseChange, defaultValue: Value) {
+ self.kind = change.kind
+ self.indexes = change.indexes
+ self.isPrior = change.isPrior
+ self.oldValue = deserialize(change.oldValue, to: Value.self) ?? defaultValue
+ self.newValue = deserialize(change.newValue, to: Value.self) ?? defaultValue
+ }
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public struct NSSecureCodingKeyChange {
+ public let kind: NSKeyValueChange
+ public let indexes: IndexSet?
+ public let isPrior: Bool
+ public let newValue: Value
+ public let oldValue: Value
+
+ init(change: BaseChange, defaultValue: Value) {
+ self.kind = change.kind
+ self.indexes = change.indexes
+ self.isPrior = change.isPrior
+ self.oldValue = deserialize(change.oldValue, to: Value.self) ?? defaultValue
+ self.newValue = deserialize(change.newValue, to: Value.self) ?? defaultValue
+ }
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public struct NSSecureCodingOptionalKeyChange {
+ public let kind: NSKeyValueChange
+ public let indexes: IndexSet?
+ public let isPrior: Bool
+ public let newValue: Value?
+ public let oldValue: Value?
+
+ init(change: BaseChange) {
+ self.kind = change.kind
+ self.indexes = change.indexes
+ self.isPrior = change.isPrior
+ self.oldValue = deserialize(change.oldValue, to: Value.self)
+ self.newValue = deserialize(change.newValue, to: Value.self)
+ }
+ }
+
+ private static var preventPropagationThreadDictionaryKey: String {
+ "\(type(of: Observation.self))_threadUpdatingValuesFlag"
+ }
+
+ /**
+ Execute the closure without triggering change events.
+
+ Any `Defaults` key changes made within the closure will not propagate to `Defaults` event listeners (`Defaults.observe()` and `Defaults.publisher()`). This can be useful to prevent infinite recursion when you want to change a key in the callback listening to changes for the same key.
+
+ - Note: This only works with `Defaults.observe()` and `Defaults.publisher()`. User-made KVO will not be affected.
+
+ ```
+ let observer = Defaults.observe(keys: .key1, .key2) {
+ // …
+
+ Defaults.withoutPropagation {
+ // Update `.key1` without propagating the change to listeners.
+ Defaults[.key1] = 11
+ }
+
+ // This will be propagated.
+ Defaults[.someKey] = true
+ }
+ ```
+ */
+ public static func withoutPropagation(_ closure: () -> Void) {
+ // How does it work?
+ // KVO observation callbacks are executed right after a change is made, and run on the same thread as the caller. So it works by storing a flag in the current thread's dictionary, which is then evaluated in the callback.
+
+ let key = preventPropagationThreadDictionaryKey
+ Thread.current.threadDictionary[key] = true
+ closure()
+ Thread.current.threadDictionary[key] = false
+ }
+
+ final class UserDefaultsKeyObservation: NSObject, Observation {
+ typealias Callback = (BaseChange) -> Void
+
+ private weak var object: UserDefaults?
+ private let key: String
+ private let callback: Callback
+
+ init(object: UserDefaults, key: String, callback: @escaping Callback) {
+ self.object = object
+ self.key = key
+ self.callback = callback
+ }
+
+ deinit {
+ invalidate()
+ }
+
+ func start(options: ObservationOptions) {
+ object?.addObserver(self, forKeyPath: key, options: options.toNSKeyValueObservingOptions, context: nil)
+ }
+
+ public func invalidate() {
+ object?.removeObserver(self, forKeyPath: key, context: nil)
+ object = nil
+ lifetimeAssociation?.cancel()
+ }
+
+ private var lifetimeAssociation: LifetimeAssociation?
+
+ public func tieToLifetime(of weaklyHeldObject: AnyObject) -> Self {
+ lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in
+ self?.invalidate()
+ })
+
+ return self
+ }
+
+ public func removeLifetimeTie() {
+ lifetimeAssociation?.cancel()
+ }
+
+ // swiftlint:disable:next block_based_kvo
+ override func observeValue(
+ forKeyPath keyPath: String?,
+ of object: Any?,
+ change: [NSKeyValueChangeKey: Any]?, // swiftlint:disable:this discouraged_optional_collection
+ context: UnsafeMutableRawPointer?
+ ) {
+ guard let selfObject = self.object else {
+ invalidate()
+ return
+ }
+
+ guard
+ selfObject == object as? NSObject,
+ let change = change
+ else {
+ return
+ }
+
+ let key = preventPropagationThreadDictionaryKey
+ let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
+ guard !updatingValuesFlag else {
+ return
+ }
+
+ callback(BaseChange(change: change))
+ }
+ }
+
+ private final class CompositeUserDefaultsKeyObservation: NSObject, Observation {
+ private static var observationContext = 0
+
+ private final class SuiteKeyPair {
+ weak var suite: UserDefaults?
+ let key: String
+
+ init(suite: UserDefaults, key: String) {
+ self.suite = suite
+ self.key = key
+ }
+ }
+
+ private var observables: [SuiteKeyPair]
+ private var lifetimeAssociation: LifetimeAssociation?
+ private let callback: UserDefaultsKeyObservation.Callback
+
+ init(observables: [(suite: UserDefaults, key: String)], callback: @escaping UserDefaultsKeyObservation.Callback) {
+ self.observables = observables.map { SuiteKeyPair(suite: $0.suite, key: $0.key) }
+ self.callback = callback
+ super.init()
+ }
+
+ deinit {
+ invalidate()
+ }
+
+ public func start(options: ObservationOptions) {
+ for observable in observables {
+ observable.suite?.addObserver(
+ self,
+ forKeyPath: observable.key,
+ options: options.toNSKeyValueObservingOptions,
+ context: &Self.observationContext
+ )
+ }
+ }
+
+ public func invalidate() {
+ for observable in observables {
+ observable.suite?.removeObserver(self, forKeyPath: observable.key, context: &Self.observationContext)
+ observable.suite = nil
+ }
+
+ lifetimeAssociation?.cancel()
+ }
+
+ public func tieToLifetime(of weaklyHeldObject: AnyObject) -> Self {
+ lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in
+ self?.invalidate()
+ })
+
+ return self
+ }
+
+ public func removeLifetimeTie() {
+ lifetimeAssociation?.cancel()
+ }
+
+ // swiftlint:disable:next block_based_kvo
+ override func observeValue(
+ forKeyPath keyPath: String?,
+ of object: Any?,
+ change: [NSKeyValueChangeKey: Any]?, // swiftlint:disable:this discouraged_optional_collection
+ context: UnsafeMutableRawPointer?
+ ) {
+ guard
+ context == &Self.observationContext
+ else {
+ super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
+ return
+ }
+
+ guard
+ object is UserDefaults,
+ let change = change
+ else {
+ return
+ }
+
+ let key = preventPropagationThreadDictionaryKey
+ let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
+ if updatingValuesFlag {
+ return
+ }
+
+ callback(BaseChange(change: change))
+ }
+ }
+
+ /**
+ Observe a defaults key.
+
+ ```
+ extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+ }
+
+ let observer = Defaults.observe(.isUnicornMode) { change in
+ print(change.newValue)
+ //=> false
+ }
+ ```
+ */
+ public static func observe(
+ _ key: Key,
+ options: ObservationOptions = [.initial],
+ handler: @escaping (KeyChange) -> Void
+ ) -> Observation {
+ let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
+ handler(
+ KeyChange(change: change, defaultValue: key.defaultValue)
+ )
+ }
+ observation.start(options: options)
+ return observation
+ }
+
+ /**
+ Observe a defaults key.
+ */
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public static func observe(
+ _ key: NSSecureCodingKey,
+ options: ObservationOptions = [.initial],
+ handler: @escaping (NSSecureCodingKeyChange) -> Void
+ ) -> Observation {
+ let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
+ handler(
+ NSSecureCodingKeyChange(change: change, defaultValue: key.defaultValue)
+ )
+ }
+ observation.start(options: options)
+ return observation
+ }
+
+ /**
+ Observe an optional defaults key.
+ */
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public static func observe(
+ _ key: NSSecureCodingOptionalKey,
+ options: ObservationOptions = [.initial],
+ handler: @escaping (NSSecureCodingOptionalKeyChange) -> Void
+ ) -> Observation {
+ let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
+ handler(
+ NSSecureCodingOptionalKeyChange(change: change)
+ )
+ }
+ observation.start(options: options)
+ return observation
+ }
+
+ /**
+ Observe multiple keys of any type, but without any information about the changes.
+
+ ```
+ extension Defaults.Keys {
+ static let setting1 = Key("setting1", default: false)
+ static let setting2 = Key("setting2", default: true)
+ }
+
+ let observer = Defaults.observe(keys: .setting1, .setting2) {
+ // …
+ }
+ ```
+ */
+ public static func observe(
+ keys: AnyKey...,
+ options: ObservationOptions = [.initial],
+ handler: @escaping () -> Void
+ ) -> Observation {
+ let pairs = keys.map {
+ (suite: $0.suite, key: $0.name)
+ }
+ let compositeObservation = CompositeUserDefaultsKeyObservation(observables: pairs) { _ in
+ handler()
+ }
+ compositeObservation.start(options: options)
+
+ return compositeObservation
+ }
+}
+
+extension Defaults.ObservationOptions {
+ var toNSKeyValueObservingOptions: NSKeyValueObservingOptions {
+ var options: NSKeyValueObservingOptions = [.old, .new]
+
+ if contains(.initial) {
+ options.insert(.initial)
+ } else if contains(.prior) {
+ options.insert(.prior)
+ }
+
+ return options
+ }
+}
diff --git a/Dependencies/Defaults/Sources/Defaults/Reset.swift b/Dependencies/Defaults/Sources/Defaults/Reset.swift
new file mode 100644
index 00000000..d86ddcd0
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/Reset.swift
@@ -0,0 +1,105 @@
+import Foundation
+
+extension Defaults {
+ /**
+ Reset the given string keys back to their default values.
+
+ Prefer using the strongly-typed keys instead whenever possible. This method can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic.
+
+ - Parameter keys: String keys to reset.
+ - Parameter suite: `UserDefaults` suite.
+
+ ```
+ extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+ }
+
+ Defaults[.isUnicornMode] = true
+ //=> true
+
+ Defaults.reset(Defaults.Keys.isUnicornMode.name)
+ // Or `Defaults.reset("isUnicornMode")`
+
+ Defaults[.isUnicornMode]
+ //=> false
+ ```
+ */
+ public static func reset(_ keys: String..., suite: UserDefaults = .standard) {
+ reset(keys, suite: suite)
+ }
+
+ /**
+ Reset the given string keys back to their default values.
+
+ Prefer using the strongly-typed keys instead whenever possible. This method can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic.
+
+ - Parameter keys: String keys to reset.
+ - Parameter suite: `UserDefaults` suite.
+
+ ```
+ extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+ }
+
+ Defaults[.isUnicornMode] = true
+ //=> true
+
+ Defaults.reset([Defaults.Keys.isUnicornMode.name])
+ // Or `Defaults.reset(["isUnicornMode"])`
+
+ Defaults[.isUnicornMode]
+ //=> false
+ ```
+ */
+ public static func reset(_ keys: [String], suite: UserDefaults = .standard) {
+ for key in keys {
+ suite.removeObject(forKey: key)
+ }
+ }
+}
+
+extension Defaults {
+ /**
+ Reset the given keys back to their default values.
+
+ ```
+ extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+ }
+
+ Defaults[.isUnicornMode] = true
+ //=> true
+
+ Defaults.reset(.isUnicornMode)
+
+ Defaults[.isUnicornMode]
+ //=> false
+ ```
+ */
+ public static func reset(_ keys: AnyKey...) {
+ reset(keys)
+ }
+
+ /**
+ Reset the given keys back to their default values.
+
+ ```
+ extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+ }
+
+ Defaults[.isUnicornMode] = true
+ //=> true
+
+ Defaults.reset(.isUnicornMode)
+
+ Defaults[.isUnicornMode]
+ //=> false
+ ```
+ */
+ public static func reset(_ keys: [AnyKey]) {
+ for key in keys {
+ key.reset()
+ }
+ }
+}
diff --git a/Dependencies/Defaults/Sources/Defaults/SwiftUI.swift b/Dependencies/Defaults/Sources/Defaults/SwiftUI.swift
new file mode 100644
index 00000000..743d2dc8
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/SwiftUI.swift
@@ -0,0 +1,113 @@
+#if canImport(Combine)
+import SwiftUI
+import Combine
+
+@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
+extension Defaults {
+ final class Observable: ObservableObject {
+ let objectWillChange = ObservableObjectPublisher()
+ private var observation: DefaultsObservation?
+ private let key: Defaults.Key
+
+ var value: Value {
+ get { Defaults[key] }
+ set {
+ objectWillChange.send()
+ Defaults[key] = newValue
+ }
+ }
+
+ init(_ key: Key) {
+ self.key = key
+
+ self.observation = Defaults.observe(key, options: [.prior]) { [weak self] change in
+ guard change.isPrior else {
+ return
+ }
+
+ DispatchQueue.mainSafeAsync {
+ self?.objectWillChange.send()
+ }
+ }
+ }
+
+ /// Reset the key back to its default value.
+ func reset() {
+ key.reset()
+ }
+ }
+}
+
+@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
+@propertyWrapper
+public struct Default: DynamicProperty {
+ public typealias Publisher = AnyPublisher, Never>
+
+ private let key: Defaults.Key
+ @ObservedObject private var observable: Defaults.Observable
+
+ /**
+ Get/set a `Defaults` item and also have the view be updated when the value changes. This is similar to `@State`.
+
+ - Important: You cannot use this in an `ObservableObject`. It's meant to be used in a `View`.
+
+ ```
+ extension Defaults.Keys {
+ static let hasUnicorn = Key("hasUnicorn", default: false)
+ }
+
+ struct ContentView: View {
+ @Default(.hasUnicorn) var hasUnicorn
+
+ var body: some View {
+ Text("Has Unicorn: \(hasUnicorn)")
+ Toggle("Toggle Unicorn", isOn: $hasUnicorn)
+ }
+ }
+ ```
+ */
+ public init(_ key: Defaults.Key) {
+ self.key = key
+ self.observable = Defaults.Observable(key)
+ }
+
+ public var wrappedValue: Value {
+ get { observable.value }
+ nonmutating set {
+ observable.value = newValue
+ }
+ }
+
+ public var projectedValue: Binding { $observable.value }
+
+ /// Combine publisher that publishes values when the `Defaults` item changes.
+ public var publisher: Publisher { Defaults.publisher(key) }
+
+ public mutating func update() {
+ _observable.update()
+ }
+
+ /**
+ Reset the key back to its default value.
+
+ ```
+ extension Defaults.Keys {
+ static let opacity = Key("opacity", default: 1)
+ }
+
+ struct ContentView: View {
+ @Default(.opacity) var opacity
+
+ var body: some View {
+ Button("Reset") {
+ _opacity.reset()
+ }
+ }
+ }
+ ```
+ */
+ public func reset() {
+ key.reset()
+ }
+}
+#endif
diff --git a/Dependencies/Defaults/Sources/Defaults/UserDefaults.swift b/Dependencies/Defaults/Sources/Defaults/UserDefaults.swift
new file mode 100644
index 00000000..147c9282
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/UserDefaults.swift
@@ -0,0 +1,147 @@
+import Foundation
+
+extension UserDefaults {
+ private func _get(_ key: String) -> Value? {
+ if UserDefaults.isNativelySupportedType(Value.self) {
+ return object(forKey: key) as? Value
+ }
+
+ guard
+ let text = string(forKey: key),
+ let data = "[\(text)]".data(using: .utf8)
+ else {
+ return nil
+ }
+
+ do {
+ return (try JSONDecoder().decode([Value].self, from: data)).first
+ } catch {
+ print(error)
+ }
+
+ return nil
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ private func _get(_ key: String) -> Value? {
+ if UserDefaults.isNativelySupportedType(Value.self) {
+ return object(forKey: key) as? Value
+ }
+
+ guard
+ let data = data(forKey: key)
+ else {
+ return nil
+ }
+
+ do {
+ return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Value
+ } catch {
+ print(error)
+ }
+
+ return nil
+ }
+
+ func _encode(_ value: Value) -> String? {
+ do {
+ // Some codable values like URL and enum are encoded as a top-level
+ // string which JSON can't handle, so we need to wrap it in an array
+ // We need this: https://forums.swift.org/t/allowing-top-level-fragments-in-jsondecoder/11750
+ let data = try JSONEncoder().encode([value])
+ return String(String(data: data, encoding: .utf8)!.dropFirst().dropLast())
+ } catch {
+ print(error)
+ return nil
+ }
+ }
+
+ private func _set(_ key: String, to value: Value) {
+ if (value as? _DefaultsOptionalType)?.isNil == true {
+ removeObject(forKey: key)
+ return
+ }
+
+ if UserDefaults.isNativelySupportedType(Value.self) {
+ set(value, forKey: key)
+ return
+ }
+
+ set(_encode(value), forKey: key)
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ private func _set(_ key: String, to value: Value) {
+ // TODO: Handle nil here too.
+ if UserDefaults.isNativelySupportedType(Value.self) {
+ set(value, forKey: key)
+ return
+ }
+
+ set(try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true), forKey: key)
+ }
+
+ public subscript(key: Defaults.Key) -> Value {
+ get { _get(key.name) ?? key.defaultValue }
+ set {
+ _set(key.name, to: newValue)
+ }
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public subscript(key: Defaults.NSSecureCodingKey) -> Value {
+ get { _get(key.name) ?? key.defaultValue }
+ set {
+ _set(key.name, to: newValue)
+ }
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ public subscript(key: Defaults.NSSecureCodingOptionalKey) -> Value? {
+ get { _get(key.name) }
+ set {
+ guard let value = newValue else {
+ set(nil, forKey: key.name)
+ return
+ }
+
+ _set(key.name, to: value)
+ }
+ }
+
+ static func isNativelySupportedType(_ type: T.Type) -> Bool {
+ switch type {
+ case
+ is Bool.Type,
+ is Bool?.Type, // swiftlint:disable:this discouraged_optional_boolean
+ is String.Type,
+ is String?.Type,
+ is Int.Type,
+ is Int?.Type,
+ is Double.Type,
+ is Double?.Type,
+ is Float.Type,
+ is Float?.Type,
+ is Date.Type,
+ is Date?.Type,
+ is Data.Type,
+ is Data?.Type:
+ return true
+ default:
+ return false
+ }
+ }
+}
+
+extension UserDefaults {
+ /**
+ Remove all entries.
+
+ - Note: This only removes user-defined entries. System-defined entries will remain.
+ */
+ public func removeAll() {
+ for key in dictionaryRepresentation().keys {
+ removeObject(forKey: key)
+ }
+ }
+}
diff --git a/Dependencies/Defaults/Sources/Defaults/Utilities.swift b/Dependencies/Defaults/Sources/Defaults/Utilities.swift
new file mode 100644
index 00000000..0746b7ad
--- /dev/null
+++ b/Dependencies/Defaults/Sources/Defaults/Utilities.swift
@@ -0,0 +1,148 @@
+import Foundation
+
+extension Decodable {
+ init?(jsonData: Data) {
+ guard let value = try? JSONDecoder().decode(Self.self, from: jsonData) else {
+ return nil
+ }
+
+ self = value
+ }
+
+ init?(jsonString: String) {
+ guard let data = jsonString.data(using: .utf8) else {
+ return nil
+ }
+
+ self.init(jsonData: data)
+ }
+}
+
+
+final class ObjectAssociation {
+ subscript(index: AnyObject) -> T? {
+ get {
+ objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T?
+ }
+ set {
+ objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+ }
+ }
+}
+
+
+/**
+Causes a given target object to live at least as long as a given owner object.
+*/
+final class LifetimeAssociation {
+ private class ObjectLifetimeTracker {
+ var object: AnyObject?
+ var deinitHandler: () -> Void
+
+ init(for weaklyHeldObject: AnyObject, deinitHandler: @escaping () -> Void) {
+ self.object = weaklyHeldObject
+ self.deinitHandler = deinitHandler
+ }
+
+ deinit {
+ deinitHandler()
+ }
+ }
+
+ private static let associatedObjects = ObjectAssociation<[ObjectLifetimeTracker]>()
+ private weak var wrappedObject: ObjectLifetimeTracker?
+ private weak var owner: AnyObject?
+
+ /**
+ Causes the given target object to live at least as long as either the given owner object or the resulting `LifetimeAssociation`, whichever is deallocated first.
+
+ When either the owner or the new `LifetimeAssociation` is destroyed, the given deinit handler, if any, is called.
+
+ ```
+ class Ghost {
+ var association: LifetimeAssociation?
+
+ func haunt(_ host: Furniture) {
+ association = LifetimeAssociation(of: self, with: host) { [weak self] in
+ // Host has been deinitialized
+ self?.haunt(seekHost())
+ }
+ }
+ }
+
+ let piano = Piano()
+ Ghost().haunt(piano)
+ // The Ghost will remain alive as long as `piano` remains alive.
+ ```
+
+ - Parameter target: The object whose lifetime will be extended.
+ - Parameter owner: The object whose lifetime extends the target object's lifetime.
+ - Parameter deinitHandler: An optional closure to call when either `owner` or the resulting `LifetimeAssociation` is deallocated.
+ */
+ init(of target: AnyObject, with owner: AnyObject, deinitHandler: @escaping () -> Void = {}) {
+ let wrappedObject = ObjectLifetimeTracker(for: target, deinitHandler: deinitHandler)
+
+ let associatedObjects = LifetimeAssociation.associatedObjects[owner] ?? []
+ LifetimeAssociation.associatedObjects[owner] = associatedObjects + [wrappedObject]
+
+ self.wrappedObject = wrappedObject
+ self.owner = owner
+ }
+
+ /**
+ Invalidates the association, unlinking the target object's lifetime from that of the owner object. The provided deinit handler is not called.
+ */
+ func cancel() {
+ wrappedObject?.deinitHandler = {}
+ invalidate()
+ }
+
+ deinit {
+ invalidate()
+ }
+
+ private func invalidate() {
+ guard
+ let owner = owner,
+ let wrappedObject = wrappedObject,
+ var associatedObjects = LifetimeAssociation.associatedObjects[owner],
+ let wrappedObjectAssociationIndex = associatedObjects.firstIndex(where: { $0 === wrappedObject })
+ else {
+ return
+ }
+
+ associatedObjects.remove(at: wrappedObjectAssociationIndex)
+ LifetimeAssociation.associatedObjects[owner] = associatedObjects
+ self.owner = nil
+ }
+}
+
+
+/// A protocol for making generic type constraints of optionals.
+/// - Note: It's intentionally not including `associatedtype Wrapped` as that limits a lot of the use-cases.
+public protocol _DefaultsOptionalType: ExpressibleByNilLiteral {
+ /// This is useful as you can't compare `_OptionalType` to `nil`.
+ var isNil: Bool { get }
+}
+
+extension Optional: _DefaultsOptionalType {
+ public var isNil: Bool { self == nil }
+}
+
+func isOptionalType(_ type: T.Type) -> Bool {
+ type is _DefaultsOptionalType.Type
+}
+
+
+extension DispatchQueue {
+ /**
+ Performs the `execute` closure immediately if we're on the main thread or asynchronously puts it on the main thread otherwise.
+ */
+ static func mainSafeAsync(execute work: @escaping () -> Void) {
+ if Thread.isMainThread {
+ work()
+ } else {
+ main.async(execute: work)
+ }
+ }
+}
diff --git a/Dependencies/Defaults/Tests/DefaultsTests/DefaultsTests.swift b/Dependencies/Defaults/Tests/DefaultsTests/DefaultsTests.swift
new file mode 100644
index 00000000..9d4d4423
--- /dev/null
+++ b/Dependencies/Defaults/Tests/DefaultsTests/DefaultsTests.swift
@@ -0,0 +1,813 @@
+import Foundation
+import CoreData
+import Combine
+import XCTest
+import Defaults
+
+let fixtureURL = URL(string: "https://sindresorhus.com")!
+let fixtureURL2 = URL(string: "https://example.com")!
+
+enum FixtureEnum: String, Codable {
+ case tenMinutes = "10 Minutes"
+ case halfHour = "30 Minutes"
+ case oneHour = "1 Hour"
+}
+
+let fixtureDate = Date()
+
+@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+final class ExamplePersistentHistory: NSPersistentHistoryToken {
+ let value: String
+
+ init(value: String) {
+ self.value = value
+ super.init()
+ }
+
+ required init?(coder: NSCoder) {
+ self.value = coder.decodeObject(forKey: "value") as! String
+ super.init()
+ }
+
+ override func encode(with coder: NSCoder) {
+ coder.encode(value, forKey: "value")
+ }
+
+ override class var supportsSecureCoding: Bool { true }
+}
+
+extension Defaults.Keys {
+ static let key = Key("key", default: false)
+ static let url = Key("url", default: fixtureURL)
+ static let `enum` = Key("enum", default: .oneHour)
+ static let data = Key("data", default: Data([]))
+ static let date = Key("date", default: fixtureDate)
+
+ // NSSecureCoding
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ static let persistentHistoryValue = ExamplePersistentHistory(value: "ExampleToken")
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ static let persistentHistory = NSSecureCodingKey("persistentHistory", default: persistentHistoryValue)
+}
+
+final class DefaultsTests: XCTestCase {
+ override func setUp() {
+ super.setUp()
+ Defaults.removeAll()
+ }
+
+ override func tearDown() {
+ super.setUp()
+ Defaults.removeAll()
+ }
+
+ func testKey() {
+ let key = Defaults.Key("independentKey", default: false)
+ XCTAssertFalse(Defaults[key])
+ Defaults[key] = true
+ XCTAssertTrue(Defaults[key])
+ }
+
+ func testOptionalKey() {
+ let key = Defaults.Key("independentOptionalKey")
+ XCTAssertNil(Defaults[key])
+ Defaults[key] = true
+ XCTAssertTrue(Defaults[key]!)
+ Defaults[key] = nil
+ XCTAssertNil(Defaults[key])
+ Defaults[key] = false
+ XCTAssertFalse(Defaults[key]!)
+ }
+
+ func testKeyRegistersDefault() {
+ let keyName = "registersDefault"
+ XCTAssertEqual(UserDefaults.standard.bool(forKey: keyName), false)
+ _ = Defaults.Key(keyName, default: true)
+ XCTAssertEqual(UserDefaults.standard.bool(forKey: keyName), true)
+
+ // Test that it works with multiple keys with `Defaults`.
+ let keyName2 = "registersDefault2"
+ _ = Defaults.Key(keyName2, default: keyName2)
+ XCTAssertEqual(UserDefaults.standard.string(forKey: keyName2), keyName2)
+ }
+
+ func testKeyWithUserDefaultSubscript() {
+ let key = Defaults.Key("keyWithUserDeaultSubscript", default: false)
+ XCTAssertFalse(UserDefaults.standard[key])
+ UserDefaults.standard[key] = true
+ XCTAssertTrue(UserDefaults.standard[key])
+ }
+
+ func testKeys() {
+ XCTAssertFalse(Defaults[.key])
+ Defaults[.key] = true
+ XCTAssertTrue(Defaults[.key])
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ func testNSSecureCodingKeys() {
+ XCTAssertEqual(Defaults.Keys.persistentHistoryValue.value, Defaults[.persistentHistory].value)
+ let newPersistentHistory = ExamplePersistentHistory(value: "NewValue")
+ Defaults[.persistentHistory] = newPersistentHistory
+ XCTAssertEqual(newPersistentHistory.value, Defaults[.persistentHistory].value)
+ }
+
+ func testUrlType() {
+ XCTAssertEqual(Defaults[.url], fixtureURL)
+
+ let newUrl = URL(string: "https://twitter.com")!
+ Defaults[.url] = newUrl
+ XCTAssertEqual(Defaults[.url], newUrl)
+ }
+
+ func testEnumType() {
+ XCTAssertEqual(Defaults[.enum], FixtureEnum.oneHour)
+ }
+
+ func testDataType() {
+ XCTAssertEqual(Defaults[.data], Data([]))
+
+ let newData = Data([0xFF])
+ Defaults[.data] = newData
+ XCTAssertEqual(Defaults[.data], newData)
+ }
+
+ func testDateType() {
+ XCTAssertEqual(Defaults[.date], fixtureDate)
+
+ let newDate = Date()
+ Defaults[.date] = newDate
+ XCTAssertEqual(Defaults[.date], newDate)
+ }
+
+ func testRemoveAll() {
+ let key = Defaults.Key("removeAll", default: false)
+ let key2 = Defaults.Key("removeAll2", default: false)
+ Defaults[key] = true
+ Defaults[key2] = true
+ XCTAssertTrue(Defaults[key])
+ XCTAssertTrue(Defaults[key2])
+ Defaults.removeAll()
+ XCTAssertFalse(Defaults[key])
+ XCTAssertFalse(Defaults[key2])
+ }
+
+ func testCustomSuite() {
+ let customSuite = UserDefaults(suiteName: "com.sindresorhus.customSuite")!
+ let key = Defaults.Key("customSuite", default: false, suite: customSuite)
+ XCTAssertFalse(customSuite[key])
+ XCTAssertFalse(Defaults[key])
+ Defaults[key] = true
+ XCTAssertTrue(customSuite[key])
+ XCTAssertTrue(Defaults[key])
+ Defaults.removeAll(suite: customSuite)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveKeyCombine() {
+ let key = Defaults.Key("observeKey", default: false)
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults
+ .publisher(key, options: [])
+ .map { ($0.oldValue, $0.newValue) }
+ .collect(2)
+
+ let cancellable = publisher.sink { tuples in
+ for (i, expected) in [(false, true), (true, false)].enumerated() {
+ XCTAssertEqual(expected.0, tuples[i].0)
+ XCTAssertEqual(expected.1, tuples[i].1)
+ }
+
+ expect.fulfill()
+ }
+
+ Defaults[key] = true
+ Defaults.reset(key)
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveNSSecureCodingKeyCombine() {
+ let key = Defaults.NSSecureCodingKey("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue"))
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults
+ .publisher(key, options: [])
+ .map { ($0.oldValue.value, $0.newValue.value) }
+ .collect(3)
+
+ let expectedValues = [
+ ("TestValue", "NewTestValue"),
+ ("NewTestValue", "NewTestValue2"),
+ ("NewTestValue2", "TestValue")
+ ]
+
+ let cancellable = publisher.sink { actualValues in
+ for (expected, actual) in zip(expectedValues, actualValues) {
+ XCTAssertEqual(expected.0, actual.0)
+ XCTAssertEqual(expected.1, actual.1)
+ }
+
+ expect.fulfill()
+ }
+
+ Defaults[key] = ExamplePersistentHistory(value: "NewTestValue")
+ Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2")
+ Defaults.reset(key)
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveOptionalKeyCombine() {
+ let key = Defaults.Key("observeOptionalKey")
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults
+ .publisher(key, options: [])
+ .map { ($0.oldValue, $0.newValue) }
+ .collect(3)
+
+ let expectedValues: [(Bool?, Bool?)] = [(nil, true), (true, false), (false, nil)]
+
+ let cancellable = publisher.sink { actualValues in
+ for (expected, actual) in zip(expectedValues, actualValues) {
+ XCTAssertEqual(expected.0, actual.0)
+ XCTAssertEqual(expected.1, actual.1)
+ }
+
+ expect.fulfill()
+ }
+
+ Defaults[key] = true
+ Defaults[key] = false
+ Defaults.reset(key)
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveNSSecureCodingOptionalKeyCombine() {
+ let key = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingOptionalKey")
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults
+ .publisher(key, options: [])
+ .map { ($0.oldValue?.value, $0.newValue?.value) }
+ .collect(3)
+
+ let expectedValues: [(String?, String?)] = [
+ (nil, "NewTestValue"),
+ ("NewTestValue", "NewTestValue2"),
+ ("NewTestValue2", nil)
+ ]
+
+ let cancellable = publisher.sink { actualValues in
+ for (expected, actual) in zip(expectedValues, actualValues) {
+ XCTAssertEqual(expected.0, actual.0)
+ XCTAssertEqual(expected.1, actual.1)
+ }
+
+ expect.fulfill()
+ }
+
+ XCTAssertNil(Defaults[key])
+ Defaults[key] = ExamplePersistentHistory(value: "NewTestValue")
+ Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2")
+ Defaults.reset(key)
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveMultipleKeysCombine() {
+ let key1 = Defaults.Key("observeKey1", default: "x")
+ let key2 = Defaults.Key("observeKey2", default: true)
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2)
+
+ let cancellable = publisher.sink { _ in
+ expect.fulfill()
+ }
+
+ Defaults[key1] = "y"
+ Defaults[key2] = false
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveMultipleNSSecureKeysCombine() {
+ let key1 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey1", default: ExamplePersistentHistory(value: "TestValue"))
+ let key2 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey2", default: ExamplePersistentHistory(value: "TestValue"))
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2)
+
+ let cancellable = publisher.sink { _ in
+ expect.fulfill()
+ }
+
+ Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1")
+ Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2")
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveMultipleOptionalKeysCombine() {
+ let key1 = Defaults.Key("observeOptionalKey1")
+ let key2 = Defaults.Key("observeOptionalKey2")
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2)
+
+ let cancellable = publisher.sink { _ in
+ expect.fulfill()
+ }
+
+ Defaults[key1] = "x"
+ Defaults[key2] = false
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObserveMultipleNSSecureOptionalKeysCombine() {
+ let key1 = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingKey1")
+ let key2 = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingKey2")
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2)
+
+ let cancellable = publisher.sink { _ in
+ expect.fulfill()
+ }
+
+ Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1")
+ Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2")
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testReceiveValueBeforeSubscriptionCombine() {
+ let key = Defaults.Key("receiveValueBeforeSubscription", default: "hello")
+ let expect = expectation(description: "Observation closure being called")
+
+ let publisher = Defaults
+ .publisher(key)
+ .map(\.newValue)
+ .eraseToAnyPublisher()
+ .collect(2)
+
+ let cancellable = publisher.sink { values in
+ XCTAssertEqual(["hello", "world"], values)
+ expect.fulfill()
+ }
+
+ Defaults[key] = "world"
+ cancellable.cancel()
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObserveKey() {
+ let key = Defaults.Key("observeKey", default: false)
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { change in
+ XCTAssertFalse(change.oldValue)
+ XCTAssertTrue(change.newValue)
+ observation.invalidate()
+ expect.fulfill()
+ }
+
+ Defaults[key] = true
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ func testObserveNSSecureCodingKey() {
+ let key = Defaults.NSSecureCodingKey("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue"))
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { change in
+ XCTAssertEqual(change.oldValue.value, "TestValue")
+ XCTAssertEqual(change.newValue.value, "NewTestValue")
+ observation.invalidate()
+ expect.fulfill()
+ }
+
+ Defaults[key] = ExamplePersistentHistory(value: "NewTestValue")
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObserveOptionalKey() {
+ let key = Defaults.Key("observeOptionalKey")
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { change in
+ XCTAssertNil(change.oldValue)
+ XCTAssertTrue(change.newValue!)
+ observation.invalidate()
+ expect.fulfill()
+ }
+
+ Defaults[key] = true
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ func testObserveNSSecureCodingOptionalKey() {
+ let key = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingOptionalKey")
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { change in
+ XCTAssertNil(change.oldValue)
+ XCTAssertEqual(change.newValue?.value, "NewOptionalValue")
+ observation.invalidate()
+ expect.fulfill()
+ }
+
+ Defaults[key] = ExamplePersistentHistory(value: "NewOptionalValue")
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObserveMultipleKeys() {
+ let key1 = Defaults.Key("observeKey1", default: "x")
+ let key2 = Defaults.Key("observeKey2", default: true)
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ var counter = 0
+ observation = Defaults.observe(keys: key1, key2, options: []) {
+ counter += 1
+ if counter == 2 {
+ expect.fulfill()
+ } else if counter > 2 {
+ XCTFail()
+ }
+ }
+
+ Defaults[key1] = "y"
+ Defaults[key2] = false
+ observation.invalidate()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
+ func testObserveMultipleNSSecureKeys() {
+ let key1 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey1", default: ExamplePersistentHistory(value: "TestValue"))
+ let key2 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey2", default: ExamplePersistentHistory(value: "TestValue"))
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ var counter = 0
+ observation = Defaults.observe(keys: key1, key2, options: []) {
+ counter += 1
+ if counter == 2 {
+ expect.fulfill()
+ } else if counter > 2 {
+ XCTFail()
+ }
+ }
+
+ Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1")
+ Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2")
+ observation.invalidate()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObserveKeyURL() {
+ let fixtureURL = URL(string: "https://sindresorhus.com")!
+ let fixtureURL2 = URL(string: "https://example.com")!
+ let key = Defaults.Key("observeKeyURL", default: fixtureURL)
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { change in
+ XCTAssertEqual(change.oldValue, fixtureURL)
+ XCTAssertEqual(change.newValue, fixtureURL2)
+ observation.invalidate()
+ expect.fulfill()
+ }
+
+ Defaults[key] = fixtureURL2
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObserveKeyEnum() {
+ let key = Defaults.Key("observeKeyEnum", default: .oneHour)
+ let expect = expectation(description: "Observation closure being called")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { change in
+ XCTAssertEqual(change.oldValue, .oneHour)
+ XCTAssertEqual(change.newValue, .tenMinutes)
+ observation.invalidate()
+ expect.fulfill()
+ }
+
+ Defaults[key] = .tenMinutes
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObservePreventPropagation() {
+ let key1 = Defaults.Key("preventPropagation0", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ var observation: Defaults.Observation!
+ var wasInside = false
+ observation = Defaults.observe(key1, options: []) { _ in
+ XCTAssertFalse(wasInside)
+ wasInside = true
+ Defaults.withoutPropagation {
+ Defaults[key1] = true
+ }
+ expect.fulfill()
+ }
+
+ Defaults[key1] = false
+ observation.invalidate()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObservePreventPropagationMultipleKeys() {
+ let key1 = Defaults.Key("preventPropagation1", default: nil)
+ let key2 = Defaults.Key("preventPropagation2", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ var observation: Defaults.Observation!
+ var wasInside = false
+ observation = Defaults.observe(keys: key1, key2, options: []) {
+ XCTAssertFalse(wasInside)
+ wasInside = true
+ Defaults.withoutPropagation {
+ Defaults[key1] = true
+ }
+ expect.fulfill()
+ }
+
+ Defaults[key1] = false
+ observation.invalidate()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ // This checks if the callback is still being called if the value is changed on a second thread while the initial thread is doing some long running task.
+ func testObservePreventPropagationMultipleThreads() {
+ let key1 = Defaults.Key("preventPropagation3", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ var observation: Defaults.Observation!
+ observation = Defaults.observe(key1, options: []) { _ in
+ Defaults.withoutPropagation {
+ Defaults[key1]! += 1
+ }
+ print("--- Main Thread: \(Thread.isMainThread)")
+ if !Thread.isMainThread {
+ XCTAssert(Defaults[key1]! == 4)
+ expect.fulfill()
+ } else {
+ usleep(100000)
+ print("--- Release: \(Thread.isMainThread)")
+ }
+ }
+ DispatchQueue.global().asyncAfter(deadline: .now() + 0.05) {
+ Defaults[key1]! += 1
+ }
+ Defaults[key1] = 1
+ observation.invalidate()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ // Check if propagation prevention works across multiple observations.
+ func testObservePreventPropagationMultipleObservations() {
+ let key1 = Defaults.Key("preventPropagation4", default: nil)
+ let key2 = Defaults.Key("preventPropagation5", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ let observation1 = Defaults.observe(key2, options: []) { _ in
+ XCTFail()
+ }
+ let observation2 = Defaults.observe(keys: key1, key2, options: []) {
+ Defaults.withoutPropagation {
+ Defaults[key2] = true
+ }
+ expect.fulfill()
+ }
+
+ Defaults[key1] = false
+ observation1.invalidate()
+ observation2.invalidate()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObservePreventPropagationCombine() {
+ let key1 = Defaults.Key("preventPropagation6", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ var wasInside = false
+ let cancellable = Defaults.publisher(key1, options: []).sink { _ in
+ XCTAssertFalse(wasInside)
+ wasInside = true
+ Defaults.withoutPropagation {
+ Defaults[key1] = true
+ }
+ expect.fulfill()
+ }
+
+ Defaults[key1] = false
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObservePreventPropagationMultipleKeysCombine() {
+ let key1 = Defaults.Key("preventPropagation7", default: nil)
+ let key2 = Defaults.Key("preventPropagation8", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ var wasInside = false
+ let cancellable = Defaults.publisher(keys: key1, key2, options: []).sink { _ in
+ XCTAssertFalse(wasInside)
+ wasInside = true
+ Defaults.withoutPropagation {
+ Defaults[key1] = true
+ }
+ expect.fulfill()
+ }
+
+ Defaults[key2] = false
+ cancellable.cancel()
+
+ waitForExpectations(timeout: 10)
+ }
+
+ @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
+ func testObservePreventPropagationModifiersCombine() {
+ let key1 = Defaults.Key("preventPropagation9", default: nil)
+ let expect = expectation(description: "No infinite recursion")
+
+ var wasInside = false
+ var cancellable: AnyCancellable!
+ cancellable = Defaults.publisher(key1, options: [])
+ .receive(on: DispatchQueue.main)
+ .delay(for: 0.5, scheduler: DispatchQueue.global())
+ .sink { _ in
+ XCTAssertFalse(wasInside)
+ wasInside = true
+ Defaults.withoutPropagation {
+ Defaults[key1] = true
+ }
+ expect.fulfill()
+ cancellable.cancel()
+ }
+
+ Defaults[key1] = false
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testResetKey() {
+ let defaultFixture1 = "foo1"
+ let defaultFixture2 = 0
+ let defaultFixture3 = "foo3"
+ let newFixture1 = "bar1"
+ let newFixture2 = 1
+ let newFixture3 = "bar3"
+ let key1 = Defaults.Key("key1", default: defaultFixture1)
+ let key2 = Defaults.Key("key2", default: defaultFixture2)
+ Defaults[key1] = newFixture1
+ Defaults[key2] = newFixture2
+ Defaults.reset(key1)
+ XCTAssertEqual(Defaults[key1], defaultFixture1)
+ XCTAssertEqual(Defaults[key2], newFixture2)
+
+ if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) {
+ let key3 = Defaults.NSSecureCodingKey("key3", default: ExamplePersistentHistory(value: defaultFixture3))
+ Defaults[key3] = ExamplePersistentHistory(value: newFixture3)
+ Defaults.reset(key3)
+
+ XCTAssertEqual(Defaults[key3].value, defaultFixture3)
+ }
+ }
+
+ func testResetMultipleKeys() {
+ let defaultFxiture1 = "foo1"
+ let defaultFixture2 = 0
+ let defaultFixture3 = "foo3"
+ let newFixture1 = "bar1"
+ let newFixture2 = 1
+ let newFixture3 = "bar3"
+ let key1 = Defaults.Key("akey1", default: defaultFxiture1)
+ let key2 = Defaults.Key("akey2", default: defaultFixture2)
+ let key3 = Defaults.Key("akey3", default: defaultFixture3)
+ Defaults[key1] = newFixture1
+ Defaults[key2] = newFixture2
+ Defaults[key3] = newFixture3
+ Defaults.reset(key1, key2)
+ XCTAssertEqual(Defaults[key1], defaultFxiture1)
+ XCTAssertEqual(Defaults[key2], defaultFixture2)
+ XCTAssertEqual(Defaults[key3], newFixture3)
+ }
+
+ func testResetOptionalKey() {
+ let newString1 = "bar1"
+ let newString2 = "bar2"
+ let newString3 = "bar3"
+ let key1 = Defaults.Key("optionalKey1")
+ let key2 = Defaults.Key("optionalKey2")
+ Defaults[key1] = newString1
+ Defaults[key2] = newString2
+ Defaults.reset(key1)
+ XCTAssertEqual(Defaults[key1], nil)
+ XCTAssertEqual(Defaults[key2], newString2)
+
+ if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) {
+ let key3 = Defaults.NSSecureCodingOptionalKey("optionalKey3")
+ Defaults[key3] = ExamplePersistentHistory(value: newString3)
+ Defaults.reset(key3)
+ XCTAssertEqual(Defaults[key3], nil)
+ }
+ }
+
+ func testResetMultipleOptionalKeys() {
+ let newFixture1 = "bar1"
+ let newFixture2 = 1
+ let newFixture3 = "bar3"
+ let key1 = Defaults.Key("aoptionalKey1")
+ let key2 = Defaults.Key("aoptionalKey2")
+ let key3 = Defaults.Key("aoptionalKey3")
+ Defaults[key1] = newFixture1
+ Defaults[key2] = newFixture2
+ Defaults[key3] = newFixture3
+ Defaults.reset(key1, key2)
+ XCTAssertEqual(Defaults[key1], nil)
+ XCTAssertEqual(Defaults[key2], nil)
+ XCTAssertEqual(Defaults[key3], newFixture3)
+ }
+
+ func testObserveWithLifetimeTie() {
+ let key = Defaults.Key("lifetimeTie", default: false)
+ let expect = expectation(description: "Observation closure being called")
+
+ weak var observation: Defaults.Observation!
+ observation = Defaults.observe(key, options: []) { _ in
+ observation.invalidate()
+ expect.fulfill()
+ }.tieToLifetime(of: self)
+
+ Defaults[key] = true
+
+ waitForExpectations(timeout: 10)
+ }
+
+ func testObserveWithLifetimeTieManualBreak() {
+ let key = Defaults.Key("lifetimeTieManualBreak", default: false)
+
+ weak var observation: Defaults.Observation? = Defaults.observe(key, options: []) { _ in }.tieToLifetime(of: self)
+ observation!.removeLifetimeTie()
+
+ for i in 1...10 {
+ if observation == nil {
+ break
+ }
+
+ sleep(1)
+
+ if i == 10 {
+ XCTFail()
+ }
+ }
+ }
+}
diff --git a/Dependencies/Defaults/license b/Dependencies/Defaults/license
new file mode 100644
index 00000000..e7af2f77
--- /dev/null
+++ b/Dependencies/Defaults/license
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+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.
diff --git a/Dependencies/Defaults/readme.md b/Dependencies/Defaults/readme.md
new file mode 100644
index 00000000..f24a6963
--- /dev/null
+++ b/Dependencies/Defaults/readme.md
@@ -0,0 +1,558 @@
+# Defaults [](https://travis-ci.org/sindresorhus/Defaults)
+
+> Swifty and modern [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults)
+
+Store key-value pairs persistently across launches of your app.
+
+It uses `NSUserDefaults` underneath but exposes a type-safe facade with lots of nice conveniences.
+
+It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gifski), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), [Battery Indicator](https://sindresorhus.com/battery-indicator), and [HEIC Converter](https://sindresorhus.com/heic-converter).
+
+For a real-world example, see my [Plash app](https://github.com/sindresorhus/Plash/blob/533dbc888d8ba3bd9581e60320af282a22c53f85/Plash/Constants.swift#L9-L18).
+
+## Highlights
+
+- **Strongly typed:** You declare the type and default value upfront.
+- **Codable support:** You can store any [Codable](https://developer.apple.com/documentation/swift/codable) value, like an enum.
+- **NSSecureCoding support:** You can store any [NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding) value.
+- **SwiftUI:** Property wrapper that updates the view when the `UserDefaults` value changes.
+- **Publishers:** Combine publishers built-in.
+- **Observation:** Observe changes to keys.
+- **Debuggable:** The data is stored as JSON-serialized values.
+
+## Compatibility
+
+- macOS 10.12+
+- iOS 10+
+- tvOS 10+
+- watchOS 3+
+
+## Install
+
+#### Swift Package Manager
+
+Add `https://github.com/sindresorhus/Defaults` in the [“Swift Package Manager” tab in Xcode](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app).
+
+You also need to set the build setting “Other Linker Flags” to `-weak_framework Combine` to work around [this Xcode bug](https://github.com/feedback-assistant/reports/issues/44).
+
+#### Carthage
+
+```
+github "sindresorhus/Defaults"
+```
+
+#### CocoaPods
+
+```ruby
+pod 'Defaults'
+```
+
+## Usage
+
+You declare the defaults keys upfront with type and default value.
+
+```swift
+import Cocoa
+import Defaults
+
+extension Defaults.Keys {
+ static let quality = Key("quality", default: 0.8)
+ // ^ ^ ^ ^
+ // Key Type UserDefaults name Default value
+}
+```
+
+You can then access it as a subscript on the `Defaults` global:
+
+```swift
+Defaults[.quality]
+//=> 0.8
+
+Defaults[.quality] = 0.5
+//=> 0.5
+
+Defaults[.quality] += 0.1
+//=> 0.6
+
+Defaults[.quality] = "🦄"
+//=> [Cannot assign value of type 'String' to type 'Double']
+```
+
+You can also declare optional keys for when you don't want to declare a default value upfront:
+
+```swift
+extension Defaults.Keys {
+ static let name = Key("name")
+}
+
+if let name = Defaults[.name] {
+ print(name)
+}
+```
+
+The default value is then `nil`.
+
+---
+
+If you have `NSSecureCoding` classes which you want to save, you can use them as follows:
+
+```swift
+extension Defaults.Keys {
+ static let someSecureCoding = NSSecureCodingKey("someSecureCoding", default: SomeNSSecureCodingClass(string: "Default", int: 5, bool: true))
+ static let someOptionalSecureCoding = NSSecureCodingOptionalKey("someOptionalSecureCoding")
+}
+
+Defaults[.someSecureCoding].string
+//=> "Default"
+
+Defaults[.someSecureCoding].int
+//=> 5
+
+Defaults[.someSecureCoding].bool
+//=> true
+```
+
+You can use those keys just like in all the other examples. The return value will be your `NSSecureCoding` class.
+
+### Enum example
+
+```swift
+enum DurationKeys: String, Codable {
+ case tenMinutes = "10 Minutes"
+ case halfHour = "30 Minutes"
+ case oneHour = "1 Hour"
+}
+
+extension Defaults.Keys {
+ static let defaultDuration = Key("defaultDuration", default: .oneHour)
+}
+
+Defaults[.defaultDuration].rawValue
+//=> "1 Hour"
+```
+
+### Use keys directly
+
+You are not required to attach keys to `Defaults.Keys`.
+
+```swift
+let isUnicorn = Defaults.Key("isUnicorn", default: true)
+
+Defaults[isUnicorn]
+//=> true
+```
+
+### SwiftUI support
+
+You can use the `@Default` property wrapper to get/set a `Defaults` item and also have the view be updated when the value changes. This is similar to `@State`.
+
+```swift
+extension Defaults.Keys {
+ static let hasUnicorn = Key("hasUnicorn", default: false)
+}
+
+struct ContentView: View {
+ @Default(.hasUnicorn) var hasUnicorn
+
+ var body: some View {
+ Text("Has Unicorn: \(hasUnicorn)")
+ Toggle("Toggle Unicorn", isOn: $hasUnicorn)
+ }
+}
+```
+
+Note that it's `@Default`, not `@Defaults`.
+
+You cannot use `@Default` in an `ObservableObject`. It's meant to be used in a `View`.
+
+This is only implemented for `Defaults.Key`. PR welcome for `Defaults.NSSecureCoding` if you need it.
+
+### Observe changes to a key
+
+```swift
+extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+}
+
+let observer = Defaults.observe(.isUnicornMode) { change in
+ // Initial event
+ print(change.oldValue)
+ //=> false
+ print(change.newValue)
+ //=> false
+
+ // First actual event
+ print(change.oldValue)
+ //=> false
+ print(change.newValue)
+ //=> true
+}
+
+Defaults[.isUnicornMode] = true
+```
+
+In contrast to the native `UserDefaults` key observation, here you receive a strongly-typed change object.
+
+There is also an observation API using the [Combine](https://developer.apple.com/documentation/combine) framework, exposing a [Publisher](https://developer.apple.com/documentation/combine/publisher) for key changes:
+
+```swift
+let publisher = Defaults.publisher(.isUnicornMode)
+
+let cancellable = publisher.sink { change in
+ // Initial event
+ print(change.oldValue)
+ //=> false
+ print(change.newValue)
+ //=> false
+
+ // First actual event
+ print(change.oldValue)
+ //=> false
+ print(change.newValue)
+ //=> true
+}
+
+Defaults[.isUnicornMode] = true
+
+// To invalidate the observation.
+cancellable.cancel()
+```
+
+### Invalidate observations automatically
+
+```swift
+extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+}
+
+final class Foo {
+ init() {
+ Defaults.observe(.isUnicornMode) { change in
+ print(change.oldValue)
+ print(change.newValue)
+ }.tieToLifetime(of: self)
+ }
+}
+
+Defaults[.isUnicornMode] = true
+```
+
+The observation will be valid until `self` is deinitialized.
+
+### Reset keys to their default values
+
+```swift
+extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: false)
+}
+
+Defaults[.isUnicornMode] = true
+//=> true
+
+Defaults.reset(.isUnicornMode)
+
+Defaults[.isUnicornMode]
+//=> false
+```
+
+This works for a `Key` with an optional too, which will be reset back to `nil`.
+
+### Control propagation of change events
+
+Changes made within the `Defaults.withoutPropagation` closure will not be propagated to observation callbacks (`Defaults.observe()` or `Defaults.publisher()`), and therefore could prevent infinite recursion.
+
+```swift
+let observer = Defaults.observe(keys: .key1, .key2) {
+ // …
+
+ Defaults.withoutPropagation {
+ // Update `.key1` without propagating the change to listeners.
+ Defaults[.key1] = 11
+ }
+
+ // This will be propagated.
+ Defaults[.someKey] = true
+ }
+```
+
+### It's just `UserDefaults` with sugar
+
+This works too:
+
+```swift
+extension Defaults.Keys {
+ static let isUnicorn = Key("isUnicorn", default: true)
+}
+
+UserDefaults.standard[.isUnicorn]
+//=> true
+```
+
+### Shared `UserDefaults`
+
+```swift
+let extensionDefaults = UserDefaults(suiteName: "com.unicorn.app")!
+
+extension Defaults.Keys {
+ static let isUnicorn = Key("isUnicorn", default: true, suite: extensionDefaults)
+}
+
+Defaults[.isUnicorn]
+//=> true
+
+// Or
+
+extensionDefaults[.isUnicorn]
+//=> true
+```
+
+### Default values are registered with `UserDefaults`
+
+When you create a `Defaults.Key`, it automatically registers the `default` value with normal `UserDefaults`. This means you can make use of the default value in, for example, bindings in Interface Builder.
+
+```swift
+extension Defaults.Keys {
+ static let isUnicornMode = Key("isUnicornMode", default: true)
+}
+
+print(UserDefaults.standard.bool(forKey: isUnicornMode.name))
+//=> true
+```
+
+## API
+
+### `Defaults`
+
+#### `Defaults.Keys`
+
+Type: `class`
+
+Stores the keys.
+
+#### `Defaults.Key` *(alias `Defaults.Keys.Key`)*
+
+```swift
+Defaults.Key(_ key: String, default: T, suite: UserDefaults = .standard)
+```
+
+Type: `class`
+
+Create a key with a default value.
+
+The default value is written to the actual `UserDefaults` and can be used elsewhere. For example, with a Interface Builder binding.
+
+#### `Defaults.NSSecureCodingKey` *(alias `Defaults.Keys.NSSecureCodingKey`)*
+
+```swift
+Defaults.NSSecureCodingKey(_ key: String, default: T, suite: UserDefaults = .standard)
+```
+
+Type: `class`
+
+Create a NSSecureCoding key with a default value.
+
+The default value is written to the actual `UserDefaults` and can be used elsewhere. For example, with a Interface Builder binding.
+
+#### `Defaults.NSSecureCodingOptionalKey` *(alias `Defaults.Keys.NSSecureCodingOptionalKey`)*
+
+```swift
+Defaults.NSSecureCodingOptionalKey(_ key: String, suite: UserDefaults = .standard)
+```
+
+Type: `class`
+
+Create a NSSecureCoding key with an optional value.
+
+#### `Defaults.reset(keys…)`
+
+Type: `func`
+
+Reset the given keys back to their default values.
+
+You can specify up to 10 keys. If you need to specify more, call this method multiple times.
+
+You can also specify string keys, which can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic.
+
+#### `Defaults.observe`
+
+```swift
+Defaults.observe(
+ _ key: Defaults.Key,
+ options: ObservationOptions = [.initial],
+ handler: @escaping (KeyChange) -> Void
+) -> Defaults.Observation
+```
+
+```swift
+Defaults.observe(
+ _ key: Defaults.NSSecureCodingKey,
+ options: ObservationOptions = [.initial],
+ handler: @escaping (NSSecureCodingKeyChange) -> Void
+) -> Defaults.Observation
+```
+
+```swift
+Defaults.observe(
+ _ key: Defaults.NSSecureCodingOptionalKey,
+ options: ObservationOptions = [.initial],
+ handler: @escaping (NSSecureCodingOptionalKeyChange) -> Void
+) -> Defaults.Observation
+```
+
+Type: `func`
+
+Observe changes to a key or an optional key.
+
+By default, it will also trigger an initial event on creation. This can be useful for setting default values on controls. You can override this behavior with the `options` argument.
+
+#### `Defaults.observe(keys: keys..., options:)`
+
+Type: `func`
+
+Observe multiple keys of any type, but without any information about the changes.
+
+Options are the same as in `.observe(…)` for a single key.
+
+#### `Defaults.publisher(_ key:, options:)`
+
+```swift
+Defaults.publisher(
+ _ key: Defaults.Key,
+ options: ObservationOptions = [.initial]
+) -> AnyPublisher, Never>
+```
+
+```swift
+Defaults.publisher(
+ _ key: Defaults.NSSecureCodingKey,
+ options: ObservationOptions = [.initial]
+) -> AnyPublisher, Never>
+```
+
+```swift
+Defaults.publisher(
+ _ key: Defaults.NSSecureCodingOptionalKey,
+ options: ObservationOptions = [.initial]
+) -> AnyPublisher, Never>
+```
+
+Type: `func`
+
+Observation API using [Publisher](https://developer.apple.com/documentation/combine/publisher) from the [Combine](https://developer.apple.com/documentation/combine) framework.
+
+Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+.
+
+#### `Defaults.publisher(keys: keys…, options:)`
+
+Type: `func`
+
+[Combine](https://developer.apple.com/documentation/combine) observation API for multiple key observation, but without specific information about changes.
+
+Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+.
+
+#### `Defaults.removeAll`
+
+```swift
+Defaults.removeAll(suite: UserDefaults = .standard)
+```
+
+Type: `func`
+
+Remove all entries from the given `UserDefaults` suite.
+
+### `Defaults.Observation`
+
+Type: `protocol`
+
+Represents an observation of a defaults key.
+
+#### `Defaults.Observation#invalidate`
+
+```swift
+Defaults.Observation#invalidate()
+```
+
+Type: `func`
+
+Invalidate the observation.
+
+#### `Defaults.Observation#tieToLifetime`
+
+```swift
+@discardableResult
+Defaults.Observation#tieToLifetime(of weaklyHeldObject: AnyObject) -> Self
+```
+
+Type: `func`
+
+Keep the observation alive for as long as, and no longer than, another object exists.
+
+When `weaklyHeldObject` is deinitialized, the observation is invalidated automatically.
+
+#### `Defaults.Observation.removeLifetimeTie`
+
+```swift
+Defaults.Observation#removeLifetimeTie()
+```
+
+Type: `func`
+
+Break the lifetime tie created by `tieToLifetime(of:)`, if one exists.
+
+The effects of any call to `tieToLifetime(of:)` are reversed. Note however that if the tied-to object has already died, then the observation is already invalid and this method has no logical effect.
+
+#### `Defaults.withoutPropagation(_ closure:)`
+
+Execute the closure without triggering change events.
+
+Any `Defaults` key changes made within the closure will not propagate to `Defaults` event listeners (`Defaults.observe()` and `Defaults.publisher()`). This can be useful to prevent infinite recursion when you want to change a key in the callback listening to changes for the same key.
+
+### `@Default(_ key:)`
+
+Get/set a `Defaults` item and also have the view be updated when the value changes.
+
+This is only implemented for `Defaults.Key`. PR welcome for `Defaults.NSSecureCoding` if you need it.
+
+## FAQ
+
+### How can I store a dictionary of arbitrary values?
+
+You cannot store `[String: Any]` directly as it cannot conform to `Codable`. However, you can use the [`AnyCodable`](https://github.com/Flight-School/AnyCodable) package to work around this `Codable` limitation:
+
+```swift
+import AnyCodable
+
+extension Defaults.Keys {
+ static let magic = Key<[String: AnyCodable]>("magic", default: [:])
+}
+
+// …
+
+Defaults[.magic]["unicorn"] = "🦄"
+
+if let value = Defaults[.magic]["unicorn"]?.value {
+ print(value)
+ //=> "🦄"
+}
+
+Defaults[.magic]["number"] = 3
+Defaults[.magic]["boolean"] = true
+```
+
+### How is this different from [`SwiftyUserDefaults`](https://github.com/radex/SwiftyUserDefaults)?
+
+It's inspired by that package and other solutions. The main difference is that this module doesn't hardcode the default values and comes with Codable support.
+
+## Maintainers
+
+- [Sindre Sorhus](https://github.com/sindresorhus)
+- [Kacper Rączy](https://github.com/fredyshox)
+
+## Related
+
+- [Preferences](https://github.com/sindresorhus/Preferences) - Add a preferences window to your macOS app
+- [KeyboardShortcuts](https://github.com/sindresorhus/KeyboardShortcuts) - Add user-customizable global keyboard shortcuts to your macOS app
+- [LaunchAtLogin](https://github.com/sindresorhus/LaunchAtLogin) - Add "Launch at Login" functionality to your macOS app
+- [DockProgress](https://github.com/sindresorhus/DockProgress) - Show progress in your app's Dock icon
+- [Gifski](https://github.com/sindresorhus/Gifski) - Convert videos to high-quality GIFs on your Mac
+- [More…](https://github.com/search?q=user%3Asindresorhus+language%3Aswift)
diff --git a/Dependencies/Haxcessibility.gitcheckout b/Dependencies/Haxcessibility.gitcheckout
new file mode 100644
index 00000000..f75045fd
--- /dev/null
+++ b/Dependencies/Haxcessibility.gitcheckout
@@ -0,0 +1 @@
+trunk:e27e9c79fc4413d97a29b5eb72ed454aeace94c4
diff --git a/Dependencies/Haxcessibility.giturl b/Dependencies/Haxcessibility.giturl
new file mode 100644
index 00000000..097046bb
--- /dev/null
+++ b/Dependencies/Haxcessibility.giturl
@@ -0,0 +1 @@
+git@github.com:numist/Haxcessibility.git
\ No newline at end of file
diff --git a/Dependencies/Haxcessibility/.gitignore b/Dependencies/Haxcessibility/.gitignore
new file mode 100644
index 00000000..8c171e4c
--- /dev/null
+++ b/Dependencies/Haxcessibility/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+xcuserdata
+*.mode*
+*.pbxuser
+*.xcuserdatad
+*.xccheckout
\ No newline at end of file
diff --git a/Dependencies/Haxcessibility/Classes/HAXApplication.h b/Dependencies/Haxcessibility/Classes/HAXApplication.h
new file mode 100644
index 00000000..3441bbb1
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXApplication.h
@@ -0,0 +1,21 @@
+// HAXApplication.h
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import
+
+#import
+
+@class HAXWindow;
+
+@interface HAXApplication : HAXElement
+
+@property (readonly, nullable) HAXWindow * focusedWindow;
+@property (readonly, nonnull) NSArray *windows;
+@property (readonly, nullable) NSString *localizedName;
+
++(nullable HAXApplication *)applicationWithPID:(pid_t)pid;
+
+-(nullable HAXWindow *)windowWithID:(CGWindowID)cgWindowID;
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXApplication.m b/Dependencies/Haxcessibility/Classes/HAXApplication.m
new file mode 100644
index 00000000..a57cff95
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXApplication.m
@@ -0,0 +1,45 @@
+// HAXApplication.m
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import "HAXApplication.h"
+#import "HAXElement+Protected.h"
+#import "HAXWindow.h"
+
+@implementation HAXApplication
+
++(HAXApplication *)applicationWithPID:(pid_t)pid {
+ AXUIElementRef app = AXUIElementCreateApplication(pid);
+ id result = nil;
+ if (app) {
+ result = [[HAXApplication alloc] initWithElementRef:app];
+ CFRelease(app);
+ }
+ return result;
+}
+
+-(HAXWindow *)focusedWindow {
+ return [self elementOfClass:[HAXWindow class] forKey:(NSString *)kAXFocusedWindowAttribute error:NULL];
+}
+
+-(NSArray *)windows {
+ NSArray *axWindowObjects = [self getAttributeValueForKey:(NSString *)kAXWindowsAttribute error:NULL];
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:[axWindowObjects count]];
+ for (id axObject in axWindowObjects) {
+ [result addObject:[[HAXWindow alloc] initWithElementRef:(AXUIElementRef)axObject]];
+ }
+ return result;
+}
+
+-(NSString *)localizedName {
+ return [self getAttributeValueForKey:(NSString *)kAXTitleAttribute error:NULL];
+}
+
+-(HAXWindow *)windowWithID:(CGWindowID)cgWindowID {
+ for (HAXWindow *window in self.windows) {
+ if (window.cgWindowID == cgWindowID) { return window; }
+ }
+ return nil;
+}
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXButton.h b/Dependencies/Haxcessibility/Classes/HAXButton.h
new file mode 100644
index 00000000..37e0e2b1
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXButton.h
@@ -0,0 +1,11 @@
+// HAXButton.h
+// Created by Kocsis Olivér on 2014-05-21
+// Copyright 2014 Joinect Technologies
+
+#import
+
+@interface HAXButton : HAXView
+
+-(void)press;
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXButton.m b/Dependencies/Haxcessibility/Classes/HAXButton.m
new file mode 100644
index 00000000..5155ef98
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXButton.m
@@ -0,0 +1,14 @@
+// HAXButton.m
+// Created by Kocsis Olivér on 2014-05-21
+// Copyright 2014 Joinect Technologies
+
+#import "HAXButton.h"
+#import "HAXElement+Protected.h"
+
+@implementation HAXButton
+
+-(void)press {
+ [self performAction:(__bridge NSString *)kAXPressAction error:NULL];
+}
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXElement+Protected.h b/Dependencies/Haxcessibility/Classes/HAXElement+Protected.h
new file mode 100644
index 00000000..7197d7f4
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXElement+Protected.h
@@ -0,0 +1,22 @@
+// HAXElement+Protected.h
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import
+
+#include
+
+@interface HAXElement ()
+
+-(instancetype)initWithElementRef:(AXUIElementRef)elementRef __attribute__((nonnull(1)));
+
+@property (nonatomic, readonly) AXUIElementRef elementRef __attribute__((NSObject));
+
+-(id)getAttributeValueForKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1)));
+-(CFTypeRef)copyAttributeValueForKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1)));
+-(BOOL)setAttributeValue:(CFTypeRef)value forKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1,2)));
+-(BOOL)performAction:(NSString *)action error:(NSError **)error __attribute__((nonnull(1)));
+
+-(id)elementOfClass:(Class)klass forKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1,2)));
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXElement.h b/Dependencies/Haxcessibility/Classes/HAXElement.h
new file mode 100644
index 00000000..6a5725e0
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXElement.h
@@ -0,0 +1,27 @@
+// HAXElement.h
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import
+
+@protocol HAXElementDelegate;
+
+@class HAXButton;
+
+@interface HAXElement : NSObject
+
+@property (nonatomic, nullable, weak) id delegate;
+@property (nonatomic, nullable, readonly) NSString *title;
+@property (nonatomic, nullable, readonly) NSString *role;
+@property (nonatomic, readonly) BOOL hasChildren;
+@property (nonatomic, nullable, readonly) NSArray *children;
+@property (nonatomic, nullable, readonly) NSArray *attributeNames;
+@property (nonatomic, nullable, readonly) NSArray *buttons;
+@property (nonatomic, readonly) pid_t processIdentifier;
+
+@end
+
+@protocol HAXElementDelegate
+@optional
+-(void)elementWasDestroyed:(nonnull HAXElement *)element;
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXElement.m b/Dependencies/Haxcessibility/Classes/HAXElement.m
new file mode 100644
index 00000000..bff21a43
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXElement.m
@@ -0,0 +1,230 @@
+// HAXElement.m
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import "HAXElement+Protected.h"
+#import "HAXButton.h"
+
+
+@interface HAXElement ()
+
+@property (nonatomic, strong) AXObserverRef observer __attribute__((NSObject));
+
+@end
+
+
+@implementation HAXElement
+
+-(instancetype)initWithElementRef:(AXUIElementRef)elementRef {
+ if((self = [super init])) {
+ _elementRef = CFRetain(elementRef);
+ }
+ return self;
+}
+
+-(void)dealloc {
+ if (_observer) {
+ [self removeAXObserver];
+ }
+ if (_elementRef) {
+ CFRelease(_elementRef);
+ _elementRef = NULL;
+ }
+}
+
+-(BOOL)isEqual:(id)object {
+ return
+ [object isKindOfClass:self.class]
+ && CFEqual(self.elementRef, [object elementRef]);
+}
+
+-(NSUInteger)hash {
+ return CFHash(self.elementRef);
+}
+
+-(void)setDelegate:(id)delegate {
+ if (delegate && !_observer) {
+ [self addAXObserver];
+ }
+ _delegate = delegate;
+}
+
+-(id)getAttributeValueForKey:(NSString *)key error:(NSError **)error {
+ CFTypeRef result = [self copyAttributeValueForKey:key error:error];
+ return result ? CFBridgingRelease(result) : nil;
+}
+
+-(CFTypeRef)copyAttributeValueForKey:(NSString *)key error:(NSError **)error {
+ NSParameterAssert(key != nil);
+ CFTypeRef attributeRef = NULL;
+ AXError result = AXUIElementCopyAttributeValue(self.elementRef, (__bridge CFStringRef)key, &attributeRef);
+ if((result != kAXErrorSuccess) && error) {
+ *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:result userInfo:@{
+ @"key": key,
+ @"elementRef": (id)self.elementRef}
+ ];
+ }
+ return attributeRef;
+}
+
+-(BOOL)setAttributeValue:(CFTypeRef)value forKey:(NSString *)key error:(NSError **)error {
+ NSParameterAssert(value != nil);
+ NSParameterAssert(key != nil);
+ AXError result = AXUIElementSetAttributeValue(self.elementRef, (__bridge CFStringRef)key, value);
+ if((result != kAXErrorSuccess) && error) {
+ *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:result userInfo:@{
+ @"key": key,
+ @"elementRef": (id)self.elementRef
+ }];
+ }
+ return result == kAXErrorSuccess;
+}
+
+-(BOOL)performAction:(NSString *)action error:(NSError **)error {
+ NSParameterAssert(action != nil);
+ AXError result = AXUIElementPerformAction(self.elementRef, (__bridge CFStringRef)action);
+ if ((result != kAXErrorSuccess) && error) {
+ *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:result userInfo:@{
+ @"action": action,
+ @"elementRef": (id)self.elementRef
+ }];
+ }
+
+ return result == kAXErrorSuccess;
+}
+
+
+-(id)elementOfClass:(Class)klass forKey:(NSString *)key error:(NSError **)error {
+ AXUIElementRef subelementRef = (AXUIElementRef)[self copyAttributeValueForKey:key error:error];
+ id result = nil;
+ if (subelementRef) {
+ result = [[klass alloc] initWithElementRef:subelementRef];
+ CFRelease(subelementRef);
+ subelementRef = NULL;
+ }
+ return result;
+}
+
+
+-(void)addAXObserver {
+ if (self.observer) { return; }
+
+ AXObserverRef observer;
+ AXError err;
+ pid_t pid;
+
+ err = AXUIElementGetPid(self.elementRef, &pid);
+ if (err != kAXErrorSuccess) { return; }
+
+ err = AXObserverCreate(pid, axCallback, &observer);
+ if (err != kAXErrorSuccess) { return; }
+
+ err = AXObserverAddNotification(observer, self.elementRef, kAXUIElementDestroyedNotification, (__bridge void *)(self));
+ if (err != kAXErrorSuccess) {
+ CFRelease(observer);
+ observer = NULL;
+ return;
+ }
+
+ CFRunLoopAddSource([[NSRunLoop mainRunLoop] getCFRunLoop], AXObserverGetRunLoopSource(observer), kCFRunLoopDefaultMode);
+
+ self.observer = observer;
+ CFRelease(observer);
+}
+
+static void axCallback(AXObserverRef observer, AXUIElementRef element, CFStringRef notification, void *refcon) {
+ [(__bridge HAXElement *)refcon didObserveNotification:(__bridge NSString *)notification];
+}
+
+-(void)didObserveNotification:(NSString *)notification {
+ id delegate = self.delegate;
+
+ if ([notification isEqualToString:(__bridge NSString *)kAXUIElementDestroyedNotification] && [delegate respondsToSelector:@selector(elementWasDestroyed:)]) {
+ [delegate elementWasDestroyed:self];
+ }
+}
+
+-(void)removeAXObserver {
+ if (!self.observer) { return; }
+
+ (void)AXObserverRemoveNotification(self.observer, self.elementRef, kAXUIElementDestroyedNotification);
+
+ CFRunLoopSourceRef observerRunLoopSource = AXObserverGetRunLoopSource(self.observer);
+ if (observerRunLoopSource) {
+ CFRunLoopRemoveSource([[NSRunLoop mainRunLoop] getCFRunLoop], observerRunLoopSource, kCFRunLoopDefaultMode);
+ }
+
+ self.observer = NULL;
+}
+
+-(BOOL)hasChildren {
+ return (self.children.count > 0);
+}
+
+-(NSArray *)children {
+ NSArray * axUIElements = nil;
+ NSMutableArray * result = nil;
+
+ axUIElements = [self getAttributeValueForKey:(__bridge NSString *)kAXChildrenAttribute error:NULL];
+ if (axUIElements != nil) {
+ result = [NSMutableArray arrayWithCapacity:[axUIElements count]];
+ for (id elementI in axUIElements) {
+ [result addObject:[[HAXElement alloc] initWithElementRef:(AXUIElementRef)(elementI)]];
+ }
+ }
+
+ return result;
+}
+
+-(NSString *)role {
+ NSString * result = [self getAttributeValueForKey:(__bridge NSString *)kAXRoleAttribute error:NULL];
+ if ([result isKindOfClass:[NSString class]] == NO) {
+ result = nil;
+ }
+ return result;
+}
+
+-(NSArray *) buttons {
+ NSArray *axChildren = self.children;
+ NSMutableArray *result = nil;
+
+ if (axChildren) {
+ result = [NSMutableArray arrayWithCapacity:axChildren.count];
+
+ for (HAXElement *haxElementI in axChildren) {
+ NSString * axRole = CFBridgingRelease([haxElementI copyAttributeValueForKey:(__bridge NSString *)kAXRoleAttribute error:NULL]);
+ if (axRole == nil) {
+ result = nil;
+ break;
+ }
+ if ([axRole isEqualToString:(__bridge NSString *)kAXButtonRole]) {
+ HAXButton *button = [[HAXButton alloc] initWithElementRef:(AXUIElementRef)haxElementI.elementRef];
+ [result addObject:button];
+ }
+ }
+ }
+
+ return result;
+}
+
+-(NSString *)title {
+ NSString * result = [self getAttributeValueForKey:NSAccessibilityTitleAttribute error:NULL];
+ if ([result isKindOfClass:[NSString class]] == NO) {
+ result = nil;
+ }
+ return result;
+}
+
+-(NSArray *)attributeNames {
+ CFArrayRef attrNamesRef = NULL;
+ AXUIElementCopyAttributeNames(_elementRef, &attrNamesRef);
+ return attrNamesRef ? CFBridgingRelease(attrNamesRef) : nil;
+}
+
+-(pid_t)processIdentifier {
+ pid_t result;
+ if (AXUIElementGetPid (self.elementRef, &result) != kAXErrorSuccess) { return 0; }
+ return result;
+}
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXSystem.h b/Dependencies/Haxcessibility/Classes/HAXSystem.h
new file mode 100644
index 00000000..67f9d5ec
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXSystem.h
@@ -0,0 +1,15 @@
+// HAXSystem.h
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import
+
+@class HAXApplication;
+
+@interface HAXSystem : HAXElement
+
++(nonnull instancetype)system;
+
+@property (readonly, nullable) HAXApplication *focusedApplication;
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXSystem.m b/Dependencies/Haxcessibility/Classes/HAXSystem.m
new file mode 100644
index 00000000..ca59d2ed
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXSystem.m
@@ -0,0 +1,23 @@
+// HAXSystem.m
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import "HAXApplication.h"
+#import "HAXSystem.h"
+#import "HAXElement+Protected.h"
+
+@implementation HAXSystem
+
++(instancetype)system {
+ AXUIElementRef element = AXUIElementCreateSystemWide();
+ HAXSystem *result = [[HAXSystem alloc] initWithElementRef:element];
+ CFRelease(element);
+ return result;
+}
+
+
+-(HAXApplication *)focusedApplication {
+ return [self elementOfClass:[HAXApplication class] forKey:(NSString *)kAXFocusedApplicationAttribute error:NULL];
+}
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXView.h b/Dependencies/Haxcessibility/Classes/HAXView.h
new file mode 100644
index 00000000..21eb9da3
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXView.h
@@ -0,0 +1,20 @@
+// HAXView.h
+// Created by Kocsis Olivér on 2014-05-12
+// Copyright 2014 Joinect Technologies
+
+#import
+#import
+
+@interface HAXView : HAXElement
+
+@property (nonatomic) CGPoint carbonOrigin;
+@property (nonatomic) NSPoint origin;
+@property (nonatomic) CGSize size;
+@property (nonatomic) CGRect carbonFrame;
+@property (nonatomic) NSRect frame;
+@property (nullable, readonly) NSString *title;
+@property (nullable, readonly) NSScreen *screen;
+@property (readonly, getter=isFullscreen) BOOL fullscreen;
+
+@end
+
diff --git a/Dependencies/Haxcessibility/Classes/HAXView.m b/Dependencies/Haxcessibility/Classes/HAXView.m
new file mode 100644
index 00000000..03625157
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXView.m
@@ -0,0 +1,107 @@
+// HAXView.m
+// Created by Kocsis Olivér on 2014-05-12
+// Copyright 2014 Joinect Technologies
+
+#import "HAXView.h"
+#import "HAXElement+Protected.h"
+#import "NSScreen+Helpers.h"
+
+@implementation HAXView
+
+-(CGPoint)carbonOrigin {
+ CGPoint origin = {0};
+ AXValueRef originRef = (AXValueRef)[self copyAttributeValueForKey:(__bridge NSString *)kAXPositionAttribute error:NULL];
+ if(originRef) {
+ AXValueGetValue(originRef, kAXValueCGPointType, &origin);
+ CFRelease(originRef);
+ originRef = NULL;
+ }
+ return origin;
+}
+
+-(void)setCarbonOrigin:(CGPoint)carbonOrigin {
+ AXValueRef originRef = AXValueCreate(kAXValueCGPointType, &carbonOrigin);
+ [self setAttributeValue:originRef forKey:(__bridge NSString *)kAXPositionAttribute error:NULL];
+ CFRelease(originRef);
+}
+
+-(NSPoint)origin {
+ return cocoaScreenFrameFromCarbonScreenFrame(self.carbonFrame).origin;
+}
+
+-(void)setOrigin:(NSPoint)origin {
+ self.carbonOrigin = carbonScreenPointFromCocoaScreenPoint(origin);
+}
+
+-(NSSize)size {
+ CGSize size = {0};
+ AXValueRef sizeRef = (AXValueRef)[self copyAttributeValueForKey:(__bridge NSString *)kAXSizeAttribute error:NULL];
+ if(sizeRef) {
+ AXValueGetValue(sizeRef, kAXValueCGSizeType, &size);
+ CFRelease(sizeRef);
+ sizeRef = NULL;
+ }
+ return size;
+}
+
+-(void)setSize:(NSSize)size {
+ AXValueRef sizeRef = AXValueCreate(kAXValueCGSizeType, &size);
+ [self setAttributeValue:sizeRef forKey:(__bridge NSString *)kAXSizeAttribute error:NULL];
+ CFRelease(sizeRef);
+}
+
+-(CGRect)carbonFrame {
+ return (CGRect){ .origin = self.carbonOrigin, .size = self.size };
+}
+
+-(void)setCarbonFrame:(CGRect)carbonFrame {
+ self.carbonOrigin = carbonFrame.origin;
+ self.size = carbonFrame.size;
+}
+
+-(NSRect)frame {
+ return cocoaScreenFrameFromCarbonScreenFrame(self.carbonFrame);
+}
+
+-(void)setFrame:(NSRect)frame {
+ self.origin = frame.origin;
+ self.size = frame.size;
+}
+
+-(NSString *)title {
+ return [self getAttributeValueForKey:(__bridge NSString *)kAXTitleAttribute error:NULL];
+}
+
+-(NSScreen *)screen {
+ NSScreen *matchingScreen = nil;
+ NSRect viewFrame = self.frame;
+ NSUInteger bestOverlap = 0;
+ for (NSScreen * screenI in [NSScreen screens]) {
+ NSRect intersection = NSIntersectionRect(screenI.frame, viewFrame);
+ NSUInteger intersectionOverlap = intersection.size.width * intersection.size.height;
+ if(intersectionOverlap > bestOverlap) {
+ matchingScreen = screenI;
+ bestOverlap = intersectionOverlap;
+ }
+ }
+ return matchingScreen;
+}
+
+-(BOOL)isFullscreen {
+ BOOL isFullScreen = NO;
+ NSArray * sceenArray = [NSScreen screens];
+ NSRect windowFrame = self.frame;
+
+ for (NSScreen * screenI in sceenArray) {
+ NSRect screenFrame;
+ screenFrame = [screenI frame];
+ if(NSEqualRects(screenFrame, windowFrame)) {
+ isFullScreen = YES;
+ break;
+ }
+ }
+
+ return isFullScreen;
+}
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXWindow.h b/Dependencies/Haxcessibility/Classes/HAXWindow.h
new file mode 100644
index 00000000..15825d2d
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXWindow.h
@@ -0,0 +1,17 @@
+// HAXWindow.h
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import
+#import
+
+@interface HAXWindow : HAXView
+
+@property (nullable, readonly) NSArray *views;
+
+-(BOOL)raise;
+-(BOOL)close;
+
+-(CGWindowID)cgWindowID;
+
+@end
diff --git a/Dependencies/Haxcessibility/Classes/HAXWindow.m b/Dependencies/Haxcessibility/Classes/HAXWindow.m
new file mode 100644
index 00000000..af0c3e13
--- /dev/null
+++ b/Dependencies/Haxcessibility/Classes/HAXWindow.m
@@ -0,0 +1,64 @@
+// HAXWindow.m
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import "HAXWindow.h"
+#import "HAXElement+Protected.h"
+
+extern AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);
+
+@implementation HAXWindow
+
+-(NSArray *)views {
+ NSArray *axChildren = self.children;
+ NSMutableArray *result = nil;
+
+ if (axChildren) {
+ result = [NSMutableArray arrayWithCapacity:axChildren.count];
+
+ for (HAXElement * haxElementI in axChildren) {
+ NSString * axRole = [haxElementI getAttributeValueForKey:(__bridge NSString *)kAXRoleAttribute error:NULL];
+ if ([axRole isEqualToString:@"AXView"]) {
+ HAXView * haxView = [[HAXView init] initWithElementRef:(AXUIElementRef)haxElementI.elementRef];
+ [result addObject:haxView];
+ }
+ }
+ }
+ return result;
+}
+
+-(BOOL)raise {
+ __block BOOL success = NO;
+ pid_t pid = self.processIdentifier;
+ if (pid == [NSProcessInfo processInfo].processIdentifier && ![NSThread isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{ success = [self raise]; });
+ return success;
+ }
+ if ([self performAction:(__bridge NSString *)kAXRaiseAction error:NULL]) {
+ ProcessSerialNumber psn;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ if (pid && GetProcessForPID (pid, &psn) == 0) {
+ success = (noErr == SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly));
+ }
+#pragma clang diagnostic pop
+ }
+ return success;
+}
+
+-(BOOL)close {
+ HAXElement *element = [self elementOfClass:[HAXElement class] forKey:(__bridge NSString *)kAXCloseButtonAttribute error:NULL];
+ return [element performAction:(__bridge NSString *)kAXPressAction error:NULL];
+}
+
+// https://stackoverflow.com/a/9624565
+-(CGWindowID)cgWindowID {
+ CGWindowID result;
+ AXError err = _AXUIElementGetWindow(self.elementRef, &result);
+ if (err == kAXErrorSuccess) {
+ return result;
+ }
+ return kCGNullWindowID;
+}
+
+@end
diff --git a/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.pbxproj b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..3d1ebf70
--- /dev/null
+++ b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.pbxproj
@@ -0,0 +1,384 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
+ BC3B3B76175E9FD4002AD452 /* HAXElement+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = D44E051312D75B3D00541D6A /* HAXElement+Protected.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ C3E21520194F381400C85776 /* HAXButton.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E2151C194F381400C85776 /* HAXButton.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ C3E21521194F381400C85776 /* HAXButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E2151D194F381400C85776 /* HAXButton.m */; };
+ C3E21526194F38BE00C85776 /* NSScreen+Helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E21524194F38BD00C85776 /* NSScreen+Helpers.h */; };
+ C3E21527194F38BE00C85776 /* NSScreen+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E21525194F38BD00C85776 /* NSScreen+Helpers.m */; };
+ C3E2152A194F38FB00C85776 /* HAXView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E21528194F38FB00C85776 /* HAXView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ C3E2152B194F38FB00C85776 /* HAXView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E21529194F38FB00C85776 /* HAXView.m */; };
+ D4D2103412D6957000509E57 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4D2103312D6957000509E57 /* Carbon.framework */; };
+ D4D2103712D6959400509E57 /* HAXElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2103512D6959400509E57 /* HAXElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D4D2103812D6959400509E57 /* HAXElement.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2103612D6959400509E57 /* HAXElement.m */; };
+ D4D2105512D698F400509E57 /* HAXSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2105412D698F400509E57 /* HAXSystem.m */; };
+ D4D2105912D6991900509E57 /* HAXSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2105312D698F200509E57 /* HAXSystem.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D4D2106D12D69C8B00509E57 /* HAXApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2106C12D69C8B00509E57 /* HAXApplication.m */; };
+ D4D2108212D6A08800509E57 /* HAXWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2108112D6A08800509E57 /* HAXWindow.m */; };
+ D4D2113D12D6AAF000509E57 /* Haxcessibility.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2113C12D6AAF000509E57 /* Haxcessibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D4D2113E12D6AB0400509E57 /* HAXApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2106B12D69C8900509E57 /* HAXApplication.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ D4D2113F12D6AB0400509E57 /* HAXWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2108012D6A08600509E57 /* HAXWindow.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; };
+ 32DBCF5E0370ADEE00C91783 /* Haxcessibility.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Haxcessibility.pch; sourceTree = ""; };
+ 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 8DC2EF5B0486A6940098B216 /* Haxcessibility.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Haxcessibility.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ C3E2151C194F381400C85776 /* HAXButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAXButton.h; sourceTree = ""; };
+ C3E2151D194F381400C85776 /* HAXButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HAXButton.m; sourceTree = ""; };
+ C3E21524194F38BD00C85776 /* NSScreen+Helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSScreen+Helpers.h"; sourceTree = ""; };
+ C3E21525194F38BD00C85776 /* NSScreen+Helpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSScreen+Helpers.m"; sourceTree = ""; };
+ C3E21528194F38FB00C85776 /* HAXView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAXView.h; sourceTree = ""; };
+ C3E21529194F38FB00C85776 /* HAXView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HAXView.m; sourceTree = ""; };
+ D44E051312D75B3D00541D6A /* HAXElement+Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HAXElement+Protected.h"; sourceTree = ""; };
+ D4D2103312D6957000509E57 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
+ D4D2103512D6959400509E57 /* HAXElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAXElement.h; sourceTree = ""; };
+ D4D2103612D6959400509E57 /* HAXElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = HAXElement.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
+ D4D2105312D698F200509E57 /* HAXSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HAXSystem.h; sourceTree = ""; };
+ D4D2105412D698F400509E57 /* HAXSystem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HAXSystem.m; sourceTree = ""; };
+ D4D2106B12D69C8900509E57 /* HAXApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HAXApplication.h; sourceTree = ""; };
+ D4D2106C12D69C8B00509E57 /* HAXApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HAXApplication.m; sourceTree = ""; };
+ D4D2108012D6A08600509E57 /* HAXWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HAXWindow.h; sourceTree = ""; };
+ D4D2108112D6A08800509E57 /* HAXWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HAXWindow.m; sourceTree = ""; };
+ D4D2113C12D6AAF000509E57 /* Haxcessibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Haxcessibility.h; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8DC2EF560486A6940098B216 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
+ D4D2103412D6957000509E57 /* Carbon.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 034768DFFF38A50411DB9C8B /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8DC2EF5B0486A6940098B216 /* Haxcessibility.framework */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 0867D691FE84028FC02AAC07 /* Haxcessibility */ = {
+ isa = PBXGroup;
+ children = (
+ D4D2102E12D6947D00509E57 /* Classes */,
+ 32C88DFF0371C24200C91783 /* Other Sources */,
+ 089C1665FE841158C02AAC07 /* Resources */,
+ 0867D69AFE84028FC02AAC07 /* Frameworks */,
+ 034768DFFF38A50411DB9C8B /* Products */,
+ );
+ name = Haxcessibility;
+ sourceTree = "";
+ };
+ 0867D69AFE84028FC02AAC07 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ D4D2103312D6957000509E57 /* Carbon.framework */,
+ 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 089C1665FE841158C02AAC07 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8DC2EF5A0486A6940098B216 /* Info.plist */,
+ );
+ path = Resources;
+ sourceTree = "";
+ };
+ 32C88DFF0371C24200C91783 /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ D4D2113C12D6AAF000509E57 /* Haxcessibility.h */,
+ 32DBCF5E0370ADEE00C91783 /* Haxcessibility.pch */,
+ C3E21524194F38BD00C85776 /* NSScreen+Helpers.h */,
+ C3E21525194F38BD00C85776 /* NSScreen+Helpers.m */,
+ );
+ path = "Other Sources";
+ sourceTree = "";
+ };
+ D4D2102E12D6947D00509E57 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ D4D2106B12D69C8900509E57 /* HAXApplication.h */,
+ D4D2106C12D69C8B00509E57 /* HAXApplication.m */,
+ C3E2151C194F381400C85776 /* HAXButton.h */,
+ C3E2151D194F381400C85776 /* HAXButton.m */,
+ D4D2103512D6959400509E57 /* HAXElement.h */,
+ D44E051312D75B3D00541D6A /* HAXElement+Protected.h */,
+ D4D2103612D6959400509E57 /* HAXElement.m */,
+ D4D2105312D698F200509E57 /* HAXSystem.h */,
+ D4D2105412D698F400509E57 /* HAXSystem.m */,
+ C3E21528194F38FB00C85776 /* HAXView.h */,
+ C3E21529194F38FB00C85776 /* HAXView.m */,
+ D4D2108012D6A08600509E57 /* HAXWindow.h */,
+ D4D2108112D6A08800509E57 /* HAXWindow.m */,
+ );
+ path = Classes;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 8DC2EF500486A6940098B216 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D4D2105912D6991900509E57 /* HAXSystem.h in Headers */,
+ C3E2152A194F38FB00C85776 /* HAXView.h in Headers */,
+ D4D2103712D6959400509E57 /* HAXElement.h in Headers */,
+ D4D2113E12D6AB0400509E57 /* HAXApplication.h in Headers */,
+ C3E21520194F381400C85776 /* HAXButton.h in Headers */,
+ D4D2113F12D6AB0400509E57 /* HAXWindow.h in Headers */,
+ C3E21526194F38BE00C85776 /* NSScreen+Helpers.h in Headers */,
+ D4D2113D12D6AAF000509E57 /* Haxcessibility.h in Headers */,
+ BC3B3B76175E9FD4002AD452 /* HAXElement+Protected.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 8DC2EF4F0486A6940098B216 /* Haxcessibility */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Haxcessibility" */;
+ buildPhases = (
+ 8DC2EF500486A6940098B216 /* Headers */,
+ 8DC2EF540486A6940098B216 /* Sources */,
+ 8DC2EF560486A6940098B216 /* Frameworks */,
+ 8DC2EF520486A6940098B216 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Haxcessibility;
+ productInstallPath = "$(HOME)/Library/Frameworks";
+ productName = Haxcessibility;
+ productReference = 8DC2EF5B0486A6940098B216 /* Haxcessibility.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0867D690FE84028FC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = HAX;
+ LastUpgradeCheck = 1200;
+ ORGANIZATIONNAME = "Rob Rix";
+ };
+ buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Haxcessibility" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 0867D691FE84028FC02AAC07 /* Haxcessibility */;
+ productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8DC2EF4F0486A6940098B216 /* Haxcessibility */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8DC2EF520486A6940098B216 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8DC2EF540486A6940098B216 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C3E2152B194F38FB00C85776 /* HAXView.m in Sources */,
+ D4D2103812D6959400509E57 /* HAXElement.m in Sources */,
+ C3E21521194F381400C85776 /* HAXButton.m in Sources */,
+ D4D2105512D698F400509E57 /* HAXSystem.m in Sources */,
+ D4D2106D12D69C8B00509E57 /* HAXApplication.m in Sources */,
+ D4D2108212D6A08800509E57 /* HAXWindow.m in Sources */,
+ C3E21527194F38BE00C85776 /* NSScreen+Helpers.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB91AE08733DA50010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INFOPLIST_FILE = Resources/Info.plist;
+ INSTALL_PATH = "@rpath";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.${PRODUCT_NAME:rfc1034Identifier}";
+ PRODUCT_NAME = Haxcessibility;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Debug;
+ };
+ 1DEB91AF08733DA50010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ INFOPLIST_FILE = Resources/Info.plist;
+ INSTALL_PATH = "@rpath";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.${PRODUCT_NAME:rfc1034Identifier}";
+ PRODUCT_NAME = Haxcessibility;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Release;
+ };
+ 1DEB91B208733DA50010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = 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_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_REPEATED_USE_OF_WEAK = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Other Sources/Haxcessibility.pch";
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 1DEB91B308733DA50010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = 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_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_REPEATED_USE_OF_WEAK = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Other Sources/Haxcessibility.pch";
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.9;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Haxcessibility" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB91AE08733DA50010E9CD /* Debug */,
+ 1DEB91AF08733DA50010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Haxcessibility" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB91B208733DA50010E9CD /* Debug */,
+ 1DEB91B308733DA50010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
+}
diff --git a/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..356e8952
--- /dev/null
+++ b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Dependencies/Haxcessibility/LICENSE b/Dependencies/Haxcessibility/LICENSE
new file mode 100644
index 00000000..9414dc1f
--- /dev/null
+++ b/Dependencies/Haxcessibility/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) 2007-2013, Rob Rix
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of Monochrome Industries nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/Dependencies/Haxcessibility/Other Sources/Haxcessibility.h b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.h
new file mode 100644
index 00000000..48eae4f0
--- /dev/null
+++ b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.h
@@ -0,0 +1,10 @@
+// Haxcessibility.h
+// Created by Rob Rix on 2011-01-06
+// Copyright 2011 Rob Rix
+
+#import
+#import
+#import
+#import
+#import
+#import
diff --git a/Dependencies/Haxcessibility/Other Sources/Haxcessibility.pch b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.pch
new file mode 100644
index 00000000..c0125768
--- /dev/null
+++ b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.pch
@@ -0,0 +1,3 @@
+#ifdef __OBJC__
+ #import
+#endif
diff --git a/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.h b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.h
new file mode 100644
index 00000000..a67b2bf1
--- /dev/null
+++ b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.h
@@ -0,0 +1,8 @@
+// NSScreen+Helpers.h
+// Created by Scott Perry on 2020-09-26
+// Copyright 2020 Scott Perry
+
+#import
+
+NSRect cocoaScreenFrameFromCarbonScreenFrame(CGRect carbonFrame);
+CGPoint carbonScreenPointFromCocoaScreenPoint(NSPoint cocoaPoint);
diff --git a/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.m b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.m
new file mode 100644
index 00000000..d4f2d5b1
--- /dev/null
+++ b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.m
@@ -0,0 +1,38 @@
+// NSScreen+Helpers.m
+// Created by Scott Perry on 2020-09-26
+// Copyright 2020 Scott Perry
+
+#import "NSScreen+Helpers.h"
+
+static NSScreen *hax_screenWithPoint(NSPoint p) {
+ for (NSScreen *screen in [NSScreen screens]) {
+ if (NSPointInRect(p, [screen frame])) {
+ return screen;
+ }
+ }
+ return nil;
+}
+
+NSRect cocoaScreenFrameFromCarbonScreenFrame(CGRect carbonFrame) {
+ // TODO(numist): is this actually correct with multimon?
+ NSRect originScreenFrame = ((NSScreen *)[NSScreen screens][0]).frame;
+
+ return NSMakeRect(
+ carbonFrame.origin.x,
+ originScreenFrame.size.height - carbonFrame.origin.y - carbonFrame.size.height,
+ carbonFrame.size.width,
+ carbonFrame.size.height
+ );
+}
+
+CGPoint carbonScreenPointFromCocoaScreenPoint(NSPoint cocoaPoint) {
+ NSScreen *foundScreen = hax_screenWithPoint(cocoaPoint);
+
+ if (foundScreen) {
+ return CGPointMake(
+ cocoaPoint.x,
+ foundScreen.frame.size.height - cocoaPoint.y - 1
+ );
+ }
+ return CGPointZero;
+}
diff --git a/Dependencies/Haxcessibility/README.mdown b/Dependencies/Haxcessibility/README.mdown
new file mode 100644
index 00000000..2c6c776a
--- /dev/null
+++ b/Dependencies/Haxcessibility/README.mdown
@@ -0,0 +1,32 @@
+# Haxcessibility
+
+Haxcessibility is, above all, a horrible pun on Mac OS X’s Accessibility framework and its AX prefix. I am shameless.
+
+Second to that, Haxcessibility is a use case–driven remote control for Mac apps by Mac apps. It enables hacks like moving and resizing another app’s windows, and there’s loads more that the AX APIs make possible that Haxcessibility could make convenient with a method or two.
+
+
+# Use it
+
+Resize the focused app’s focused window to fullscreen:
+
+ [HAXSystem system].focusedApplication.focusedWindow.frame = NSScreen.mainScreen.frame
+
+Close all windows in the focused app:
+
+ [[HAXSystem system].focusedApplication.windows makeObjectsPerformSelector:@selector(close)];
+
+# Improve it
+
+Don’t see the feature you want? Fortunately, it’s pretty easy to add your own convenience methods. [Fork Haxcessibility](https://github.com/numist/Haxcessibility/fork) and send me a pull request with your code.
+
+You’ll want to pay special attention to the HAXElement+Protected.h private header. `HAXElement` is the root of most functionality in Haxcessibility, and this header declares the conveniences defined for wrapping more of the Accessibility APIs’ functionality.
+
+# Thanks to
+
+This framework would not be what it is without the help of:
+
+- [Rob Rix](https://github.com/robrix) for creating and curating Haxcessibility for the first five years of its existence.
+
+- [Decimus Software](http://decimus.net) for DTerm, which showed us what you can do with the Accessibility APIs
+
+- [DEVONtechnologies, LLC](http://devontechnologies.com/) for their patronage of [Grid](https://github.com/robrix/Grid), the original _raison d’être_ of this framework
diff --git a/Dependencies/Haxcessibility/Resources/English.lproj/InfoPlist.strings b/Dependencies/Haxcessibility/Resources/English.lproj/InfoPlist.strings
new file mode 100644
index 00000000..88f65cf6
--- /dev/null
+++ b/Dependencies/Haxcessibility/Resources/English.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/Frameworks/NNKit/NNKit/NNKit-Info.plist b/Dependencies/Haxcessibility/Resources/Info.plist
similarity index 80%
rename from Frameworks/NNKit/NNKit/NNKit-Info.plist
rename to Dependencies/Haxcessibility/Resources/Info.plist
index 0729ca8e..f4f9da73 100644
--- a/Frameworks/NNKit/NNKit/NNKit-Info.plist
+++ b/Dependencies/Haxcessibility/Resources/Info.plist
@@ -9,7 +9,7 @@
CFBundleIconFile
CFBundleIdentifier
- net.numist.${PRODUCT_NAME:rfc1034identifier}
+ ${PRODUCT_BUNDLE_IDENTIFIER}
CFBundleInfoDictionaryVersion
6.0
CFBundleName
@@ -21,9 +21,7 @@
CFBundleSignature
????
CFBundleVersion
- 1
- NSHumanReadableCopyright
- Copyright © 2013 Scott Perry. All rights reserved.
+ 2
NSPrincipalClass
diff --git a/Dependencies/Haxcessibility/TODO.mdown b/Dependencies/Haxcessibility/TODO.mdown
new file mode 100644
index 00000000..3acc698f
--- /dev/null
+++ b/Dependencies/Haxcessibility/TODO.mdown
@@ -0,0 +1,13 @@
+# To-do
+
+- HAXWindow
+ - window elements should subclass a general widget class of some description
+ - animate size/position/frame changes?
+ - Maybe something like the NSWindow -setFrame:animate: method for that.
+
+- Overall
+ - wrap AXAPIEnabled/AXIsProcessTrusted
+ - handle AX trust authorization
+ - getting auth
+ - setting the trust bit or whatever
+ - relaunching the app at the client’s request (so they can in turn ask the user, if they deem it necessary)
diff --git a/Dependencies/LetsMove.gitcheckout b/Dependencies/LetsMove.gitcheckout
new file mode 100644
index 00000000..1b58f0a3
--- /dev/null
+++ b/Dependencies/LetsMove.gitcheckout
@@ -0,0 +1 @@
+master:70c5772c2ce84613ba539cb122e4065a9e33db5b
diff --git a/Dependencies/LetsMove.giturl b/Dependencies/LetsMove.giturl
new file mode 100644
index 00000000..f54c5233
--- /dev/null
+++ b/Dependencies/LetsMove.giturl
@@ -0,0 +1 @@
+git@github.com:potionfactory/LetsMove.git
\ No newline at end of file
diff --git a/Dependencies/LetsMove/.gitignore b/Dependencies/LetsMove/.gitignore
new file mode 100644
index 00000000..f157394b
--- /dev/null
+++ b/Dependencies/LetsMove/.gitignore
@@ -0,0 +1,9 @@
+# Mac OS X
+*.DS_Store
+
+# Xcode
+build
+*.mode*
+*.pbxuser
+xcuserdata/
+project.xcworkspace/
diff --git a/Dependencies/LetsMove/Base.lproj/MainMenu.xib b/Dependencies/LetsMove/Base.lproj/MainMenu.xib
new file mode 100644
index 00000000..91ffec69
--- /dev/null
+++ b/Dependencies/LetsMove/Base.lproj/MainMenu.xib
@@ -0,0 +1,4208 @@
+
+
+
+ 1060
+ 10C540
+ 740
+ 1038.25
+ 458.00
+
+
+
+
+
+ YES
+
+ NSApplication
+
+
+ FirstResponder
+
+
+ NSApplication
+
+