diff --git a/ElixxirDAppsSDK.xcworkspace/contents.xcworkspacedata b/ElixxirDAppsSDK.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..9ca22f2ec7dc6801cd91f1e2b68ae885d4abb591 --- /dev/null +++ b/ElixxirDAppsSDK.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "group:"> + </FileRef> + <FileRef + location = "group:Example/Example.xcodeproj"> + </FileRef> +</Workspace> diff --git a/ElixxirDAppsSDK.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ElixxirDAppsSDK.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000000000000000000000000000000..18d981003d68d0546c4804ac2ff47dd97c6e7921 --- /dev/null +++ b/ElixxirDAppsSDK.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IDEDidComputeMac32BitWarning</key> + <true/> +</dict> +</plist> diff --git a/ElixxirDAppsSDK.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ElixxirDAppsSDK.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000000000000000000000000000000000..08de0be8d3c8c1786ebe04545dd772526853eef4 --- /dev/null +++ b/ElixxirDAppsSDK.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key> + <false/> +</dict> +</plist> diff --git a/ElixxirDAppsSDK.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElixxirDAppsSDK.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000000000000000000000000000000000000..0ee0d575b6265a1319b61f4e9167c7a059296261 --- /dev/null +++ b/ElixxirDAppsSDK.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,95 @@ +{ + "pins" : [ + { + "identity" : "combine-schedulers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/combine-schedulers", + "state" : { + "revision" : "4cf088c29a20f52be0f2ca54992b492c54e0076b", + "version" : "0.5.3" + } + }, + { + "identity" : "keychainaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git", + "state" : { + "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7", + "version" : "4.2.2" + } + }, + { + "identity" : "swift-case-paths", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-case-paths", + "state" : { + "revision" : "ce9c0d897db8a840c39de64caaa9b60119cf4be8", + "version" : "0.8.1" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "48254824bb4248676bf7ce56014ff57b142b77eb", + "version" : "1.0.2" + } + }, + { + "identity" : "swift-composable-architecture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-composable-architecture", + "state" : { + "revision" : "c307541328a636b9e8e25ac868d89be54a8f8dbf", + "version" : "0.35.0" + } + }, + { + "identity" : "swift-composable-presentation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/darrarski/swift-composable-presentation.git", + "state" : { + "revision" : "1f4d17fae1f7ed41cbed17929083190fd9a78ee6", + "version" : "0.5.2" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "c4f78db9b90ca57b7b6abc2223e235242739ea3c", + "version" : "0.4.0" + } + }, + { + "identity" : "swift-identified-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-identified-collections", + "state" : { + "revision" : "2d6b7ffcc67afd9077fac5e5a29bcd6d39b71076", + "version" : "0.4.0" + } + }, + { + "identity" : "swiftui-app-icon-creator", + "kind" : "remoteSourceControl", + "location" : "https://github.com/darrarski/swiftui-app-icon-creator.git", + "state" : { + "revision" : "f0c7ba4e66d3dc8135ccf9146afc05f9dff3c4ff", + "version" : "1.2.0" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "50a70a9d3583fe228ce672e8923010c8df2deddd", + "version" : "0.2.1" + } + } + ], + "version" : 2 +} diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000000000000000000000000000000..1118178dfef93cbdafbccdbfdbd572ff027bc961 --- /dev/null +++ b/Example/Example.xcodeproj/project.pbxproj @@ -0,0 +1,344 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 312C371E28475E63003E9E39 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 312C371D28475E63003E9E39 /* Assets.xcassets */; }; + 316D95EE28477B0E008475F9 /* AppFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 316D95ED28477B0E008475F9 /* AppFeature */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 312C371628475E62003E9E39 /* dApps Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dApps Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 312C371D28475E63003E9E39 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 312C3728284761DC003E9E39 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 316D95EB28477ACA008475F9 /* example-app */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "example-app"; sourceTree = "<group>"; }; + 31B3342F2847669900865D82 /* example-app-icon */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "example-app-icon"; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 312C371328475E62003E9E39 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 316D95EE28477B0E008475F9 /* AppFeature in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 312C370D28475E62003E9E39 = { + isa = PBXGroup; + children = ( + 312C3727284761C7003E9E39 /* ExampleApp (iOS) */, + 316D95EB28477ACA008475F9 /* example-app */, + 31B3342F2847669900865D82 /* example-app-icon */, + 312C371728475E62003E9E39 /* Products */, + ); + sourceTree = "<group>"; + }; + 312C371728475E62003E9E39 /* Products */ = { + isa = PBXGroup; + children = ( + 312C371628475E62003E9E39 /* dApps Example.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + 312C3727284761C7003E9E39 /* ExampleApp (iOS) */ = { + isa = PBXGroup; + children = ( + 312C371D28475E63003E9E39 /* Assets.xcassets */, + 312C3728284761DC003E9E39 /* Info.plist */, + ); + path = "ExampleApp (iOS)"; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 312C371528475E62003E9E39 /* ExampleApp (iOS) */ = { + isa = PBXNativeTarget; + buildConfigurationList = 312C372428475E63003E9E39 /* Build configuration list for PBXNativeTarget "ExampleApp (iOS)" */; + buildPhases = ( + 312C371228475E62003E9E39 /* Sources */, + 312C371328475E62003E9E39 /* Frameworks */, + 312C371428475E62003E9E39 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ExampleApp (iOS)"; + packageProductDependencies = ( + 316D95ED28477B0E008475F9 /* AppFeature */, + ); + productName = ExampleApp; + productReference = 312C371628475E62003E9E39 /* dApps Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 312C370E28475E62003E9E39 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 312C371528475E62003E9E39 = { + CreatedOnToolsVersion = 13.4; + }; + }; + }; + buildConfigurationList = 312C371128475E62003E9E39 /* Build configuration list for PBXProject "Example" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 312C370D28475E62003E9E39; + productRefGroup = 312C371728475E62003E9E39 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 312C371528475E62003E9E39 /* ExampleApp (iOS) */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 312C371428475E62003E9E39 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 312C371E28475E63003E9E39 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 312C371228475E62003E9E39 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 312C372228475E63003E9E39 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 312C372328475E63003E9E39 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 312C372528475E63003E9E39 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "ExampleApp (iOS)/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "dApps Example"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = xx.network.dApps.ExampleApp; + PRODUCT_MODULE_NAME = ExampleApp; + PRODUCT_NAME = "dApps Example"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 312C372628475E63003E9E39 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "ExampleApp (iOS)/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "dApps Example"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = xx.network.dApps.ExampleApp; + PRODUCT_MODULE_NAME = ExampleApp; + PRODUCT_NAME = "dApps Example"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 312C371128475E62003E9E39 /* Build configuration list for PBXProject "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 312C372228475E63003E9E39 /* Debug */, + 312C372328475E63003E9E39 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 312C372428475E63003E9E39 /* Build configuration list for PBXNativeTarget "ExampleApp (iOS)" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 312C372528475E63003E9E39 /* Debug */, + 312C372628475E63003E9E39 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 316D95ED28477B0E008475F9 /* AppFeature */ = { + isa = XCSwiftPackageProductDependency; + productName = AppFeature; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 312C370E28475E62003E9E39 /* Project object */; +} diff --git a/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..919434a6254f0e9651f402737811be6634a03e9c --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:"> + </FileRef> +</Workspace> diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000000000000000000000000000000..18d981003d68d0546c4804ac2ff47dd97c6e7921 --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IDEDidComputeMac32BitWarning</key> + <true/> +</dict> +</plist> diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000000000000000000000000000000000..08de0be8d3c8c1786ebe04545dd772526853eef4 --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key> + <false/> +</dict> +</plist> diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/ExampleApp (iOS).xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/ExampleApp (iOS).xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..57df4eaac7e1b2bcda0ab78bc6f21b6b61a287bf --- /dev/null +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/ExampleApp (iOS).xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1340" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "312C371528475E62003E9E39" + BuildableName = "dApps Example.app" + BlueprintName = "ExampleApp (iOS)" + ReferencedContainer = "container:Example.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "312C371528475E62003E9E39" + BuildableName = "dApps Example.app" + BlueprintName = "ExampleApp (iOS)" + ReferencedContainer = "container:Example.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "312C371528475E62003E9E39" + BuildableName = "dApps Example.app" + BlueprintName = "ExampleApp (iOS)" + ReferencedContainer = "container:Example.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/ExampleApp (iOS)/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..eb8789700816459c1e1480e0b34781d9fb78a1ca --- /dev/null +++ b/Example/ExampleApp (iOS)/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..84c0167b1a1045edaaee154e355e3d9362bb03c2 --- /dev/null +++ b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "filename" : "iPhone Notification 20pt 2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "iPhone Notification 20pt 3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "iPhone Settings 29pt 2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "iPhone Settings 29pt 3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "iPhone Spotlight 40pt 2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "iPhone Spotlight 40pt 3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "iPhone App 60pt 2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "iPhone App 60pt 3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "iPad Notification 20pt 1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "iPad Notification 20pt 2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "iPad Settings 29pt 1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "iPad Settings 29pt 2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "iPad Spotlight 40pt 1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "iPad Spotlight 40pt 2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "iPad App 76pt 1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "iPad App 76pt 2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "iPad Pro (12.9-inch) App 83.5pt 2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "iOS App Store 1024pt 1x.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..f74bafba9a74e2c0f9a075ef11c390612af07c69 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..33755231d84723c6cfc3f823bad05eb5c8961ba2 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5ae552b64790957d1a355d80eec642bf93d100 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9a346232d62f79579473062b9758afb7f8907f Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5a33677bdedee2496d9f1bf25f34dc88b0a473 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..35bcfba03a28d1a7e50573e9e6438cce84e75363 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..907b2832c75cded4bca5e65ec9da25f3c04d572a Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2aade831c23ada90101c8fc4cd8b07442a68fcb8 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5a33677bdedee2496d9f1bf25f34dc88b0a473 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c35f34b4c2a4bea13a14c6901964057817ec0a0c Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..911a010e155c39f2a82f8dd2b472a2dfbcfa486e Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5a7a7ac44be361e8fb67318af7b3028630d2f0 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5a33677bdedee2496d9f1bf25f34dc88b0a473 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3561cb40fa8325368092ed54c78d8ebaa670d170 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2aade831c23ada90101c8fc4cd8b07442a68fcb8 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2719654ceaa888998261e08e89b94143165c1b50 Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c35f34b4c2a4bea13a14c6901964057817ec0a0c Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png new file mode 100644 index 0000000000000000000000000000000000000000..911a010e155c39f2a82f8dd2b472a2dfbcfa486e Binary files /dev/null and b/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png differ diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/Contents.json b/Example/ExampleApp (iOS)/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/Example/ExampleApp (iOS)/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/ExampleApp (iOS)/Info.plist b/Example/ExampleApp (iOS)/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..669a5ca760ffe22bd5f4fec92e67939fa64476af --- /dev/null +++ b/Example/ExampleApp (iOS)/Info.plist @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>ITSAppUsesNonExemptEncryption</key> + <false/> + <key>UIApplicationSceneManifest</key> + <dict> + <key>UIApplicationSupportsMultipleScenes</key> + <false/> + </dict> +</dict> +</plist> diff --git a/Example/Package.swift b/Example/Package.swift new file mode 100644 index 0000000000000000000000000000000000000000..d0a31fe81dd861a3d33bf4eaeb4f59fb7c9f4947 --- /dev/null +++ b/Example/Package.swift @@ -0,0 +1,4 @@ +// swift-tools-version:5.6 +// This file makes Xcode doesn't display this directory inside swift package. +import PackageDescription +let package = Package(name: "", products: [], targets: []) diff --git a/Example/example-app-icon/.gitignore b/Example/example-app-icon/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3b29812086f28a2b21884e57ead495ffd9434178 --- /dev/null +++ b/Example/example-app-icon/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/ExampleAppIcon.xcscheme b/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/ExampleAppIcon.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..577a588463331de08076d97e67d30ddfa7eb6e70 --- /dev/null +++ b/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/ExampleAppIcon.xcscheme @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1330" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ExampleAppIcon" + BuildableName = "ExampleAppIcon" + BlueprintName = "ExampleAppIcon" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ExampleAppIcon" + BuildableName = "ExampleAppIcon" + BlueprintName = "ExampleAppIcon" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/example-app-icon-export.xcscheme b/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/example-app-icon-export.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..e903b8e44600283e207b4b588d9799cbd1a78f8e --- /dev/null +++ b/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/example-app-icon-export.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1330" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "example-app-icon-export" + BuildableName = "example-app-icon-export" + BlueprintName = "example-app-icon-export" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "example-app-icon-export" + BuildableName = "example-app-icon-export" + BlueprintName = "example-app-icon-export" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "example-app-icon-export" + BuildableName = "example-app-icon-export" + BlueprintName = "example-app-icon-export" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app-icon/Package.swift b/Example/example-app-icon/Package.swift new file mode 100644 index 0000000000000000000000000000000000000000..d7a9c8bdd343f98a34609faa7f18f473c534833f --- /dev/null +++ b/Example/example-app-icon/Package.swift @@ -0,0 +1,42 @@ +// swift-tools-version: 5.6 +import PackageDescription + +let package = Package( + name: "example-app-icon", + platforms: [ + .macOS(.v12), + ], + products: [ + .library( + name: "ExampleAppIcon", + targets: ["ExampleAppIcon"] + ), + .executable( + name: "example-app-icon-export", + targets: ["ExampleAppIconExport"] + ), + ], + dependencies: [ + .package( + url: "https://github.com/darrarski/swiftui-app-icon-creator.git", + .upToNextMajor(from: "1.2.0") + ), + ], + targets: [ + .target( + name: "ExampleAppIcon", + dependencies: [ + .product( + name: "AppIconCreator", + package: "swiftui-app-icon-creator" + ), + ] + ), + .executableTarget( + name: "ExampleAppIconExport", + dependencies: [ + .target(name: "ExampleAppIcon"), + ] + ) + ] +) diff --git a/Example/example-app-icon/Sources/ExampleAppIcon/ExampleAppIcon.swift b/Example/example-app-icon/Sources/ExampleAppIcon/ExampleAppIcon.swift new file mode 100644 index 0000000000000000000000000000000000000000..0c24ca0094213d7c58dc5b02361b6bc45cbf96a1 --- /dev/null +++ b/Example/example-app-icon/Sources/ExampleAppIcon/ExampleAppIcon.swift @@ -0,0 +1,73 @@ +import SwiftUI +import AppIconCreator + +public struct ExampleAppIconView: View { + public init() {} + + public var body: some View { + GeometryReader { geometry in + ZStack { + Image(systemName: "cube.transparent") + .resizable() + .scaledToFit() + .foregroundColor(.black.opacity(0.2)) + .padding(geometry.size.width * 0.1) + .mask( + ZStack { + Rectangle() + + Image(systemName: "cube") + .resizable() + .scaledToFit() + .blendMode(.destinationOut) + .padding(geometry.size.width * 0.1) + + Circle() + .blendMode(.destinationOut) + .padding(geometry.size.width * 0.24) + } + ) + + Circle() + .fill(.black.opacity(0.3)) + .padding(geometry.size.width * 0.3) + .mask { + ZStack { + Rectangle() + Image(systemName: "cube") + .resizable() + .scaledToFit() + .blendMode(.destinationOut) + .padding(geometry.size.width * 0.1) + } + } + + Image(systemName: "cube") + .resizable() + .scaledToFit() + .foregroundColor(.black.opacity(0.5)) + .padding(geometry.size.width * 0.1) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background { + LinearGradient( + gradient: Gradient(colors: [ + Color(cgColor: CGColor(red: 0.49, green: 0.94, blue: 0.94, alpha: 1)), + Color(cgColor: CGColor(red: 0.16, green: 0.81, blue: 0.86, alpha: 1)), + ]), + startPoint: .top, + endPoint: .bottom + ) + } + } + } +} + +struct ExampleAppIconView_Previews: PreviewProvider { + static var previews: some View { + IconPreviews( + icon: ExampleAppIconView(), + configs: .iOS + ) + } +} diff --git a/Example/example-app-icon/Sources/ExampleAppIconExport/main.swift b/Example/example-app-icon/Sources/ExampleAppIconExport/main.swift new file mode 100644 index 0000000000000000000000000000000000000000..87afce21a46c6c01c1d08dc08cfeed6cd11cfb87 --- /dev/null +++ b/Example/example-app-icon/Sources/ExampleAppIconExport/main.swift @@ -0,0 +1,24 @@ +import AppIconCreator +import ExampleAppIcon +import Foundation + +extension URL { + func deletingLastPathComponent() -> URL { + var url = self + url.deleteLastPathComponent() + return url + } +} + +let exportURL = URL(fileURLWithPath: #file) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("ExampleApp (iOS)") + .appendingPathComponent("Assets.xcassets") + .appendingPathComponent("AppIcon.appiconset") + +[IconImage] + .images(for: ExampleAppIconView(), with: .iOS) + .forEach { $0.save(to: exportURL) } diff --git a/Example/example-app/.gitignore b/Example/example-app/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3b29812086f28a2b21884e57ead495ffd9434178 --- /dev/null +++ b/Example/example-app/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..076cfa82379a4f3c57008017aa6f356892754cc9 --- /dev/null +++ b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1340" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeature" + BuildableName = "AppFeature" + BlueprintName = "AppFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeatureTests" + BuildableName = "AppFeatureTests" + BlueprintName = "AppFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeature" + BuildableName = "AppFeature" + BlueprintName = "AppFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/ErrorFeature.xcscheme b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/ErrorFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..95ae136eb336e7590ebc8e1d791912f4c0393a84 --- /dev/null +++ b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/ErrorFeature.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1340" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ErrorFeature" + BuildableName = "ErrorFeature" + BlueprintName = "ErrorFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ErrorFeatureTests" + BuildableName = "ErrorFeatureTests" + BlueprintName = "ErrorFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ErrorFeature" + BuildableName = "ErrorFeature" + BlueprintName = "ErrorFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/LandingFeature.xcscheme b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/LandingFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..7d203fde1b21e22854fa66dfc0b2f4253280f862 --- /dev/null +++ b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/LandingFeature.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1340" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "LandingFeature" + BuildableName = "LandingFeature" + BlueprintName = "LandingFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "LandingFeatureTests" + BuildableName = "LandingFeatureTests" + BlueprintName = "LandingFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "LandingFeature" + BuildableName = "LandingFeature" + BlueprintName = "LandingFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/SessionFeature.xcscheme b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/SessionFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..9361c5551eb399da5f095ac23edd410d6b284503 --- /dev/null +++ b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/SessionFeature.xcscheme @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1340" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SessionFeature" + BuildableName = "SessionFeature" + BlueprintName = "SessionFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SessionFeatureTests" + BuildableName = "SessionFeatureTests" + BlueprintName = "SessionFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SessionFeature" + BuildableName = "SessionFeature" + BlueprintName = "SessionFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/example-app.xcscheme b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/example-app.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..ac213623f3e728760923364f7769a3ffd075fbd1 --- /dev/null +++ b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/example-app.xcscheme @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1340" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeature" + BuildableName = "AppFeature" + BlueprintName = "AppFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ErrorFeature" + BuildableName = "ErrorFeature" + BlueprintName = "ErrorFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "LandingFeature" + BuildableName = "LandingFeature" + BlueprintName = "LandingFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SessionFeature" + BuildableName = "SessionFeature" + BlueprintName = "SessionFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeatureTests" + BuildableName = "AppFeatureTests" + BlueprintName = "AppFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ErrorFeatureTests" + BuildableName = "ErrorFeatureTests" + BlueprintName = "ErrorFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "LandingFeatureTests" + BuildableName = "LandingFeatureTests" + BlueprintName = "LandingFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "SessionFeatureTests" + BuildableName = "SessionFeatureTests" + BlueprintName = "SessionFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppFeature" + BuildableName = "AppFeature" + BlueprintName = "AppFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/example-app/Package.swift b/Example/example-app/Package.swift new file mode 100644 index 0000000000000000000000000000000000000000..3b02bf979acbbd2d44d20cc04eca3ad9dc4a8ce9 --- /dev/null +++ b/Example/example-app/Package.swift @@ -0,0 +1,153 @@ +// swift-tools-version: 5.6 + +import PackageDescription + +let swiftSettings: [SwiftSetting] = [ + .unsafeFlags( + [ + "-Xfrontend", + "-debug-time-function-bodies", + "-Xfrontend", + "-debug-time-expression-type-checking", + ], + .when(configuration: .debug) + ), +] + +let package = Package( + name: "example-app", + platforms: [ + .iOS(.v15), + ], + products: [ + .library( + name: "AppFeature", + targets: ["AppFeature"] + ), + .library( + name: "ErrorFeature", + targets: ["ErrorFeature"] + ), + .library( + name: "LandingFeature", + targets: ["LandingFeature"] + ), + .library( + name: "SessionFeature", + targets: ["SessionFeature"] + ), + ], + dependencies: [ + .package(path: "../../"), // elixxir-dapps-sdk-swift + .package( + url: "https://github.com/pointfreeco/swift-composable-architecture.git", + .upToNextMajor(from: "0.35.0") + ), + .package( + url: "https://github.com/darrarski/swift-composable-presentation.git", + .upToNextMajor(from: "0.5.2") + ), + .package( + url: "https://github.com/kishikawakatsumi/KeychainAccess.git", + .upToNextMajor(from: "4.2.2") + ), + ], + targets: [ + .target( + name: "AppFeature", + dependencies: [ + .target(name: "ErrorFeature"), + .target(name: "LandingFeature"), + .target(name: "SessionFeature"), + .product( + name: "ElixxirDAppsSDK", + package: "elixxir-dapps-sdk-swift" + ), + .product( + name: "ComposableArchitecture", + package: "swift-composable-architecture" + ), + .product( + name: "ComposablePresentation", + package: "swift-composable-presentation" + ), + .product( + name: "KeychainAccess", + package: "KeychainAccess" + ), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "AppFeatureTests", + dependencies: [ + .target(name: "AppFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "ErrorFeature", + dependencies: [ + .product( + name: "ComposableArchitecture", + package: "swift-composable-architecture" + ), + .product( + name: "ElixxirDAppsSDK", + package: "elixxir-dapps-sdk-swift" + ), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "ErrorFeatureTests", + dependencies: [ + .target(name: "ErrorFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "LandingFeature", + dependencies: [ + .target(name: "ErrorFeature"), + .product( + name: "ComposableArchitecture", + package: "swift-composable-architecture" + ), + .product( + name: "ComposablePresentation", + package: "swift-composable-presentation" + ), + .product( + name: "ElixxirDAppsSDK", + package: "elixxir-dapps-sdk-swift" + ), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "LandingFeatureTests", + dependencies: [ + .target(name: "LandingFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "SessionFeature", + dependencies: [ + .product( + name: "ComposableArchitecture", + package: "swift-composable-architecture" + ), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "SessionFeatureTests", + dependencies: [ + .target(name: "SessionFeature"), + ], + swiftSettings: swiftSettings + ), + ] +) diff --git a/Example/example-app/Sources/AppFeature/App.swift b/Example/example-app/Sources/AppFeature/App.swift new file mode 100644 index 0000000000000000000000000000000000000000..1353882a744740f111d93562021f892f3108d5a9 --- /dev/null +++ b/Example/example-app/Sources/AppFeature/App.swift @@ -0,0 +1,46 @@ +import Combine +import ComposableArchitecture +import ElixxirDAppsSDK +import ErrorFeature +import LandingFeature +import SessionFeature +import SwiftUI + +@main +struct App: SwiftUI.App { + var body: some Scene { + WindowGroup { + AppView(store: Store( + initialState: AppState(), + reducer: appReducer, + environment: .live() + )) + } + } +} + +extension AppEnvironment { + static func live() -> AppEnvironment { + let clientSubject = CurrentValueSubject<Client?, Never>(nil) + let mainScheduler = DispatchQueue.main.eraseToAnyScheduler() + let bgScheduler = DispatchQueue( + label: "xx.network.dApps.ExampleApp.bg", + qos: .background + ).eraseToAnyScheduler() + + return AppEnvironment( + hasClient: clientSubject.map { $0 != nil }.eraseToAnyPublisher(), + mainScheduler: mainScheduler, + landing: LandingEnvironment( + clientStorage: .live( + passwordStorage: .keychain + ), + setClient: { clientSubject.send($0) }, + bgScheduler: bgScheduler, + mainScheduler: mainScheduler, + error: ErrorEnvironment() + ), + session: SessionEnvironment() + ) + } +} diff --git a/Example/example-app/Sources/AppFeature/AppFeature.swift b/Example/example-app/Sources/AppFeature/AppFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..b99904b08b9a7f800fb7d244e5e274f3f3715c57 --- /dev/null +++ b/Example/example-app/Sources/AppFeature/AppFeature.swift @@ -0,0 +1,104 @@ +import Combine +import ComposableArchitecture +import ComposablePresentation +import LandingFeature +import SessionFeature + +struct AppState: Equatable { + enum Scene: Equatable { + case landing(LandingState) + case session(SessionState) + } + + var scene: Scene = .landing(LandingState()) +} + +extension AppState.Scene { + var asLanding: LandingState? { + get { + guard case .landing(let state) = self else { return nil } + return state + } + set { + guard let newValue = newValue else { return } + self = .landing(newValue) + } + } + + var asSession: SessionState? { + get { + guard case .session(let state) = self else { return nil } + return state + } + set { + guard let newValue = newValue else { return } + self = .session(newValue) + } + } +} + +enum AppAction: Equatable { + case viewDidLoad + case clientDidChange(hasClient: Bool) + case landing(LandingAction) + case session(SessionAction) +} + +struct AppEnvironment { + var hasClient: AnyPublisher<Bool, Never> + var mainScheduler: AnySchedulerOf<DispatchQueue> + var landing: LandingEnvironment + var session: SessionEnvironment +} + +let appReducer = Reducer<AppState, AppAction, AppEnvironment> +{ state, action, env in + switch action { + case .viewDidLoad: + struct HasClientEffectId: Hashable {} + return env.hasClient + .removeDuplicates() + .map(AppAction.clientDidChange(hasClient:)) + .receive(on: env.mainScheduler) + .eraseToEffect() + .cancellable(id: HasClientEffectId(), cancelInFlight: true) + + case .clientDidChange(let hasClient): + if hasClient { + let sessionState = state.scene.asSession ?? SessionState() + state.scene = .session(sessionState) + } else { + let landingState = state.scene.asLanding ?? LandingState() + state.scene = .landing(landingState) + } + return .none + + case .landing(_), .session(_): + return .none + } +} +.presenting( + landingReducer, + state: .keyPath(\.scene.asLanding), + id: .notNil(), + action: /AppAction.landing, + environment: \.landing +) +.presenting( + sessionReducer, + state: .keyPath(\.scene.asSession), + id: .notNil(), + action: /AppAction.session, + environment: \.session +) + +#if DEBUG +extension AppEnvironment { + static let failing = AppEnvironment( + hasClient: Empty().eraseToAnyPublisher(), + mainScheduler: .failing, + landing: .failing, + session: .failing + ) +} +#endif diff --git a/Example/example-app/Sources/AppFeature/AppView.swift b/Example/example-app/Sources/AppFeature/AppView.swift new file mode 100644 index 0000000000000000000000000000000000000000..bf7bcd3d7718f938986b9d1401ef96231a212720 --- /dev/null +++ b/Example/example-app/Sources/AppFeature/AppView.swift @@ -0,0 +1,82 @@ +import ComposableArchitecture +import LandingFeature +import SessionFeature +import SwiftUI + +struct AppView: View { + let store: Store<AppState, AppAction> + + struct ViewState: Equatable { + enum Scene: Equatable { + case landing + case session + } + + let scene: Scene + + init(state: AppState) { + switch state.scene { + case .landing(_): + self.scene = .landing + + case .session(_): + self.scene = .session + } + } + } + + var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + ZStack { + SwitchStore(store.scope(state: \.scene)) { + CaseLet( + state: /AppState.Scene.landing, + action: AppAction.landing, + then: { store in + NavigationView { + LandingView(store: store) + } + .navigationViewStyle(.stack) + .transition(.asymmetric( + insertion: .move(edge: .leading), + removal: .opacity + )) + } + ) + + CaseLet( + state: /AppState.Scene.session, + action: AppAction.session, + then: { store in + NavigationView { + SessionView(store: store) + } + .navigationViewStyle(.stack) + .transition(.asymmetric( + insertion: .move(edge: .trailing), + removal: .opacity + )) + } + ) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .animation(.default, value: viewStore.scene) + .task { + viewStore.send(.viewDidLoad) + } + } + } +} + +#if DEBUG +struct AppView_Previews: PreviewProvider { + static var previews: some View { + AppView(store: Store( + initialState: AppState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Example/example-app/Sources/AppFeature/PasswordStorage+Keychain.swift b/Example/example-app/Sources/AppFeature/PasswordStorage+Keychain.swift new file mode 100644 index 0000000000000000000000000000000000000000..7af6bc4faa48b2b35a7a7a5eb8583f31998c5e14 --- /dev/null +++ b/Example/example-app/Sources/AppFeature/PasswordStorage+Keychain.swift @@ -0,0 +1,14 @@ +import ElixxirDAppsSDK +import KeychainAccess + +extension PasswordStorage { + static let keychain: PasswordStorage = { + let keychain = KeychainAccess.Keychain( + service: "xx.network.dApps.ExampleApp" + ) + return PasswordStorage( + save: { password in keychain[data: "password"] = password}, + load: { try keychain[data: "password"] ?? { throw PasswordStorageMissingPasswordError() }() } + ) + }() +} diff --git a/Example/example-app/Sources/ErrorFeature/ErrorFeature.swift b/Example/example-app/Sources/ErrorFeature/ErrorFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..dc7d8b69db96f23837fd62a352dbe8d4f0f2e2f0 --- /dev/null +++ b/Example/example-app/Sources/ErrorFeature/ErrorFeature.swift @@ -0,0 +1,24 @@ +import ComposableArchitecture +import SwiftUI + +public struct ErrorState: Equatable { + public init(error: NSError) { + self.error = error + } + + public var error: NSError +} + +public enum ErrorAction: Equatable {} + +public struct ErrorEnvironment { + public init() {} +} + +public let errorReducer = Reducer<ErrorState, ErrorAction, ErrorEnvironment>.empty + +#if DEBUG +extension ErrorEnvironment { + public static let failing = ErrorEnvironment() +} +#endif diff --git a/Example/example-app/Sources/ErrorFeature/ErrorView.swift b/Example/example-app/Sources/ErrorFeature/ErrorView.swift new file mode 100644 index 0000000000000000000000000000000000000000..35d9b84785a2906d677008ea64440a4952f84654 --- /dev/null +++ b/Example/example-app/Sources/ErrorFeature/ErrorView.swift @@ -0,0 +1,53 @@ +import ComposableArchitecture +import SwiftUI + +public struct ErrorView: View { + public init(store: Store<ErrorState, ErrorAction>) { + self.store = store + } + + let store: Store<ErrorState, ErrorAction> + @Environment(\.dismiss) var dismiss + + struct ViewState: Equatable { + let error: NSError + + init(state: ErrorState) { + error = state.error + } + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + NavigationView { + Form { + Text("\(viewStore.error)") + } + .navigationTitle("Error") + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button { + dismiss() + } label: { + Image(systemName: "xmark") + } + } + } + } + } + } +} + +#if DEBUG +public struct ErrorView_Previews: PreviewProvider { + public static var previews: some View { + ErrorView(store: .init( + initialState: .init( + error: NSError(domain: "preview", code: 1234) + ), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Example/example-app/Sources/LandingFeature/LandingFeature.swift b/Example/example-app/Sources/LandingFeature/LandingFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..dfca1d37c532b6cdbf682c3ea5465f4829e513e0 --- /dev/null +++ b/Example/example-app/Sources/LandingFeature/LandingFeature.swift @@ -0,0 +1,143 @@ +import Combine +import ComposableArchitecture +import ElixxirDAppsSDK +import ErrorFeature + +public struct LandingState: Equatable { + public init( + hasStoredClient: Bool = false, + isMakingClient: Bool = false, + isRemovingClient: Bool = false, + error: ErrorState? = nil + ) { + self.hasStoredClient = hasStoredClient + self.isMakingClient = isMakingClient + self.isRemovingClient = isRemovingClient + self.error = error + } + + var hasStoredClient: Bool + var isMakingClient: Bool + var isRemovingClient: Bool + var error: ErrorState? +} + +public enum LandingAction: Equatable { + case viewDidLoad + case makeClient + case didMakeClient + case didFailMakingClient(NSError) + case removeStoredClient + case didRemoveStoredClient + case didFailRemovingStoredClient(NSError) + case didDismissError + case error(ErrorAction) +} + +public struct LandingEnvironment { + public init( + clientStorage: ClientStorage, + setClient: @escaping (Client) -> Void, + bgScheduler: AnySchedulerOf<DispatchQueue>, + mainScheduler: AnySchedulerOf<DispatchQueue>, + error: ErrorEnvironment + ) { + self.clientStorage = clientStorage + self.setClient = setClient + self.bgScheduler = bgScheduler + self.mainScheduler = mainScheduler + self.error = error + } + + public var clientStorage: ClientStorage + public var setClient: (Client) -> Void + public var bgScheduler: AnySchedulerOf<DispatchQueue> + public var mainScheduler: AnySchedulerOf<DispatchQueue> + public var error: ErrorEnvironment +} + +public let landingReducer = Reducer<LandingState, LandingAction, LandingEnvironment> +{ state, action, env in + switch action { + case .viewDidLoad: + state.hasStoredClient = env.clientStorage.hasStoredClient() + return .none + + case .makeClient: + state.isMakingClient = true + return Effect.future { fulfill in + do { + if env.clientStorage.hasStoredClient() { + env.setClient(try env.clientStorage.loadClient()) + } else { + env.setClient(try env.clientStorage.createClient()) + } + fulfill(.success(.didMakeClient)) + } catch { + fulfill(.success(.didFailMakingClient(error as NSError))) + } + } + .subscribe(on: env.bgScheduler) + .receive(on: env.mainScheduler) + .eraseToEffect() + + case .didMakeClient: + state.isMakingClient = false + state.hasStoredClient = env.clientStorage.hasStoredClient() + return .none + + case .didFailMakingClient(let error): + state.isMakingClient = false + state.hasStoredClient = env.clientStorage.hasStoredClient() + state.error = ErrorState(error: error) + return .none + + case .removeStoredClient: + state.isRemovingClient = true + return Effect.future { fulfill in + do { + try env.clientStorage.removeClient() + fulfill(.success(.didRemoveStoredClient)) + } catch { + fulfill(.success(.didFailRemovingStoredClient(error as NSError))) + } + } + .subscribe(on: env.bgScheduler) + .receive(on: env.mainScheduler) + .eraseToEffect() + + case .didRemoveStoredClient: + state.isRemovingClient = false + state.hasStoredClient = env.clientStorage.hasStoredClient() + return .none + + case .didFailRemovingStoredClient(let error): + state.isRemovingClient = false + state.hasStoredClient = env.clientStorage.hasStoredClient() + state.error = ErrorState(error: error) + return .none + + case .didDismissError: + state.error = nil + return .none + } +} +.presenting( + errorReducer, + state: .keyPath(\.error), + id: .keyPath(\.?.error), + action: /LandingAction.error, + environment: \.error +) + +#if DEBUG +extension LandingEnvironment { + public static let failing = LandingEnvironment( + clientStorage: .failing, + setClient: { _ in fatalError() }, + bgScheduler: .failing, + mainScheduler: .failing, + error: .failing + ) +} +#endif diff --git a/Example/example-app/Sources/LandingFeature/LandingView.swift b/Example/example-app/Sources/LandingFeature/LandingView.swift new file mode 100644 index 0000000000000000000000000000000000000000..a4dd2b73face637d99e3615922a512d84d9994e5 --- /dev/null +++ b/Example/example-app/Sources/LandingFeature/LandingView.swift @@ -0,0 +1,91 @@ +import ComposableArchitecture +import ComposablePresentation +import ErrorFeature +import SwiftUI + +public struct LandingView: View { + public init(store: Store<LandingState, LandingAction>) { + self.store = store + } + + let store: Store<LandingState, LandingAction> + + struct ViewState: Equatable { + let hasStoredClient: Bool + let isMakingClient: Bool + let isRemovingClient: Bool + + init(state: LandingState) { + hasStoredClient = state.hasStoredClient + isMakingClient = state.isMakingClient + isRemovingClient = state.isRemovingClient + } + + var isLoading: Bool { + isMakingClient || + isRemovingClient + } + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + Form { + Button { + viewStore.send(.makeClient) + } label: { + HStack { + Text(viewStore.hasStoredClient ? "Load stored client" : "Create new client") + Spacer() + if viewStore.isMakingClient { + ProgressView() + } + } + } + + if viewStore.hasStoredClient { + Button(role: .destructive) { + viewStore.send(.removeStoredClient) + } label: { + HStack { + Text("Remove stored client") + Spacer() + if viewStore.isRemovingClient { + ProgressView() + } + } + } + } + } + .navigationTitle("Landing") + .disabled(viewStore.isLoading) + .task { + viewStore.send(.viewDidLoad) + } + .sheet( + store.scope( + state: \.error, + action: LandingAction.error + ), + onDismiss: { + viewStore.send(.didDismissError) + }, + content: ErrorView.init(store:) + ) + } + } +} + +#if DEBUG +public struct LandingView_Previews: PreviewProvider { + public static var previews: some View { + NavigationView { + LandingView(store: .init( + initialState: .init(), + reducer: .empty, + environment: () + )) + } + .navigationViewStyle(.stack) + } +} +#endif diff --git a/Example/example-app/Sources/SessionFeature/SessionFeature.swift b/Example/example-app/Sources/SessionFeature/SessionFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..3b7dd16fe1aecf92658f3d9e2508b2fefc20927a --- /dev/null +++ b/Example/example-app/Sources/SessionFeature/SessionFeature.swift @@ -0,0 +1,27 @@ +import ComposableArchitecture + +public struct SessionState: Equatable { + public init() {} +} + +public enum SessionAction: Equatable { + case viewDidLoad +} + +public struct SessionEnvironment { + public init() {} +} + +public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironment> +{ state, action, env in + switch action { + case .viewDidLoad: + return .none + } +} + +#if DEBUG +extension SessionEnvironment { + public static let failing = SessionEnvironment() +} +#endif diff --git a/Example/example-app/Sources/SessionFeature/SessionView.swift b/Example/example-app/Sources/SessionFeature/SessionView.swift new file mode 100644 index 0000000000000000000000000000000000000000..eb1fcf0071a4e534f867c443b2913cbab1ec0779 --- /dev/null +++ b/Example/example-app/Sources/SessionFeature/SessionView.swift @@ -0,0 +1,36 @@ +import ComposableArchitecture +import SwiftUI + +public struct SessionView: View { + public init(store: Store<SessionState, SessionAction>) { + self.store = store + } + + let store: Store<SessionState, SessionAction> + + struct ViewState: Equatable { + init(state: SessionState) {} + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + Text("SessionView") + .navigationTitle("Session") + .task { + viewStore.send(.viewDidLoad) + } + } + } +} + +#if DEBUG +public struct SessionView_Previews: PreviewProvider { + public static var previews: some View { + SessionView(store: .init( + initialState: .init(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift b/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..96a0b07759825f391e32584051116f09acf55934 --- /dev/null +++ b/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift @@ -0,0 +1,50 @@ +import Combine +import ComposableArchitecture +import LandingFeature +import SessionFeature +import XCTest +@testable import AppFeature + +final class AppFeatureTests: XCTestCase { + func testViewDidLoad() throws { + let hasClient = PassthroughSubject<Bool, Never>() + let mainScheduler = DispatchQueue.test + + var env = AppEnvironment.failing + env.hasClient = hasClient.eraseToAnyPublisher() + env.mainScheduler = mainScheduler.eraseToAnyScheduler() + + let store = TestStore( + initialState: AppState(), + reducer: appReducer, + environment: env + ) + + store.send(.viewDidLoad) + + hasClient.send(false) + mainScheduler.advance() + + store.receive(.clientDidChange(hasClient: false)) + + hasClient.send(true) + mainScheduler.advance() + + store.receive(.clientDidChange(hasClient: true)) { + $0.scene = .session(SessionState()) + } + + hasClient.send(true) + mainScheduler.advance() + + hasClient.send(false) + mainScheduler.advance() + + store.receive(.clientDidChange(hasClient: false)) { + $0.scene = .landing(LandingState()) + } + + hasClient.send(completion: .finished) + mainScheduler.advance() + } +} diff --git a/Example/example-app/Tests/ErrorFeatureTests/ErrorFeatureTests.swift b/Example/example-app/Tests/ErrorFeatureTests/ErrorFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..50dabc308273fc6c77beafab3ab8733397b462bc --- /dev/null +++ b/Example/example-app/Tests/ErrorFeatureTests/ErrorFeatureTests.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import ErrorFeature + +final class ErrorFeatureTests: XCTestCase { + func testExample() { + XCTAssert(true) + } +} diff --git a/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift b/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..df9791e7de8c81a4a43f21a11e16f188afa044f7 --- /dev/null +++ b/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift @@ -0,0 +1,188 @@ +import ComposableArchitecture +import ErrorFeature +import XCTest +@testable import LandingFeature + +final class LandingFeatureTests: XCTestCase { + func testViewDidLoad() throws { + var env = LandingEnvironment.failing + env.clientStorage.hasStoredClient = { true } + + let store = TestStore( + initialState: LandingState(), + reducer: landingReducer, + environment: env + ) + + store.send(.viewDidLoad) { + $0.hasStoredClient = true + } + } + + func testCreateClient() { + var hasStoredClient = false + var didSetClient = false + let bgScheduler = DispatchQueue.test + let mainScheduler = DispatchQueue.test + + var env = LandingEnvironment.failing + env.clientStorage.hasStoredClient = { hasStoredClient } + env.clientStorage.createClient = { .failing } + env.setClient = { _ in didSetClient = true } + env.bgScheduler = bgScheduler.eraseToAnyScheduler() + env.mainScheduler = mainScheduler.eraseToAnyScheduler() + + let store = TestStore( + initialState: LandingState(), + reducer: landingReducer, + environment: env + ) + + store.send(.makeClient) { + $0.isMakingClient = true + } + + bgScheduler.advance() + + XCTAssertTrue(didSetClient) + + hasStoredClient = true + mainScheduler.advance() + + store.receive(.didMakeClient) { + $0.isMakingClient = false + $0.hasStoredClient = true + } + } + + func testLoadStoredClient() { + var didSetClient = false + let bgScheduler = DispatchQueue.test + let mainScheduler = DispatchQueue.test + + var env = LandingEnvironment.failing + env.clientStorage.hasStoredClient = { true } + env.clientStorage.loadClient = { .failing } + env.setClient = { _ in didSetClient = true } + env.bgScheduler = bgScheduler.eraseToAnyScheduler() + env.mainScheduler = mainScheduler.eraseToAnyScheduler() + + let store = TestStore( + initialState: LandingState(), + reducer: landingReducer, + environment: env + ) + + store.send(.makeClient) { + $0.isMakingClient = true + } + + bgScheduler.advance() + + XCTAssertTrue(didSetClient) + + mainScheduler.advance() + + store.receive(.didMakeClient) { + $0.isMakingClient = false + $0.hasStoredClient = true + } + } + + func testMakeClientFailure() { + let error = NSError(domain: "test", code: 1234) + let bgScheduler = DispatchQueue.test + let mainScheduler = DispatchQueue.test + + var env = LandingEnvironment.failing + env.clientStorage.hasStoredClient = { false } + env.clientStorage.createClient = { throw error } + env.bgScheduler = bgScheduler.eraseToAnyScheduler() + env.mainScheduler = mainScheduler.eraseToAnyScheduler() + + let store = TestStore( + initialState: LandingState(), + reducer: landingReducer, + environment: env + ) + + store.send(.makeClient) { + $0.isMakingClient = true + } + + bgScheduler.advance() + mainScheduler.advance() + + store.receive(.didFailMakingClient(error)) { + $0.isMakingClient = false + $0.hasStoredClient = false + $0.error = ErrorState(error: error) + } + } + + func testRemoveStoredClient() { + var hasStoredClient = true + var didRemoveClient = false + let bgScheduler = DispatchQueue.test + let mainScheduler = DispatchQueue.test + + var env = LandingEnvironment.failing + env.clientStorage.hasStoredClient = { hasStoredClient } + env.clientStorage.removeClient = { didRemoveClient = true } + env.bgScheduler = bgScheduler.eraseToAnyScheduler() + env.mainScheduler = mainScheduler.eraseToAnyScheduler() + + let store = TestStore( + initialState: LandingState(), + reducer: landingReducer, + environment: env + ) + + store.send(.removeStoredClient) { + $0.isRemovingClient = true + } + + bgScheduler.advance() + + XCTAssertTrue(didRemoveClient) + + hasStoredClient = false + mainScheduler.advance() + + store.receive(.didRemoveStoredClient) { + $0.isRemovingClient = false + $0.hasStoredClient = false + } + } + + func testRemoveStoredClientFailure() { + let error = NSError(domain: "test", code: 1234) + let bgScheduler = DispatchQueue.test + let mainScheduler = DispatchQueue.test + + var env = LandingEnvironment.failing + env.clientStorage.hasStoredClient = { true } + env.clientStorage.removeClient = { throw error } + env.bgScheduler = bgScheduler.eraseToAnyScheduler() + env.mainScheduler = mainScheduler.eraseToAnyScheduler() + + let store = TestStore( + initialState: LandingState(), + reducer: landingReducer, + environment: env + ) + + store.send(.removeStoredClient) { + $0.isRemovingClient = true + } + + bgScheduler.advance() + mainScheduler.advance() + + store.receive(.didFailRemovingStoredClient(error)) { + $0.isRemovingClient = false + $0.hasStoredClient = true + $0.error = ErrorState(error: error) + } + } +} diff --git a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..5ed11e32b14f6ee73da18a53cee600418122f87c --- /dev/null +++ b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift @@ -0,0 +1,15 @@ +import ComposableArchitecture +import XCTest +@testable import SessionFeature + +final class SessionFeatureTests: XCTestCase { + func testViewDidLoad() throws { + let store = TestStore( + initialState: SessionState(), + reducer: sessionReducer, + environment: .failing + ) + + store.send(.viewDidLoad) + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..8551215fa5b651bb93a247cde5ab89d4b8f5f7e6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2022, xx network SEZC + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. 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. + +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. diff --git a/Package.swift b/Package.swift index 058d1eb9fa9bd8a9274a3dff49763296c3c571c0..1c347fa7f59cb8ac18b1cabd166caaa5f9e9d4a3 100644 --- a/Package.swift +++ b/Package.swift @@ -34,11 +34,6 @@ let package = Package( ], swiftSettings: swiftSettings ), - .testTarget( - name: "ElixxirDAppsSDKTests", - dependencies: ["ElixxirDAppsSDK"], - swiftSettings: swiftSettings - ), .binaryTarget( name: "Bindings", path: "Frameworks/Bindings.xcframework" diff --git a/README.md b/README.md index 1dcb9cedd6da2b43ccf2a9bb14263d3e662b9577..49d8fbb44a5398de29a79d5b13be47e1ba0db1d0 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,38 @@   + +## 🛠Development + +Open `ElixxirDAppsSDK.xcworkspace` in Xcode (≥13.4). + +### Project structure + +``` +ElixxirDAppsSDK [Xcode Workspace] + ├─ elixxir-dapps-sdk-swift [Swift Package] + | └─ ElixxirDAppsSDK [Library] + └─ Example [Xcode Project] + ├─ ExampleApp (iOS) [iOS App Target] + ├─ example-app [Swift Package] + | ├─ AppFeature [Library] + | └─ ... + └─ example-app-icon [Swift Package] + ├─ ExampleAppIcon [Library] + └─ example-app-icon-export [Executable] +``` + +### Build schemes + +- Use `exlixxir-dapps-sdk-swift` scheme to build the package with `ElixxirDAppsSDK` library. +- Use `ExampleApp (iOS)` to build and run the example app. +- Use `example-app` scheme to build and test the example app package with all contained libraries. +- Use `ExampleAppIcon` scheme with macOS target to build and preview the example app icon. +- Use `example-app-icon-export` scheme with macOS target to build and update the example app icon. +- Use other schemes, like `AppFeature`, for building and testing individual libraries in isolation. + +## 📄 License + +Copyright © 2022 xx network SEZC + +[License](LICENSE) diff --git a/Sources/ElixxirDAppsSDK/Client.swift b/Sources/ElixxirDAppsSDK/Client.swift new file mode 100644 index 0000000000000000000000000000000000000000..49a970947ecb0c5b5e80a3965d1dc0399855fbab --- /dev/null +++ b/Sources/ElixxirDAppsSDK/Client.swift @@ -0,0 +1,17 @@ +import Bindings + +public struct Client { + +} + +extension Client { + public static func live(bindingsClient: BindingsClient) -> Client { + Client() + } +} + +#if DEBUG +extension Client { + public static let failing = Client() +} +#endif diff --git a/Sources/ElixxirDAppsSDK/ClientCreator.swift b/Sources/ElixxirDAppsSDK/ClientCreator.swift new file mode 100644 index 0000000000000000000000000000000000000000..e048227edb7b0db96fc42362725acb2bbf519d44 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/ClientCreator.swift @@ -0,0 +1,32 @@ +import Bindings + +public struct ClientCreator { + public var create: (URL, Data, Data) throws -> Void + + public func callAsFunction(directoryURL: URL, ndf: Data, password: Data) throws { + try create(directoryURL, ndf, password) + } +} + +extension ClientCreator { + public static let live = ClientCreator { directoryURL, ndf, password in + var error: NSError? + let network = String(data: ndf, encoding: .utf8)! + let created = BindingsNewClient(network, directoryURL.path, password, nil, &error) + if let error = error { + throw error + } + if !created { + throw BindingsNewClientUnknownError() + } + } +} + +#if DEBUG +extension ClientCreator { + public static let failing = ClientCreator { _, _, _ in + struct NotImplemented: Error {} + throw NotImplemented() + } +} +#endif diff --git a/Sources/ElixxirDAppsSDK/ClientLoader.swift b/Sources/ElixxirDAppsSDK/ClientLoader.swift new file mode 100644 index 0000000000000000000000000000000000000000..632f01070f054694ab261c5b045e6956060b29a2 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/ClientLoader.swift @@ -0,0 +1,30 @@ +import Bindings + +public struct ClientLoader { + public var load: (URL, Data) throws -> Client + + public func callAsFunction(directoryURL: URL, password: Data) throws -> Client { + try load(directoryURL, password) + } +} + +extension ClientLoader { + public static let live = ClientLoader { directoryURL, password in + var error: NSError? + let bindingsClient = BindingsLogin(directoryURL.path, password, &error) + if let error = error { throw error } + guard let bindingsClient = bindingsClient else { + throw BindingsLoginUnknownError() + } + return Client.live(bindingsClient: bindingsClient) + } +} + +#if DEBUG +extension ClientLoader { + public static let failing = ClientLoader { _, _ in + struct NotImplemented: Error {} + throw NotImplemented() + } +} +#endif diff --git a/Sources/ElixxirDAppsSDK/ClientStorage.swift b/Sources/ElixxirDAppsSDK/ClientStorage.swift new file mode 100644 index 0000000000000000000000000000000000000000..8471fa783efd5d7cd6ce80007bd9708d17ce3229 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/ClientStorage.swift @@ -0,0 +1,69 @@ +import Bindings + +public struct ClientStorage { + public var hasStoredClient: () -> Bool + public var createClient: () throws -> Client + public var loadClient: () throws -> Client + public var removeClient: () throws -> Void +} + +extension ClientStorage { + public static let defaultDirectoryURL = FileManager.default + .urls(for: .applicationSupportDirectory, in: .userDomainMask) + .first! + .appendingPathComponent("xx.network.client") + + public static func live( + environment: Environment = .mainnet, + directoryURL: URL = defaultDirectoryURL, + fileManager: FileManager = .default, + generatePassword: PasswordGenerator = .live, + passwordStorage: PasswordStorage, + downloadNDF: NDFDownloader = .live, + createClient: ClientCreator = .live, + loadClient: ClientLoader = .live + ) -> ClientStorage { + ClientStorage( + hasStoredClient: { + let contents = try? fileManager.contentsOfDirectory(atPath: directoryURL.path) + return contents.map { $0.isEmpty == false } ?? false + }, + createClient: { + let ndf = try downloadNDF(environment) + let password = try generatePassword() + try passwordStorage.save(password) + try? fileManager.removeItem(at: directoryURL) + try? fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true) + try createClient(directoryURL: directoryURL, ndf: ndf, password: password) + return try loadClient(directoryURL: directoryURL, password: password) + }, + loadClient: { + let password = try passwordStorage.load() + return try loadClient(directoryURL: directoryURL, password: password) + }, + removeClient: { + try fileManager.removeItem(at: directoryURL) + } + ) + } +} + +#if DEBUG +extension ClientStorage { + public static let failing = ClientStorage( + hasStoredClient: { false }, + createClient: { + struct NotImplemented: Error {} + throw NotImplemented() + }, + loadClient: { + struct NotImplemented: Error {} + throw NotImplemented() + }, + removeClient: { + struct NotImplemented: Error {} + throw NotImplemented() + } + ) +} +#endif diff --git a/Sources/ElixxirDAppsSDK/ElixxirDAppsSDK.swift b/Sources/ElixxirDAppsSDK/ElixxirDAppsSDK.swift deleted file mode 100644 index 839b3ade88185a4312b2651b41d3316e975ef2c7..0000000000000000000000000000000000000000 --- a/Sources/ElixxirDAppsSDK/ElixxirDAppsSDK.swift +++ /dev/null @@ -1 +0,0 @@ -private enum NotImplemented {} diff --git a/Sources/ElixxirDAppsSDK/Environment.swift b/Sources/ElixxirDAppsSDK/Environment.swift new file mode 100644 index 0000000000000000000000000000000000000000..92df7275de565f0953458ed05f6d32f2e04c9031 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/Environment.swift @@ -0,0 +1,52 @@ +import Foundation + +public struct Environment: Equatable { + public init(url: URL, cert: String) { + self.url = url + self.cert = cert + } + + public var url: URL + public var cert: String +} + +extension Environment { + public static let mainnet = Environment( + url: URL(string: "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json")!, + cert: """ + -----BEGIN CERTIFICATE----- + MIIFqTCCA5GgAwIBAgIUO0qHXSeKrOMucO+Zz82Mf1Zlq4gwDQYJKoZIhvcNAQEL + BQAwgYAxCzAJBgNVBAYTAktZMRQwEgYDVQQHDAtHZW9yZ2UgVG93bjETMBEGA1UE + CgwKeHggbmV0d29yazEPMA0GA1UECwwGRGV2T3BzMRMwEQYDVQQDDAp4eC5uZXR3 + b3JrMSAwHgYJKoZIhvcNAQkBFhFhZG1pbnNAeHgubmV0d29yazAeFw0yMTEwMzAy + MjI5MjZaFw0zMTEwMjgyMjI5MjZaMIGAMQswCQYDVQQGEwJLWTEUMBIGA1UEBwwL + R2VvcmdlIFRvd24xEzARBgNVBAoMCnh4IG5ldHdvcmsxDzANBgNVBAsMBkRldk9w + czETMBEGA1UEAwwKeHgubmV0d29yazEgMB4GCSqGSIb3DQEJARYRYWRtaW5zQHh4 + Lm5ldHdvcmswggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD08ixnPWwz + FtBIEWx2SnFjBsdrSWCp9NcWXRtGWeq3ACz+ixiflj/U9U4b57aULeOAvcoC7bwU + j5w3oYxRmXIV40QSevx1z9mNcW3xbbacQ+yCgPPhhj3/c285gVVOUzURLBTNAi9I + EA59zAb8Vy0E6zfq4HRAhH11Q/10QgDjEXuGXra1k3IlemVsouiJGNAtKojNDE1N + x9HnraSEiXzdnV2GDplEvMHaLd3s9vs4XsiLB3VwKyHv7EH9+LOIra6pr5BWw+kD + 2qHKGmQMOQe0a7nCirW/k9axH0WiA0XWuQu3U1WfcMEfdC/xn1vtubrdYjtzpXUy + oUEX5eHfu4OlA/zoH+trocfARDyBmTVbDy0P9imH//a6GUKDui9r3fXwEy5YPMhb + dKaNc7QWLPHMh1n25h559z6PqxxPT6UqFFbZD2gTw1sbbpjyqhLbnYguurkxY3jZ + ztW337hROzQ1/abbg/P59JA95Pmhkl8nqqDEf0buOmvMazq3Lwg92nuZ8gsdMKXB + xaEtTTpxhTPOqzc1/XQgScZnc+092MBDh3C2GMxzylOIdk+yF2Gyb+VWPUe29dSa + azzxsDXzRy8y8jaOjdSUWaLa/MgS5Dg1AfHtD55bdvqYzw3NEXIVarpMlzl+Z+6w + jvuwz8GyoMSVe+YEGgvSDvlfY/z19aqneQIDAQABoxkwFzAVBgNVHREEDjAMggp4 + eC5uZXR3b3JrMA0GCSqGSIb3DQEBCwUAA4ICAQCp0JDub2w5vZQvFREyA+utZ/+s + XT05j1iTgIRKMa3nofDGERYJUG7FcTd373I2baS70PGx8FF1QuXhn4DNNZlW/SZt + pa1d0pAerqFrIzwOuWVDponYHQ8ayvsT7awCbwZEZE4RhooqS4LqnvtgFu/g7LuM + zkFN8TER7HAUn3P7BujLvcgtqk2LMDz+AgBRszDp/Bw7+1EJDeG9d7hC/stXgDV/ + vpD1YDpxSmW4zjezFJqV6OdMOwo9RWVIktK3RXbFc6I5UJZ5kmzPe/I2oPPCBQvD + G3VqFLQe5ik5rXP7SgAN1fL/7KuQna0s42hkV64Z2ymCX69G1ofpgpEFaQLaxLbj + QOun0r8A3NyKvHRIh4K0dFcc3FSOF60Y6k769HKbOPmSDjSSg0qO9GEONBJ8BxAT + IHcHoTAOQoqGehdzepXQSjHsPqTXv3ZFFwCCgO0toI0Qhqwo89X6R3k+i4Kaktr7 + mLiPO8s0nq1PZ1XrybKE9BCHkYH1JkUDA+M0pn4QAEx/BuM0QnGXoi1sImW3pEUG + NP7fjkISrD48P8P/TLS45sx5pB8MNGEsRw0lBKmuOdWDmdfhOltB6JxmbhpstNZp + 6LVLK6SEOwE76xnHiisR2KyhTTiroUq73BgPFWkWhoJDPbmL1DHgnbdKwwstG8Qu + UGb8k8vh6tzqYZAOKg== + -----END CERTIFICATE----- + """ + ) +} diff --git a/Sources/ElixxirDAppsSDK/Errors.swift b/Sources/ElixxirDAppsSDK/Errors.swift new file mode 100644 index 0000000000000000000000000000000000000000..d28f5bafc0e6843d9a43a6f10171c5fa35596437 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/Errors.swift @@ -0,0 +1,19 @@ +public struct BindingsDownloadAndVerifySignedNdfWithUrlUnknownError: Error, Equatable { + public init() {} +} + +public struct BindingsGenerateSecretUnknownError: Error, Equatable { + public init() {} +} + +public struct PasswordStorageMissingPasswordError: Error, Equatable { + public init() {} +} + +public struct BindingsNewClientUnknownError: Error, Equatable { + public init() {} +} + +public struct BindingsLoginUnknownError: Error, Equatable { + public init() {} +} diff --git a/Sources/ElixxirDAppsSDK/NDFDownloader.swift b/Sources/ElixxirDAppsSDK/NDFDownloader.swift new file mode 100644 index 0000000000000000000000000000000000000000..c0552b99985d3a9b4c3a52fc554b2fad2a92a61c --- /dev/null +++ b/Sources/ElixxirDAppsSDK/NDFDownloader.swift @@ -0,0 +1,36 @@ +import Bindings + +public struct NDFDownloader { + public var run: (Environment) throws -> Data + + public func callAsFunction(_ env: Environment) throws -> Data { + try run(env) + } +} + +extension NDFDownloader { + public static let live = NDFDownloader { env in + var error: NSError? + let data = BindingsDownloadAndVerifySignedNdfWithUrl( + env.url.absoluteString, + env.cert, + &error + ) + if let error = error { + throw error + } + guard let data = data else { + throw BindingsDownloadAndVerifySignedNdfWithUrlUnknownError() + } + return data + } +} + +#if DEBUG +extension NDFDownloader { + public static let failing = NDFDownloader { _ in + struct NotImplemented: Error {} + throw NotImplemented() + } +} +#endif diff --git a/Sources/ElixxirDAppsSDK/PasswordGenerator.swift b/Sources/ElixxirDAppsSDK/PasswordGenerator.swift new file mode 100644 index 0000000000000000000000000000000000000000..251ea87399bbcd310c56dd78af6908d802f3aaf1 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/PasswordGenerator.swift @@ -0,0 +1,27 @@ +import Bindings + +public struct PasswordGenerator { + public var run: () throws -> Data + + public func callAsFunction() throws -> Data { + try run() + } +} + +extension PasswordGenerator { + public static let live = PasswordGenerator { + guard let secret = BindingsGenerateSecret(32) else { + throw BindingsGenerateSecretUnknownError() + } + return secret + } +} + +#if DEBUG +extension PasswordGenerator { + public static let failing = PasswordGenerator { + struct NotImplemented: Error {} + throw NotImplemented() + } +} +#endif diff --git a/Sources/ElixxirDAppsSDK/PasswordStorage.swift b/Sources/ElixxirDAppsSDK/PasswordStorage.swift new file mode 100644 index 0000000000000000000000000000000000000000..82ae04dcd844cd97c365bec9fe2f1d9170043a59 --- /dev/null +++ b/Sources/ElixxirDAppsSDK/PasswordStorage.swift @@ -0,0 +1,29 @@ +import Foundation + +public struct PasswordStorage { + public init( + save: @escaping (Data) throws -> Void, + load: @escaping () throws -> Data + ) { + self.save = save + self.load = load + } + + public var save: (Data) throws -> Void + public var load: () throws -> Data +} + +#if DEBUG +extension PasswordStorage { + public static let failing = PasswordStorage( + save: { _ in + struct NotImplemented: Error {} + throw NotImplemented() + }, + load: { + struct NotImplemented: Error {} + throw NotImplemented() + } + ) +} +#endif diff --git a/Tests/ElixxirDAppsSDKTests/ElixxirDAppsSDKTests.swift b/Tests/ElixxirDAppsSDKTests/ElixxirDAppsSDKTests.swift deleted file mode 100644 index 1332921d027786d3173684beb381b305a2e80337..0000000000000000000000000000000000000000 --- a/Tests/ElixxirDAppsSDKTests/ElixxirDAppsSDKTests.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest -@testable import ElixxirDAppsSDK - -final class ElixxirDAppsSDKTests: XCTestCase { - func testExample() { - XCTAssert(true) - } -}