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 @@
 
 ![Swift 5.6](https://img.shields.io/badge/swift-5.6-orange.svg)
 ![platform iOS](https://img.shields.io/badge/platform-iOS-blue.svg)
+
+## 🛠 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)
-  }
-}