diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/elixxir-dapps-sdk-swift.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/elixxir-dapps-sdk-swift-Package.xcscheme similarity index 76% rename from .swiftpm/xcode/xcshareddata/xcschemes/elixxir-dapps-sdk-swift.xcscheme rename to .swiftpm/xcode/xcshareddata/xcschemes/elixxir-dapps-sdk-swift-Package.xcscheme index 223297f5afd6ce419d4485fb8500ed96aeacbe18..7818f7363eeee23304e1aece2235ce6bbaa19fb8 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/elixxir-dapps-sdk-swift.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/elixxir-dapps-sdk-swift-Package.xcscheme @@ -34,6 +34,34 @@ ReferencedContainer = "container:"> </BuildableReference> </BuildActionEntry> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "XXClientTests" + BuildableName = "XXClientTests" + BlueprintName = "XXClientTests" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "NO" + buildForArchiving = "NO" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "XXMessengerClientTests" + BuildableName = "XXMessengerClientTests" + BlueprintName = "XXMessengerClientTests" + ReferencedContainer = "container:"> + </BuildableReference> + </BuildActionEntry> </BuildActionEntries> </BuildAction> <TestAction diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 08de0be8d3c8c1786ebe04545dd772526853eef4..0000000000000000000000000000000000000000 --- a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ -<?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/ExampleApp (iOS)/Info.plist b/Example/ExampleApp (iOS)/Info.plist deleted file mode 100644 index 669a5ca760ffe22bd5f4fec92e67939fa64476af..0000000000000000000000000000000000000000 --- a/Example/ExampleApp (iOS)/Info.plist +++ /dev/null @@ -1,13 +0,0 @@ -<?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/example-app-icon/.gitignore b/Example/example-app-icon/.gitignore deleted file mode 100644 index 3b29812086f28a2b21884e57ead495ffd9434178..0000000000000000000000000000000000000000 --- a/Example/example-app-icon/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.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/example-app-icon-export.xcscheme b/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/example-app-icon-export.xcscheme deleted file mode 100644 index e903b8e44600283e207b4b588d9799cbd1a78f8e..0000000000000000000000000000000000000000 --- a/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/example-app-icon-export.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ -<?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 deleted file mode 100644 index d7a9c8bdd343f98a34609faa7f18f473c534833f..0000000000000000000000000000000000000000 --- a/Example/example-app-icon/Package.swift +++ /dev/null @@ -1,42 +0,0 @@ -// 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 deleted file mode 100644 index 0c24ca0094213d7c58dc5b02361b6bc45cbf96a1..0000000000000000000000000000000000000000 --- a/Example/example-app-icon/Sources/ExampleAppIcon/ExampleAppIcon.swift +++ /dev/null @@ -1,73 +0,0 @@ -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 deleted file mode 100644 index 87afce21a46c6c01c1d08dc08cfeed6cd11cfb87..0000000000000000000000000000000000000000 --- a/Example/example-app-icon/Sources/ExampleAppIconExport/main.swift +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 3b29812086f28a2b21884e57ead495ffd9434178..0000000000000000000000000000000000000000 --- a/Example/example-app/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.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/example-app.xcscheme b/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/example-app.xcscheme deleted file mode 100644 index c6edd5dda410050c40e6df64bd48b668c357afde..0000000000000000000000000000000000000000 --- a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/example-app.xcscheme +++ /dev/null @@ -1,198 +0,0 @@ -<?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 = "MyContactFeature" - BuildableName = "MyContactFeature" - BlueprintName = "MyContactFeature" - ReferencedContainer = "container:"> - </BuildableReference> - </BuildActionEntry> - <BuildActionEntry - buildForTesting = "YES" - buildForRunning = "YES" - buildForProfiling = "YES" - buildForArchiving = "YES" - buildForAnalyzing = "YES"> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "MyIdentityFeature" - BuildableName = "MyIdentityFeature" - BlueprintName = "MyIdentityFeature" - 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 = "MyContactFeatureTests" - BuildableName = "MyContactFeatureTests" - BlueprintName = "MyContactFeatureTests" - ReferencedContainer = "container:"> - </BuildableReference> - </TestableReference> - <TestableReference - skipped = "NO"> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "MyIdentityFeatureTests" - BuildableName = "MyIdentityFeatureTests" - BlueprintName = "MyIdentityFeatureTests" - 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 deleted file mode 100644 index ec68a31a650f1ce9d1e4fc5a0282894c9e61a5b7..0000000000000000000000000000000000000000 --- a/Example/example-app/Package.swift +++ /dev/null @@ -1,121 +0,0 @@ -// 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: "../../" - ), - .package( - url: "https://github.com/pointfreeco/swift-composable-architecture.git", - .upToNextMajor(from: "0.39.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") - ), - .package( - url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git", - .upToNextMajor(from: "0.4.0") - ), - ], - targets: [ - .target( - name: "AppFeature", - dependencies: [ - .target(name: "ErrorFeature"), - .target(name: "LandingFeature"), - .target(name: "SessionFeature"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "ComposablePresentation", package: "swift-composable-presentation"), - .product(name: "KeychainAccess", package: "KeychainAccess"), - .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), - .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), - ], - swiftSettings: swiftSettings - ), - .testTarget( - name: "AppFeatureTests", - dependencies: [ - .target(name: "AppFeature"), - ], - swiftSettings: swiftSettings - ), - .target( - name: "ErrorFeature", - dependencies: [ - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), - .product(name: "XXClient", 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: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), - .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), - ], - swiftSettings: swiftSettings - ), - .testTarget( - name: "LandingFeatureTests", - dependencies: [ - .target(name: "LandingFeature"), - ], - swiftSettings: swiftSettings - ), - .target( - name: "SessionFeature", - dependencies: [ - .target(name: "ErrorFeature"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "ComposablePresentation", package: "swift-composable-presentation"), - .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), - .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), - ], - 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 deleted file mode 100644 index c1fa59417c089cbf3e9371ced8a3bb80225f0b37..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/AppFeature/App.swift +++ /dev/null @@ -1,52 +0,0 @@ -import Combine -import ComposableArchitecture -import ErrorFeature -import LandingFeature -import SessionFeature -import SwiftUI -import XXClient - -@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 cMixSubject = CurrentValueSubject<CMix?, Never>(nil) - let mainScheduler = DispatchQueue.main.eraseToAnyScheduler() - let bgScheduler = DispatchQueue( - label: "xx.network.dApps.ExampleApp.bg", - qos: .background - ).eraseToAnyScheduler() - - return AppEnvironment( - makeId: UUID.init, - hasCMix: { cMixSubject.map { $0 != nil }.eraseToAnyPublisher() }, - mainScheduler: mainScheduler, - landing: LandingEnvironment( - cMixManager: .live( - passwordStorage: .keychain - ), - setCMix: { cMixSubject.value = $0 }, - bgScheduler: bgScheduler, - mainScheduler: mainScheduler, - error: ErrorEnvironment() - ), - session: SessionEnvironment( - getCMix: { cMixSubject.value }, - bgScheduler: bgScheduler, - mainScheduler: mainScheduler, - error: ErrorEnvironment() - ) - ) - } -} diff --git a/Example/example-app/Sources/AppFeature/AppFeature.swift b/Example/example-app/Sources/AppFeature/AppFeature.swift deleted file mode 100644 index cc871972d695b8e0f35d56ba3d9b8d4d94d12a1e..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/AppFeature/AppFeature.swift +++ /dev/null @@ -1,107 +0,0 @@ -import Combine -import ComposableArchitecture -import ComposablePresentation -import LandingFeature -import SessionFeature -import XCTestDynamicOverlay - -struct AppState: Equatable { - enum Scene: Equatable { - case landing(LandingState) - case session(SessionState) - } - - var id: UUID = UUID() - var scene: Scene = .landing(LandingState(id: UUID())) -} - -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 cMixDidChange(hasCMix: Bool) - case landing(LandingAction) - case session(SessionAction) -} - -struct AppEnvironment { - var makeId: () -> UUID - var hasCMix: () -> AnyPublisher<Bool, Never> - var mainScheduler: AnySchedulerOf<DispatchQueue> - var landing: LandingEnvironment - var session: SessionEnvironment -} - -let appReducer = Reducer<AppState, AppAction, AppEnvironment> -{ state, action, env in - enum HasCMixEffectId {} - - switch action { - case .viewDidLoad: - return env.hasCMix() - .removeDuplicates() - .map(AppAction.cMixDidChange(hasCMix:)) - .receive(on: env.mainScheduler) - .eraseToEffect() - .cancellable(id: HasCMixEffectId.self, cancelInFlight: true) - - case .cMixDidChange(let hasClient): - if hasClient { - let sessionState = state.scene.asSession ?? SessionState(id: env.makeId()) - state.scene = .session(sessionState) - } else { - let landingState = state.scene.asLanding ?? LandingState(id: env.makeId()) - 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 -) - -extension AppEnvironment { - static let unimplemented = AppEnvironment( - makeId: XCTUnimplemented("\(Self.self).makeId", placeholder: UUID()), - hasCMix: XCTUnimplemented("\(Self.self).hasCMix", placeholder: Empty().eraseToAnyPublisher()), - mainScheduler: .unimplemented, - landing: .unimplemented, - session: .unimplemented - ) -} diff --git a/Example/example-app/Sources/AppFeature/AppView.swift b/Example/example-app/Sources/AppFeature/AppView.swift deleted file mode 100644 index bf7bcd3d7718f938986b9d1401ef96231a212720..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/AppFeature/AppView.swift +++ /dev/null @@ -1,82 +0,0 @@ -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 deleted file mode 100644 index 1b1a26de96c3fc947daf479a4d7eb528754aeada..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/AppFeature/PasswordStorage+Keychain.swift +++ /dev/null @@ -1,14 +0,0 @@ -import KeychainAccess -import XXClient - -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 MissingPasswordError() }() } - ) - }() -} diff --git a/Example/example-app/Sources/ErrorFeature/ErrorFeature.swift b/Example/example-app/Sources/ErrorFeature/ErrorFeature.swift deleted file mode 100644 index eb1671e8424a7db0ee5ccb12c8fef9289d1277da..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/ErrorFeature/ErrorFeature.swift +++ /dev/null @@ -1,22 +0,0 @@ -import ComposableArchitecture -import XCTestDynamicOverlay - -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 - -extension ErrorEnvironment { - public static let unimplemented = ErrorEnvironment() -} diff --git a/Example/example-app/Sources/ErrorFeature/ErrorView.swift b/Example/example-app/Sources/ErrorFeature/ErrorView.swift deleted file mode 100644 index 35d9b84785a2906d677008ea64440a4952f84654..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/ErrorFeature/ErrorView.swift +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 294b53f8ed023f78dd566fd6e5e6581fe4e68e7e..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/LandingFeature/LandingFeature.swift +++ /dev/null @@ -1,145 +0,0 @@ -import Combine -import ComposableArchitecture -import ErrorFeature -import XCTestDynamicOverlay -import XXClient - -public struct LandingState: Equatable { - public init( - id: UUID, - hasStoredCMix: Bool = false, - isMakingCMix: Bool = false, - isRemovingCMix: Bool = false, - error: ErrorState? = nil - ) { - self.id = id - self.hasStoredCMix = hasStoredCMix - self.isMakingCMix = isMakingCMix - self.isRemovingCMix = isRemovingCMix - self.error = error - } - - var id: UUID - var hasStoredCMix: Bool - var isMakingCMix: Bool - var isRemovingCMix: Bool - var error: ErrorState? -} - -public enum LandingAction: Equatable { - case viewDidLoad - case makeCMix - case didMakeCMix - case didFailMakingCMix(NSError) - case removeStoredCMix - case didRemoveStoredCMix - case didFailRemovingStoredCMix(NSError) - case didDismissError - case error(ErrorAction) -} - -public struct LandingEnvironment { - public init( - cMixManager: CMixManager, - setCMix: @escaping (CMix) -> Void, - bgScheduler: AnySchedulerOf<DispatchQueue>, - mainScheduler: AnySchedulerOf<DispatchQueue>, - error: ErrorEnvironment - ) { - self.cMixManager = cMixManager - self.setCMix = setCMix - self.bgScheduler = bgScheduler - self.mainScheduler = mainScheduler - self.error = error - } - - public var cMixManager: CMixManager - public var setCMix: (CMix) -> 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.hasStoredCMix = env.cMixManager.hasStorage() - return .none - - case .makeCMix: - state.isMakingCMix = true - return Effect.future { fulfill in - do { - if env.cMixManager.hasStorage() { - env.setCMix(try env.cMixManager.load()) - } else { - env.setCMix(try env.cMixManager.create()) - } - fulfill(.success(.didMakeCMix)) - } catch { - fulfill(.success(.didFailMakingCMix(error as NSError))) - } - } - .subscribe(on: env.bgScheduler) - .receive(on: env.mainScheduler) - .eraseToEffect() - - case .didMakeCMix: - state.isMakingCMix = false - state.hasStoredCMix = env.cMixManager.hasStorage() - return .none - - case .didFailMakingCMix(let error): - state.isMakingCMix = false - state.hasStoredCMix = env.cMixManager.hasStorage() - state.error = ErrorState(error: error) - return .none - - case .removeStoredCMix: - state.isRemovingCMix = true - return Effect.future { fulfill in - do { - try env.cMixManager.remove() - fulfill(.success(.didRemoveStoredCMix)) - } catch { - fulfill(.success(.didFailRemovingStoredCMix(error as NSError))) - } - } - .subscribe(on: env.bgScheduler) - .receive(on: env.mainScheduler) - .eraseToEffect() - - case .didRemoveStoredCMix: - state.isRemovingCMix = false - state.hasStoredCMix = env.cMixManager.hasStorage() - return .none - - case .didFailRemovingStoredCMix(let error): - state.isRemovingCMix = false - state.hasStoredCMix = env.cMixManager.hasStorage() - 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 -) - -extension LandingEnvironment { - public static let unimplemented = LandingEnvironment( - cMixManager: .unimplemented, - setCMix: XCTUnimplemented("\(Self.self).setCMix"), - bgScheduler: .unimplemented, - mainScheduler: .unimplemented, - error: .unimplemented - ) -} diff --git a/Example/example-app/Sources/LandingFeature/LandingView.swift b/Example/example-app/Sources/LandingFeature/LandingView.swift deleted file mode 100644 index e6903c2e957a9c40913b5f3635824fa43ef93ef5..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/LandingFeature/LandingView.swift +++ /dev/null @@ -1,91 +0,0 @@ -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 hasStoredCMix: Bool - let isMakingCMix: Bool - let isRemovingCMix: Bool - - init(state: LandingState) { - hasStoredCMix = state.hasStoredCMix - isMakingCMix = state.isMakingCMix - isRemovingCMix = state.isRemovingCMix - } - - var isLoading: Bool { - isMakingCMix || - isRemovingCMix - } - } - - public var body: some View { - WithViewStore(store.scope(state: ViewState.init)) { viewStore in - Form { - Button { - viewStore.send(.makeCMix) - } label: { - HStack { - Text(viewStore.hasStoredCMix ? "Load stored cMix" : "Create new cMix") - Spacer() - if viewStore.isMakingCMix { - ProgressView() - } - } - } - - if viewStore.hasStoredCMix { - Button(role: .destructive) { - viewStore.send(.removeStoredCMix) - } label: { - HStack { - Text("Remove stored cMix") - Spacer() - if viewStore.isRemovingCMix { - 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(id: UUID()), - reducer: .empty, - environment: () - )) - } - .navigationViewStyle(.stack) - } -} -#endif diff --git a/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift b/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift deleted file mode 100644 index 7b478312477ca5f898952a4ab50213dd9f3c9b3f..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift +++ /dev/null @@ -1,40 +0,0 @@ -import SwiftUI -import XXClient - -struct NetworkFollowerStatusView: View { - var status: NetworkFollowerStatus? - - var body: some View { - switch status { - case .stopped: - Label("Stopped", systemImage: "stop.fill") - - case .running: - Label("Running", systemImage: "play.fill") - - case .stopping: - Label("Stopping...", systemImage: "stop") - - case .unknown(let code): - Label("Status \(code)", systemImage: "questionmark") - - case .none: - Label("Unknown", systemImage: "questionmark") - } - } -} - -#if DEBUG -struct NetworkFollowerStatusView_Previews: PreviewProvider { - static var previews: some View { - Group { - NetworkFollowerStatusView(status: .stopped) - NetworkFollowerStatusView(status: .running) - NetworkFollowerStatusView(status: .stopping) - NetworkFollowerStatusView(status: .unknown(code: -1)) - NetworkFollowerStatusView(status: nil) - } - .previewLayout(.sizeThatFits) - } -} -#endif diff --git a/Example/example-app/Sources/SessionFeature/NetworkHealthStatusView.swift b/Example/example-app/Sources/SessionFeature/NetworkHealthStatusView.swift deleted file mode 100644 index e13cb6fc56378fc9717b4b7e5fe3469c0b7f3162..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/SessionFeature/NetworkHealthStatusView.swift +++ /dev/null @@ -1,33 +0,0 @@ -import SwiftUI - -struct NetworkHealthStatusView: View { - var status: Bool? - - var body: some View { - switch status { - case .some(true): - Label("Healthy", systemImage: "wifi") - .foregroundColor(.green) - - case .some(false): - Label("Unhealthy", systemImage: "bolt.horizontal.fill") - .foregroundColor(.red) - - case .none: - Label("Unknown", systemImage: "questionmark") - } - } -} - -#if DEBUG -struct NetworkHealthStatusView_Previews: PreviewProvider { - static var previews: some View { - Group { - NetworkHealthStatusView(status: true) - NetworkHealthStatusView(status: false) - NetworkHealthStatusView(status: nil) - } - .previewLayout(.sizeThatFits) - } -} -#endif diff --git a/Example/example-app/Sources/SessionFeature/SessionFeature.swift b/Example/example-app/Sources/SessionFeature/SessionFeature.swift deleted file mode 100644 index 6e0931cb84253f7dba686529e2fb16f19fec99e2..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/SessionFeature/SessionFeature.swift +++ /dev/null @@ -1,155 +0,0 @@ -import Combine -import ComposableArchitecture -import ErrorFeature -import XCTestDynamicOverlay -import XXClient - -public struct SessionState: Equatable { - public init( - id: UUID, - networkFollowerStatus: NetworkFollowerStatus? = nil, - isNetworkHealthy: Bool? = nil, - error: ErrorState? = nil - ) { - self.id = id - self.networkFollowerStatus = networkFollowerStatus - self.isNetworkHealthy = isNetworkHealthy - self.error = error - } - - public var id: UUID - public var networkFollowerStatus: NetworkFollowerStatus? - public var isNetworkHealthy: Bool? - public var error: ErrorState? -} - -public enum SessionAction: Equatable { - case viewDidLoad - case updateNetworkFollowerStatus - case didUpdateNetworkFollowerStatus(NetworkFollowerStatus?) - case runNetworkFollower(Bool) - case networkFollowerDidFail(NSError) - case monitorNetworkHealth(Bool) - case didUpdateNetworkHealth(Bool?) - case didDismissError - case error(ErrorAction) -} - -public struct SessionEnvironment { - public init( - getCMix: @escaping () -> CMix?, - bgScheduler: AnySchedulerOf<DispatchQueue>, - mainScheduler: AnySchedulerOf<DispatchQueue>, - error: ErrorEnvironment - ) { - self.getCMix = getCMix - self.bgScheduler = bgScheduler - self.mainScheduler = mainScheduler - self.error = error - } - - public var getCMix: () -> CMix? - public var bgScheduler: AnySchedulerOf<DispatchQueue> - public var mainScheduler: AnySchedulerOf<DispatchQueue> - public var error: ErrorEnvironment -} - -public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironment> -{ state, action, env in - switch action { - case .viewDidLoad: - return .merge([ - .init(value: .updateNetworkFollowerStatus), - .init(value: .monitorNetworkHealth(true)), - ]) - - case .updateNetworkFollowerStatus: - return Effect.future { fulfill in - let status = env.getCMix()?.networkFollowerStatus() - fulfill(.success(.didUpdateNetworkFollowerStatus(status))) - } - .subscribe(on: env.bgScheduler) - .receive(on: env.mainScheduler) - .eraseToEffect() - - case .didUpdateNetworkFollowerStatus(let status): - state.networkFollowerStatus = status - return .none - - case .runNetworkFollower(let start): - return Effect.run { subscriber in - do { - if start { - try env.getCMix()?.startNetworkFollower(timeoutMS: 30_000) - } else { - try env.getCMix()?.stopNetworkFollower() - } - } catch { - subscriber.send(.networkFollowerDidFail(error as NSError)) - } - let status = env.getCMix()?.networkFollowerStatus() - subscriber.send(.didUpdateNetworkFollowerStatus(status)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgScheduler) - .receive(on: env.mainScheduler) - .eraseToEffect() - - case .networkFollowerDidFail(let error): - state.error = ErrorState(error: error) - return .none - - case .monitorNetworkHealth(let start): - struct MonitorEffectId: Hashable { - var id: UUID - } - let effectId = MonitorEffectId(id: state.id) - if start { - return Effect.run { subscriber in - let callback = HealthCallback { isHealthy in - subscriber.send(.didUpdateNetworkHealth(isHealthy)) - } - let cancellable = env.getCMix()?.addHealthCallback(callback) - return AnyCancellable { - cancellable?.cancel() - } - } - .subscribe(on: env.bgScheduler) - .receive(on: env.mainScheduler) - .eraseToEffect() - .cancellable(id: effectId, cancelInFlight: true) - } else { - return Effect.cancel(id: effectId) - .subscribe(on: env.bgScheduler) - .eraseToEffect() - } - - case .didUpdateNetworkHealth(let isHealthy): - state.isNetworkHealthy = isHealthy - return .none - - case .didDismissError: - state.error = nil - return .none - - case .error(_): - return .none - } -} -.presenting( - errorReducer, - state: .keyPath(\.error), - id: .keyPath(\.?.error), - action: /SessionAction.error, - environment: \.error -) - -extension SessionEnvironment { - public static let unimplemented = SessionEnvironment( - getCMix: XCTUnimplemented("\(Self.self).getCMix"), - bgScheduler: .unimplemented, - mainScheduler: .unimplemented, - error: .unimplemented - ) -} diff --git a/Example/example-app/Sources/SessionFeature/SessionView.swift b/Example/example-app/Sources/SessionFeature/SessionView.swift deleted file mode 100644 index d095a4211a67d0e0e6b0c027a40641be66347cfb..0000000000000000000000000000000000000000 --- a/Example/example-app/Sources/SessionFeature/SessionView.swift +++ /dev/null @@ -1,81 +0,0 @@ -import ComposableArchitecture -import ComposablePresentation -import ErrorFeature -import SwiftUI -import XXClient - -public struct SessionView: View { - public init(store: Store<SessionState, SessionAction>) { - self.store = store - } - - let store: Store<SessionState, SessionAction> - - struct ViewState: Equatable { - let networkFollowerStatus: NetworkFollowerStatus? - let isNetworkHealthy: Bool? - - init(state: SessionState) { - networkFollowerStatus = state.networkFollowerStatus - isNetworkHealthy = state.isNetworkHealthy - } - } - - public var body: some View { - WithViewStore(store.scope(state: ViewState.init)) { viewStore in - Form { - Section { - NetworkFollowerStatusView(status: viewStore.networkFollowerStatus) - - Button { - viewStore.send(.runNetworkFollower(true)) - } label: { - Text("Start") - } - .disabled(viewStore.networkFollowerStatus != .stopped) - - Button { - viewStore.send(.runNetworkFollower(false)) - } label: { - Text("Stop") - } - .disabled(viewStore.networkFollowerStatus != .running) - } header: { - Text("Network follower") - } - - Section { - NetworkHealthStatusView(status: viewStore.isNetworkHealthy) - } header: { - Text("Network health") - } - } - .navigationTitle("Session") - .task { - viewStore.send(.viewDidLoad) - } - .sheet( - store.scope( - state: \.error, - action: SessionAction.error - ), - onDismiss: { - viewStore.send(.didDismissError) - }, - content: ErrorView.init(store:) - ) - } - } -} - -#if DEBUG -public struct SessionView_Previews: PreviewProvider { - public static var previews: some View { - SessionView(store: .init( - initialState: .init(id: UUID()), - reducer: .empty, - environment: () - )) - } -} -#endif diff --git a/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift b/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift deleted file mode 100644 index d5cd1cef41315d31db675122547d3f1f55d83525..0000000000000000000000000000000000000000 --- a/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift +++ /dev/null @@ -1,51 +0,0 @@ -import Combine -import ComposableArchitecture -import LandingFeature -import SessionFeature -import XCTest -@testable import AppFeature - -final class AppFeatureTests: XCTestCase { - func testViewDidLoad() throws { - let newId = UUID() - let hasCMix = PassthroughSubject<Bool, Never>() - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: AppState(), - reducer: appReducer, - environment: .unimplemented - ) - - store.environment.makeId = { newId } - store.environment.hasCMix = { hasCMix.eraseToAnyPublisher() } - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.viewDidLoad) - - hasCMix.send(false) - mainScheduler.advance() - - store.receive(.cMixDidChange(hasCMix: false)) - - hasCMix.send(true) - mainScheduler.advance() - - store.receive(.cMixDidChange(hasCMix: true)) { - $0.scene = .session(SessionState(id: newId)) - } - - hasCMix.send(true) - mainScheduler.advance() - - hasCMix.send(false) - mainScheduler.advance() - - store.receive(.cMixDidChange(hasCMix: false)) { - $0.scene = .landing(LandingState(id: newId)) - } - - hasCMix.send(completion: .finished) - mainScheduler.advance() - } -} diff --git a/Example/example-app/Tests/ErrorFeatureTests/ErrorFeatureTests.swift b/Example/example-app/Tests/ErrorFeatureTests/ErrorFeatureTests.swift deleted file mode 100644 index 50dabc308273fc6c77beafab3ab8733397b462bc..0000000000000000000000000000000000000000 --- a/Example/example-app/Tests/ErrorFeatureTests/ErrorFeatureTests.swift +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 9018641dbf895d99919c8234600d304a65d8f0ce..0000000000000000000000000000000000000000 --- a/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift +++ /dev/null @@ -1,182 +0,0 @@ -import ComposableArchitecture -import ErrorFeature -import XCTest -@testable import LandingFeature - -final class LandingFeatureTests: XCTestCase { - func testViewDidLoad() throws { - let store = TestStore( - initialState: LandingState(id: UUID()), - reducer: landingReducer, - environment: .unimplemented - ) - - store.environment.cMixManager.hasStorage.run = { true } - - store.send(.viewDidLoad) { - $0.hasStoredCMix = true - } - } - - func testCreateCMix() { - var hasStoredCMix = false - var didSetCMix = false - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: LandingState(id: UUID()), - reducer: landingReducer, - environment: .unimplemented - ) - - store.environment.cMixManager.hasStorage.run = { hasStoredCMix } - store.environment.cMixManager.create.run = { .unimplemented } - store.environment.setCMix = { _ in didSetCMix = true } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.makeCMix) { - $0.isMakingCMix = true - } - - bgScheduler.advance() - - XCTAssertTrue(didSetCMix) - - hasStoredCMix = true - mainScheduler.advance() - - store.receive(.didMakeCMix) { - $0.isMakingCMix = false - $0.hasStoredCMix = true - } - } - - func testLoadStoredCMix() { - var didSetCMix = false - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: LandingState(id: UUID()), - reducer: landingReducer, - environment: .unimplemented - ) - - store.environment.cMixManager.hasStorage.run = { true } - store.environment.cMixManager.load.run = { .unimplemented } - store.environment.setCMix = { _ in didSetCMix = true } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.makeCMix) { - $0.isMakingCMix = true - } - - bgScheduler.advance() - - XCTAssertTrue(didSetCMix) - - mainScheduler.advance() - - store.receive(.didMakeCMix) { - $0.isMakingCMix = false - $0.hasStoredCMix = true - } - } - - func testMakeCMixFailure() { - let error = NSError(domain: "test", code: 1234) - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: LandingState(id: UUID()), - reducer: landingReducer, - environment: .unimplemented - ) - - store.environment.cMixManager.hasStorage.run = { false } - store.environment.cMixManager.create.run = { throw error } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.makeCMix) { - $0.isMakingCMix = true - } - - bgScheduler.advance() - mainScheduler.advance() - - store.receive(.didFailMakingCMix(error)) { - $0.isMakingCMix = false - $0.hasStoredCMix = false - $0.error = ErrorState(error: error) - } - } - - func testRemoveStoredCMix() { - var hasStoredCMix = true - var didRemoveCMix = false - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: LandingState(id: UUID()), - reducer: landingReducer, - environment: .unimplemented - ) - - store.environment.cMixManager.hasStorage.run = { hasStoredCMix } - store.environment.cMixManager.remove.run = { didRemoveCMix = true } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.removeStoredCMix) { - $0.isRemovingCMix = true - } - - bgScheduler.advance() - - XCTAssertTrue(didRemoveCMix) - - hasStoredCMix = false - mainScheduler.advance() - - store.receive(.didRemoveStoredCMix) { - $0.isRemovingCMix = false - $0.hasStoredCMix = false - } - } - - func testRemoveStoredCMixFailure() { - let error = NSError(domain: "test", code: 1234) - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: LandingState(id: UUID()), - reducer: landingReducer, - environment: .unimplemented - ) - - store.environment.cMixManager.hasStorage.run = { true } - store.environment.cMixManager.remove.run = { throw error } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.removeStoredCMix) { - $0.isRemovingCMix = true - } - - bgScheduler.advance() - mainScheduler.advance() - - store.receive(.didFailRemovingStoredCMix(error)) { - $0.isRemovingCMix = false - $0.hasStoredCMix = true - $0.error = ErrorState(error: error) - } - } -} diff --git a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift deleted file mode 100644 index 33261ec67e11aa5494103216ccc5cdfd6f135eb6..0000000000000000000000000000000000000000 --- a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift +++ /dev/null @@ -1,146 +0,0 @@ -import ComposableArchitecture -import ErrorFeature -import XCTest -import XXClient -@testable import SessionFeature - -final class SessionFeatureTests: XCTestCase { - func testViewDidLoad() { - var networkFollowerStatus: NetworkFollowerStatus! - var didStartMonitoringNetworkHealth = 0 - var didStopMonitoringNetworkHealth = 0 - var networkHealthCallback: HealthCallback! - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: SessionState(id: UUID()), - reducer: sessionReducer, - environment: .unimplemented - ) - - store.environment.getCMix = { - var cMix = CMix.unimplemented - cMix.networkFollowerStatus.run = { networkFollowerStatus } - cMix.addHealthCallback.run = { callback in - networkHealthCallback = callback - didStartMonitoringNetworkHealth += 1 - return Cancellable { - didStopMonitoringNetworkHealth += 1 - } - } - return cMix - } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.viewDidLoad) - - store.receive(.updateNetworkFollowerStatus) - store.receive(.monitorNetworkHealth(true)) - - networkFollowerStatus = .stopped - bgScheduler.advance() - mainScheduler.advance() - - store.receive(.didUpdateNetworkFollowerStatus(.stopped)) { - $0.networkFollowerStatus = .stopped - } - - XCTAssertEqual(didStartMonitoringNetworkHealth, 1) - XCTAssertEqual(didStopMonitoringNetworkHealth, 0) - - networkHealthCallback.handle(true) - bgScheduler.advance() - mainScheduler.advance() - - store.receive(.didUpdateNetworkHealth(true)) { - $0.isNetworkHealthy = true - } - - store.send(.monitorNetworkHealth(false)) - - bgScheduler.advance() - - XCTAssertEqual(didStartMonitoringNetworkHealth, 1) - XCTAssertEqual(didStopMonitoringNetworkHealth, 1) - } - - func testStartStopNetworkFollower() { - var networkFollowerStatus: NetworkFollowerStatus! - var didStartNetworkFollowerWithTimeout = [Int]() - var didStopNetworkFollower = 0 - var networkFollowerStartError: NSError? - let bgScheduler = DispatchQueue.test - let mainScheduler = DispatchQueue.test - - let store = TestStore( - initialState: SessionState(id: UUID()), - reducer: sessionReducer, - environment: .unimplemented - ) - - store.environment.getCMix = { - var cMix = CMix.unimplemented - cMix.networkFollowerStatus.run = { networkFollowerStatus } - cMix.startNetworkFollower.run = { - didStartNetworkFollowerWithTimeout.append($0) - if let error = networkFollowerStartError { - throw error - } - } - cMix.stopNetworkFollower.run = { - didStopNetworkFollower += 1 - } - return cMix - } - store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() - store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - - store.send(.runNetworkFollower(true)) - - networkFollowerStatus = .running - bgScheduler.advance() - mainScheduler.advance() - - XCTAssertEqual(didStartNetworkFollowerWithTimeout, [30_000]) - XCTAssertEqual(didStopNetworkFollower, 0) - - store.receive(.didUpdateNetworkFollowerStatus(.running)) { - $0.networkFollowerStatus = .running - } - - store.send(.runNetworkFollower(false)) - - networkFollowerStatus = .stopped - bgScheduler.advance() - mainScheduler.advance() - - XCTAssertEqual(didStartNetworkFollowerWithTimeout, [30_000]) - XCTAssertEqual(didStopNetworkFollower, 1) - - store.receive(.didUpdateNetworkFollowerStatus(.stopped)) { - $0.networkFollowerStatus = .stopped - } - - store.send(.runNetworkFollower(true)) - - networkFollowerStartError = NSError(domain: "test", code: 1234) - networkFollowerStatus = .stopped - bgScheduler.advance() - mainScheduler.advance() - - XCTAssertEqual(didStartNetworkFollowerWithTimeout, [30_000, 30_000]) - XCTAssertEqual(didStopNetworkFollower, 1) - - store.receive(.networkFollowerDidFail(networkFollowerStartError!)) { - $0.error = ErrorState(error: networkFollowerStartError!) - } - - store.receive(.didUpdateNetworkFollowerStatus(.stopped)) - - store.send(.didDismissError) { - $0.error = nil - } - } -} diff --git a/Example/Package.swift b/Examples/Package.swift similarity index 100% rename from Example/Package.swift rename to Examples/Package.swift diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/ErrorFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/AppCore.xcscheme similarity index 84% rename from Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/ErrorFeature.xcscheme rename to Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/AppCore.xcscheme index 95ae136eb336e7590ebc8e1d791912f4c0393a84..2bd4e0801d9c8785542e12f5dedb6e8e66fa27aa 100644 --- a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/ErrorFeature.xcscheme +++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/AppCore.xcscheme @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "ErrorFeature" - BuildableName = "ErrorFeature" - BlueprintName = "ErrorFeature" + BlueprintIdentifier = "AppCore" + BuildableName = "AppCore" + BlueprintName = "AppCore" ReferencedContainer = "container:"> </BuildableReference> </BuildActionEntry> @@ -33,9 +33,9 @@ skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "ErrorFeatureTests" - BuildableName = "ErrorFeatureTests" - BlueprintName = "ErrorFeatureTests" + BlueprintIdentifier = "AppCoreTests" + BuildableName = "AppCoreTests" + BlueprintName = "AppCoreTests" ReferencedContainer = "container:"> </BuildableReference> </TestableReference> @@ -61,9 +61,9 @@ <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "ErrorFeature" - BuildableName = "ErrorFeature" - BlueprintName = "ErrorFeature" + BlueprintIdentifier = "AppCore" + BuildableName = "AppCore" + BlueprintName = "AppCore" ReferencedContainer = "container:"> </BuildableReference> </MacroExpansion> diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme similarity index 100% rename from Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme rename to Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/AppFeature.xcscheme diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/LandingFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/HomeFeature.xcscheme similarity index 83% rename from Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/LandingFeature.xcscheme rename to Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/HomeFeature.xcscheme index 7d203fde1b21e22854fa66dfc0b2f4253280f862..44bed4e71971f74ec4d316580602b1e965d0a3a4 100644 --- a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/LandingFeature.xcscheme +++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/HomeFeature.xcscheme @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "LandingFeature" - BuildableName = "LandingFeature" - BlueprintName = "LandingFeature" + BlueprintIdentifier = "HomeFeature" + BuildableName = "HomeFeature" + BlueprintName = "HomeFeature" ReferencedContainer = "container:"> </BuildableReference> </BuildActionEntry> @@ -33,9 +33,9 @@ skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "LandingFeatureTests" - BuildableName = "LandingFeatureTests" - BlueprintName = "LandingFeatureTests" + BlueprintIdentifier = "HomeFeatureTests" + BuildableName = "HomeFeatureTests" + BlueprintName = "HomeFeatureTests" ReferencedContainer = "container:"> </BuildableReference> </TestableReference> @@ -61,9 +61,9 @@ <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "LandingFeature" - BuildableName = "LandingFeature" - BlueprintName = "LandingFeature" + BlueprintIdentifier = "HomeFeature" + BuildableName = "HomeFeature" + BlueprintName = "HomeFeature" ReferencedContainer = "container:"> </BuildableReference> </MacroExpansion> diff --git a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/SessionFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/LaunchFeature.xcscheme similarity index 83% rename from Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/SessionFeature.xcscheme rename to Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/LaunchFeature.xcscheme index 9361c5551eb399da5f095ac23edd410d6b284503..0b5b716cb194f784d36c6cfe45d705107c4e6549 100644 --- a/Example/example-app/.swiftpm/xcode/xcshareddata/xcschemes/SessionFeature.xcscheme +++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/LaunchFeature.xcscheme @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "SessionFeature" - BuildableName = "SessionFeature" - BlueprintName = "SessionFeature" + BlueprintIdentifier = "LaunchFeature" + BuildableName = "LaunchFeature" + BlueprintName = "LaunchFeature" ReferencedContainer = "container:"> </BuildableReference> </BuildActionEntry> @@ -33,9 +33,9 @@ skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "SessionFeatureTests" - BuildableName = "SessionFeatureTests" - BlueprintName = "SessionFeatureTests" + BlueprintIdentifier = "LaunchFeatureTests" + BuildableName = "LaunchFeatureTests" + BlueprintName = "LaunchFeatureTests" ReferencedContainer = "container:"> </BuildableReference> </TestableReference> @@ -61,9 +61,9 @@ <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "SessionFeature" - BuildableName = "SessionFeature" - BlueprintName = "SessionFeature" + BlueprintIdentifier = "LaunchFeature" + BuildableName = "LaunchFeature" + BlueprintName = "LaunchFeature" ReferencedContainer = "container:"> </BuildableReference> </MacroExpansion> diff --git a/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/RegisterFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/RegisterFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..69611b8706471b3f246fe1747b7bb63953c22ab4 --- /dev/null +++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/RegisterFeature.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 = "RegisterFeature" + BuildableName = "RegisterFeature" + BlueprintName = "RegisterFeature" + 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 = "RegisterFeatureTests" + BuildableName = "RegisterFeatureTests" + BlueprintName = "RegisterFeatureTests" + 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 = "RegisterFeature" + BuildableName = "RegisterFeature" + BlueprintName = "RegisterFeature" + 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/ExampleAppIcon.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/RestoreFeature.xcscheme similarity index 70% rename from Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/ExampleAppIcon.xcscheme rename to Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/RestoreFeature.xcscheme index 577a588463331de08076d97e67d30ddfa7eb6e70..47afca01630455cc3749e2016b21a0b879e0b5e3 100644 --- a/Example/example-app-icon/.swiftpm/xcode/xcshareddata/xcschemes/ExampleAppIcon.xcscheme +++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/RestoreFeature.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "1330" + LastUpgradeVersion = "1340" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "ExampleAppIcon" - BuildableName = "ExampleAppIcon" - BlueprintName = "ExampleAppIcon" + BlueprintIdentifier = "RestoreFeature" + BuildableName = "RestoreFeature" + BlueprintName = "RestoreFeature" ReferencedContainer = "container:"> </BuildableReference> </BuildActionEntry> @@ -26,8 +26,19 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "RestoreFeatureTests" + BuildableName = "RestoreFeatureTests" + BlueprintName = "RestoreFeatureTests" + ReferencedContainer = "container:"> + </BuildableReference> + </TestableReference> </Testables> </TestAction> <LaunchAction @@ -50,9 +61,9 @@ <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "ExampleAppIcon" - BuildableName = "ExampleAppIcon" - BlueprintName = "ExampleAppIcon" + BlueprintIdentifier = "RestoreFeature" + BuildableName = "RestoreFeature" + BlueprintName = "RestoreFeature" ReferencedContainer = "container:"> </BuildableReference> </MacroExpansion> diff --git a/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/WelcomeFeature.xcscheme b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/WelcomeFeature.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..60bd803bf3175a785b693461f4154e8b89467a2a --- /dev/null +++ b/Examples/xx-messenger/.swiftpm/xcode/xcshareddata/xcschemes/WelcomeFeature.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 = "WelcomeFeature" + BuildableName = "WelcomeFeature" + BlueprintName = "WelcomeFeature" + 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 = "WelcomeFeatureTests" + BuildableName = "WelcomeFeatureTests" + BlueprintName = "WelcomeFeatureTests" + 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 = "WelcomeFeature" + BuildableName = "WelcomeFeature" + BlueprintName = "WelcomeFeature" + ReferencedContainer = "container:"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Examples/xx-messenger/LICENSE b/Examples/xx-messenger/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..8551215fa5b651bb93a247cde5ab89d4b8f5f7e6 --- /dev/null +++ b/Examples/xx-messenger/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/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift new file mode 100644 index 0000000000000000000000000000000000000000..5eb7bede849b86baf218f969361cf911bd4a5907 --- /dev/null +++ b/Examples/xx-messenger/Package.swift @@ -0,0 +1,171 @@ +// swift-tools-version: 5.6 +import PackageDescription + +let swiftSettings: [SwiftSetting] = [ + .unsafeFlags( + [ + // "-Xfrontend", "-warn-concurrency", + "-Xfrontend", "-debug-time-function-bodies", + "-Xfrontend", "-debug-time-expression-type-checking", + ], + .when(configuration: .debug) + ), +] + +let package = Package( + name: "xx-messenger", + platforms: [ + .iOS(.v15), + ], + products: [ + .library(name: "AppCore", targets: ["AppCore"]), + .library(name: "AppFeature", targets: ["AppFeature"]), + .library(name: "HomeFeature", targets: ["HomeFeature"]), + .library(name: "LaunchFeature", targets: ["LaunchFeature"]), + .library(name: "RegisterFeature", targets: ["RegisterFeature"]), + .library(name: "RestoreFeature", targets: ["RestoreFeature"]), + .library(name: "WelcomeFeature", targets: ["WelcomeFeature"]), + ], + dependencies: [ + .package( + path: "../../" + ), + .package( + url: "https://github.com/pointfreeco/swift-composable-architecture.git", + .upToNextMajor(from: "0.39.0") + ), + .package( + url: "https://git.xx.network/elixxir/client-ios-db.git", + .upToNextMajor(from: "1.1.0") + ), + .package( + url: "https://github.com/darrarski/swift-composable-presentation.git", + .upToNextMajor(from: "0.5.2") + ), + .package( + url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git", + .upToNextMajor(from: "0.4.0") + ), + ], + targets: [ + .target( + name: "AppCore", + dependencies: [ + .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), + .product(name: "XXDatabase", package: "client-ios-db"), + .product(name: "XXModels", package: "client-ios-db"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "AppCoreTests", + dependencies: [ + .target(name: "AppCore") + ], + swiftSettings: swiftSettings + ), + .target( + name: "AppFeature", + dependencies: [ + .target(name: "AppCore"), + .target(name: "HomeFeature"), + .target(name: "LaunchFeature"), + .target(name: "RegisterFeature"), + .target(name: "RestoreFeature"), + .target(name: "WelcomeFeature"), + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "ComposablePresentation", package: "swift-composable-presentation"), + .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + .product(name: "XXModels", package: "client-ios-db"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "AppFeatureTests", + dependencies: [ + .target(name: "AppFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "HomeFeature", + dependencies: [ + .target(name: "AppCore"), + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "HomeFeatureTests", + dependencies: [ + .target(name: "HomeFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "LaunchFeature", + dependencies: [ + .target(name: "AppCore"), + .target(name: "RegisterFeature"), + .target(name: "RestoreFeature"), + .target(name: "WelcomeFeature"), + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "ComposablePresentation", package: "swift-composable-presentation"), + .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "LaunchFeatureTests", + dependencies: [ + .target(name: "LaunchFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "RegisterFeature", + dependencies: [ + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "RegisterFeatureTests", + dependencies: [ + .target(name: "RegisterFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "RestoreFeature", + dependencies: [ + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "RestoreFeatureTests", + dependencies: [ + .target(name: "RestoreFeature"), + ], + swiftSettings: swiftSettings + ), + .target( + name: "WelcomeFeature", + dependencies: [ + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), + ], + swiftSettings: swiftSettings + ), + .testTarget( + name: "WelcomeFeatureTests", + dependencies: [ + .target(name: "WelcomeFeature"), + ], + swiftSettings: swiftSettings + ), + ] +) diff --git a/Examples/xx-messenger/Project/Package.swift b/Examples/xx-messenger/Project/Package.swift new file mode 100644 index 0000000000000000000000000000000000000000..d0a31fe81dd861a3d33bf4eaeb4f59fb7c9f4947 --- /dev/null +++ b/Examples/xx-messenger/Project/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.xcodeproj/project.pbxproj b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.pbxproj similarity index 66% rename from Example/Example.xcodeproj/project.pbxproj rename to Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.pbxproj index 6147b07eb28b1d32c4af098309f1427b4a2b2884..48ab32e161ebe4a729ed9473c83f522eb376a019 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.pbxproj @@ -7,96 +7,90 @@ objects = { /* Begin PBXBuildFile section */ - 312C371E28475E63003E9E39 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 312C371D28475E63003E9E39 /* Assets.xcassets */; }; - 316D95EE28477B0E008475F9 /* AppFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 316D95ED28477B0E008475F9 /* AppFeature */; }; + 313CFFFA28B632E40050B10D /* AppFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 313CFFF928B632E40050B10D /* AppFeature */; }; + 31964B9228A6D37200BBDC17 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 31964B9128A6D37200BBDC17 /* Assets.xcassets */; }; /* 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>"; }; + 31964B8A28A6D37100BBDC17 /* XXMessenger.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XXMessenger.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 31964B9128A6D37200BBDC17 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 312C371328475E62003E9E39 /* Frameworks */ = { + 31964B8728A6D37100BBDC17 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 316D95EE28477B0E008475F9 /* AppFeature in Frameworks */, + 313CFFFA28B632E40050B10D /* AppFeature in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 312C370D28475E62003E9E39 = { + 31964B8128A6D37100BBDC17 = { isa = PBXGroup; children = ( - 312C3727284761C7003E9E39 /* ExampleApp (iOS) */, - 316D95EB28477ACA008475F9 /* example-app */, - 31B3342F2847669900865D82 /* example-app-icon */, - 312C371728475E62003E9E39 /* Products */, + 31964B8C28A6D37100BBDC17 /* XXMessenger */, + 31964B8B28A6D37100BBDC17 /* Products */, ); sourceTree = "<group>"; }; - 312C371728475E62003E9E39 /* Products */ = { + 31964B8B28A6D37100BBDC17 /* Products */ = { isa = PBXGroup; children = ( - 312C371628475E62003E9E39 /* dApps Example.app */, + 31964B8A28A6D37100BBDC17 /* XXMessenger.app */, ); name = Products; sourceTree = "<group>"; }; - 312C3727284761C7003E9E39 /* ExampleApp (iOS) */ = { + 31964B8C28A6D37100BBDC17 /* XXMessenger */ = { isa = PBXGroup; children = ( - 312C371D28475E63003E9E39 /* Assets.xcassets */, - 312C3728284761DC003E9E39 /* Info.plist */, + 31964B9128A6D37200BBDC17 /* Assets.xcassets */, ); - path = "ExampleApp (iOS)"; + path = XXMessenger; sourceTree = "<group>"; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 312C371528475E62003E9E39 /* ExampleApp (iOS) */ = { + 31964B8928A6D37100BBDC17 /* XXMessenger */ = { isa = PBXNativeTarget; - buildConfigurationList = 312C372428475E63003E9E39 /* Build configuration list for PBXNativeTarget "ExampleApp (iOS)" */; + buildConfigurationList = 31964B9828A6D37200BBDC17 /* Build configuration list for PBXNativeTarget "XXMessenger" */; buildPhases = ( - 312C371228475E62003E9E39 /* Sources */, - 312C371328475E62003E9E39 /* Frameworks */, - 312C371428475E62003E9E39 /* Resources */, + 31964B8628A6D37100BBDC17 /* Sources */, + 31964B8728A6D37100BBDC17 /* Frameworks */, + 31964B8828A6D37100BBDC17 /* Resources */, ); buildRules = ( ); dependencies = ( ); - name = "ExampleApp (iOS)"; + name = XXMessenger; packageProductDependencies = ( - 316D95ED28477B0E008475F9 /* AppFeature */, + 313CFFF928B632E40050B10D /* AppFeature */, ); - productName = ExampleApp; - productReference = 312C371628475E62003E9E39 /* dApps Example.app */; + productName = XXMessenger; + productReference = 31964B8A28A6D37100BBDC17 /* XXMessenger.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 312C370E28475E62003E9E39 /* Project object */ = { + 31964B8228A6D37100BBDC17 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1340; TargetAttributes = { - 312C371528475E62003E9E39 = { - CreatedOnToolsVersion = 13.4; + 31964B8928A6D37100BBDC17 = { + CreatedOnToolsVersion = 13.4.1; }; }; }; - buildConfigurationList = 312C371128475E62003E9E39 /* Build configuration list for PBXProject "Example" */; + buildConfigurationList = 31964B8528A6D37100BBDC17 /* Build configuration list for PBXProject "XXMessenger" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -104,29 +98,29 @@ en, Base, ); - mainGroup = 312C370D28475E62003E9E39; - productRefGroup = 312C371728475E62003E9E39 /* Products */; + mainGroup = 31964B8128A6D37100BBDC17; + productRefGroup = 31964B8B28A6D37100BBDC17 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 312C371528475E62003E9E39 /* ExampleApp (iOS) */, + 31964B8928A6D37100BBDC17 /* XXMessenger */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 312C371428475E62003E9E39 /* Resources */ = { + 31964B8828A6D37100BBDC17 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 312C371E28475E63003E9E39 /* Assets.xcassets in Resources */, + 31964B9228A6D37200BBDC17 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 312C371228475E62003E9E39 /* Sources */ = { + 31964B8628A6D37100BBDC17 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -136,7 +130,7 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - 312C372228475E63003E9E39 /* Debug */ = { + 31964B9628A6D37200BBDC17 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -169,6 +163,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -186,17 +181,19 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.3; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MARKETING_VERSION = 1.0.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; - 312C372328475E63003E9E39 /* Release */ = { + 31964B9728A6D37200BBDC17 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -229,6 +226,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -240,93 +238,89 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.3; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MARKETING_VERSION = 1.0.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; - 312C372528475E63003E9E39 /* Debug */ = { + 31964B9928A6D37200BBDC17 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = P38XW29864; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "ExampleApp (iOS)/Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = "dApps Example"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; 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"; + PRODUCT_BUNDLE_IDENTIFIER = pl.darrarski.XXMessenger; + PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; - 312C372628475E63003E9E39 /* Release */ = { + 31964B9A28A6D37200BBDC17 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = P38XW29864; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "ExampleApp (iOS)/Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = "dApps Example"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; 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"; + PRODUCT_BUNDLE_IDENTIFIER = pl.darrarski.XXMessenger; + PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 312C371128475E62003E9E39 /* Build configuration list for PBXProject "Example" */ = { + 31964B8528A6D37100BBDC17 /* Build configuration list for PBXProject "XXMessenger" */ = { isa = XCConfigurationList; buildConfigurations = ( - 312C372228475E63003E9E39 /* Debug */, - 312C372328475E63003E9E39 /* Release */, + 31964B9628A6D37200BBDC17 /* Debug */, + 31964B9728A6D37200BBDC17 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 312C372428475E63003E9E39 /* Build configuration list for PBXNativeTarget "ExampleApp (iOS)" */ = { + 31964B9828A6D37200BBDC17 /* Build configuration list for PBXNativeTarget "XXMessenger" */ = { isa = XCConfigurationList; buildConfigurations = ( - 312C372528475E63003E9E39 /* Debug */, - 312C372628475E63003E9E39 /* Release */, + 31964B9928A6D37200BBDC17 /* Debug */, + 31964B9A28A6D37200BBDC17 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -334,11 +328,11 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - 316D95ED28477B0E008475F9 /* AppFeature */ = { + 313CFFF928B632E40050B10D /* AppFeature */ = { isa = XCSwiftPackageProductDependency; productName = AppFeature; }; /* End XCSwiftPackageProductDependency section */ }; - rootObject = 312C370E28475E62003E9E39 /* Project object */; + rootObject = 31964B8228A6D37100BBDC17 /* Project object */; } diff --git a/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ElixxirDAppsSDK.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from ElixxirDAppsSDK.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/xx-messenger/Project/XXMessenger.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/ExampleApp (iOS).xcscheme b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme similarity index 55% rename from Example/Example.xcodeproj/xcshareddata/xcschemes/ExampleApp (iOS).xcscheme rename to Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme index 6ae359a987e276d0151fecdeff0fc8854e31bea1..9e83a72f3d20c49c9a5fec35e35ca64500fe5ebe 100644 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/ExampleApp (iOS).xcscheme +++ b/Examples/xx-messenger/Project/XXMessenger.xcodeproj/xcshareddata/xcschemes/XXMessenger.xcscheme @@ -14,10 +14,10 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "312C371528475E62003E9E39" - BuildableName = "dApps Example.app" - BlueprintName = "ExampleApp (iOS)" - ReferencedContainer = "container:Example.xcodeproj"> + BlueprintIdentifier = "31964B8928A6D37100BBDC17" + BuildableName = "XXMessenger.app" + BlueprintName = "XXMessenger" + ReferencedContainer = "container:XXMessenger.xcodeproj"> </BuildableReference> </BuildActionEntry> </BuildActionEntries> @@ -29,6 +29,16 @@ shouldUseLaunchSchemeArgsEnv = "YES" codeCoverageEnabled = "YES"> <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "AppCoreTests" + BuildableName = "AppCoreTests" + BlueprintName = "AppCoreTests" + ReferencedContainer = "container:.."> + </BuildableReference> + </TestableReference> <TestableReference skipped = "NO"> <BuildableReference @@ -36,37 +46,57 @@ BlueprintIdentifier = "AppFeatureTests" BuildableName = "AppFeatureTests" BlueprintName = "AppFeatureTests" - ReferencedContainer = "container:example-app"> + ReferencedContainer = "container:.."> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "HomeFeatureTests" + BuildableName = "HomeFeatureTests" + BlueprintName = "HomeFeatureTests" + ReferencedContainer = "container:.."> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "LaunchFeatureTests" + BuildableName = "LaunchFeatureTests" + BlueprintName = "LaunchFeatureTests" + ReferencedContainer = "container:.."> </BuildableReference> </TestableReference> <TestableReference skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "ErrorFeatureTests" - BuildableName = "ErrorFeatureTests" - BlueprintName = "ErrorFeatureTests" - ReferencedContainer = "container:example-app"> + BlueprintIdentifier = "RegisterFeatureTests" + BuildableName = "RegisterFeatureTests" + BlueprintName = "RegisterFeatureTests" + ReferencedContainer = "container:.."> </BuildableReference> </TestableReference> <TestableReference skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "LandingFeatureTests" - BuildableName = "LandingFeatureTests" - BlueprintName = "LandingFeatureTests" - ReferencedContainer = "container:example-app"> + BlueprintIdentifier = "RestoreFeatureTests" + BuildableName = "RestoreFeatureTests" + BlueprintName = "RestoreFeatureTests" + ReferencedContainer = "container:.."> </BuildableReference> </TestableReference> <TestableReference skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "SessionFeatureTests" - BuildableName = "SessionFeatureTests" - BlueprintName = "SessionFeatureTests" - ReferencedContainer = "container:example-app"> + BlueprintIdentifier = "WelcomeFeatureTests" + BuildableName = "WelcomeFeatureTests" + BlueprintName = "WelcomeFeatureTests" + ReferencedContainer = "container:.."> </BuildableReference> </TestableReference> </Testables> @@ -85,10 +115,10 @@ runnableDebuggingMode = "0"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "312C371528475E62003E9E39" - BuildableName = "dApps Example.app" - BlueprintName = "ExampleApp (iOS)" - ReferencedContainer = "container:Example.xcodeproj"> + BlueprintIdentifier = "31964B8928A6D37100BBDC17" + BuildableName = "XXMessenger.app" + BlueprintName = "XXMessenger" + ReferencedContainer = "container:XXMessenger.xcodeproj"> </BuildableReference> </BuildableProductRunnable> </LaunchAction> @@ -102,10 +132,10 @@ runnableDebuggingMode = "0"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "312C371528475E62003E9E39" - BuildableName = "dApps Example.app" - BlueprintName = "ExampleApp (iOS)" - ReferencedContainer = "container:Example.xcodeproj"> + BlueprintIdentifier = "31964B8928A6D37100BBDC17" + BuildableName = "XXMessenger.app" + BlueprintName = "XXMessenger" + ReferencedContainer = "container:XXMessenger.xcodeproj"> </BuildableReference> </BuildableProductRunnable> </ProfileAction> diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AccentColor.colorset/Contents.json rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iOS App Store 1024pt 1x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 1x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad App 76pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 1x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Notification 20pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Pro (12.9-inch) App 83.5pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 1x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Settings 29pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 1x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPad Spotlight 40pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone App 60pt 3x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Notification 20pt 3x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Settings 29pt 3x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 2x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/AppIcon.appiconset/iPhone Spotlight 40pt 3x.png diff --git a/Example/ExampleApp (iOS)/Assets.xcassets/Contents.json b/Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/Contents.json similarity index 100% rename from Example/ExampleApp (iOS)/Assets.xcassets/Contents.json rename to Examples/xx-messenger/Project/XXMessenger/Assets.xcassets/Contents.json diff --git a/Examples/xx-messenger/README.md b/Examples/xx-messenger/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6863cdab629da2701b2a732ebaf04fce1455ccfe --- /dev/null +++ b/Examples/xx-messenger/README.md @@ -0,0 +1,32 @@ +# xx-messenger + +Example iOS messenger application built with **Elixxir dApps Swift SDK**. + + + + +## 🛠Development + +Open `XXMessenger.xcworkspace` in Xcode (≥13.4). + +### Project structure + +``` +XXMessenger [Xcode Workspace] + ├─ xx-messenger [Swift Package] + | ├─ AppFeature [Library] + | └─ ... + └─ XXMessenger [Xcode Project] + └─ XXMessenger (iOS) [iOS App Target] +``` + +### Build schemes + +- Use `XXMessenger` scheme to build, test, and run the app. +- Use other schemes (like `AppFeature`) for building and testing individual libraries in isolation. + +## 📄 License + +Copyright © 2022 xx network SEZC + +[License](LICENSE) diff --git a/Examples/xx-messenger/Sources/AppCore/DBManager/DBManager.swift b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManager.swift new file mode 100644 index 0000000000000000000000000000000000000000..178e1078e56aaddaf6ef8170834ea830b944aac5 --- /dev/null +++ b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManager.swift @@ -0,0 +1,31 @@ +import XXModels + +public struct DBManager { + public var hasDB: DBManagerHasDB + public var makeDB: DBManagerMakeDB + public var getDB: DBManagerGetDB +} + +extension DBManager { + public static func live() -> DBManager { + class Container { + var db: Database? + } + + let container = Container() + + return DBManager( + hasDB: .init { container.db != nil }, + makeDB: .live(setDB: { container.db = $0 }), + getDB: .live(getDB: { container.db }) + ) + } +} + +extension DBManager { + public static let unimplemented = DBManager( + hasDB: .unimplemented, + makeDB: .unimplemented, + getDB: .unimplemented + ) +} diff --git a/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerGetDB.swift b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerGetDB.swift new file mode 100644 index 0000000000000000000000000000000000000000..7b5898b7f4b57fdb49dec6b978e3ef04489251b5 --- /dev/null +++ b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerGetDB.swift @@ -0,0 +1,33 @@ +import XCTestDynamicOverlay +import XXModels + +public struct DBManagerGetDB { + public enum Error: Swift.Error, Equatable { + case missingDB + } + + public var run: () throws -> Database + + public func callAsFunction() throws -> Database { + try run() + } +} + +extension DBManagerGetDB { + public static func live( + getDB: @escaping () -> Database? + ) -> DBManagerGetDB { + DBManagerGetDB { + guard let db = getDB() else { + throw Error.missingDB + } + return db + } + } +} + +extension DBManagerGetDB { + public static let unimplemented = DBManagerGetDB( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerHasDB.swift b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerHasDB.swift new file mode 100644 index 0000000000000000000000000000000000000000..12fb1bb34ca0882404bf765f640e61496cbaedd5 --- /dev/null +++ b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerHasDB.swift @@ -0,0 +1,19 @@ +import XCTestDynamicOverlay + +public struct DBManagerHasDB { + init(run: @escaping () -> Bool) { + self.run = run + } + + public var run: () -> Bool + + public func callAsFunction() -> Bool { + run() + } +} + +extension DBManagerHasDB { + public static let unimplemented = DBManagerHasDB( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerMakeDB.swift b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerMakeDB.swift new file mode 100644 index 0000000000000000000000000000000000000000..a3f018b1d617ad402a4c5f8914b07a2c622150ac --- /dev/null +++ b/Examples/xx-messenger/Sources/AppCore/DBManager/DBManagerMakeDB.swift @@ -0,0 +1,41 @@ +import Foundation +import XCTestDynamicOverlay +import XXDatabase +import XXModels + +public struct DBManagerMakeDB { + public var run: () throws -> Void + + public func callAsFunction() throws -> Void { + try run() + } +} + +extension DBManagerMakeDB { + public static func live( + setDB: @escaping (Database) -> Void + ) -> DBManagerMakeDB { + DBManagerMakeDB { + let dbDirectoryURL = FileManager.default + .urls(for: .applicationSupportDirectory, in: .userDomainMask) + .first! + .appendingPathComponent("database") + + try? FileManager.default + .createDirectory(at: dbDirectoryURL, withIntermediateDirectories: true) + + let dbFilePath = dbDirectoryURL + .appendingPathComponent("db") + .appendingPathExtension("sqlite") + .path + + setDB(try Database.onDisk(path: dbFilePath)) + } + } +} + +extension DBManagerMakeDB { + public static let unimplemented = DBManagerMakeDB( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Examples/xx-messenger/Sources/AppFeature/App.swift b/Examples/xx-messenger/Sources/AppFeature/App.swift new file mode 100644 index 0000000000000000000000000000000000000000..d6b0b722c4d610d823b5313aae797e3f72dc10a3 --- /dev/null +++ b/Examples/xx-messenger/Sources/AppFeature/App.swift @@ -0,0 +1,15 @@ +import ComposableArchitecture +import SwiftUI + +@main +struct App: SwiftUI.App { + var body: some Scene { + WindowGroup { + AppView(store: Store( + initialState: AppState(), + reducer: appReducer, + environment: .live() + )) + } + } +} diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift new file mode 100644 index 0000000000000000000000000000000000000000..6b1df10d25b421f5530cc0b8b77146acdee6023b --- /dev/null +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -0,0 +1,52 @@ +import AppCore +import Foundation +import HomeFeature +import LaunchFeature +import RegisterFeature +import RestoreFeature +import WelcomeFeature +import XXMessengerClient +import XXModels + +extension AppEnvironment { + static func live() -> AppEnvironment { + let dbManager = DBManager.live() + let messengerEnv = MessengerEnvironment.live() + let messenger = Messenger.live(messengerEnv) + let mainQueue = DispatchQueue.main.eraseToAnyScheduler() + let bgQueue = DispatchQueue.global(qos: .background).eraseToAnyScheduler() + + return AppEnvironment( + launch: { + LaunchEnvironment( + dbManager: dbManager, + messenger: messenger, + mainQueue: mainQueue, + bgQueue: bgQueue, + welcome: { + WelcomeEnvironment( + messenger: messenger, + mainQueue: mainQueue, + bgQueue: bgQueue + ) + }, + restore: { + RestoreEnvironment() + }, + register: { + RegisterEnvironment( + messenger: messenger, + mainQueue: mainQueue, + bgQueue: bgQueue + ) + } + ) + }, + home: { + HomeEnvironment( + messenger: messenger + ) + } + ) + } +} diff --git a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..18927c0bc21af48ba57317be8b38c7c80ca27a0b --- /dev/null +++ b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift @@ -0,0 +1,67 @@ +import ComposableArchitecture +import ComposablePresentation +import HomeFeature +import LaunchFeature + +struct AppState: Equatable { + enum Screen: Equatable { + case launch(LaunchState) + case home(HomeState) + } + + var screen: Screen = .launch(LaunchState()) +} + +extension AppState.Screen { + var asLaunch: LaunchState? { + get { (/AppState.Screen.launch).extract(from: self) } + set { if let newValue = newValue { self = .launch(newValue) } } + } + var asHome: HomeState? { + get { (/AppState.Screen.home).extract(from: self) } + set { if let newValue = newValue { self = .home(newValue) } } + } +} + +enum AppAction: Equatable { + case home(HomeAction) + case launch(LaunchAction) +} + +struct AppEnvironment { + var launch: () -> LaunchEnvironment + var home: () -> HomeEnvironment +} + +extension AppEnvironment { + static let unimplemented = AppEnvironment( + launch: { .unimplemented }, + home: { .unimplemented } + ) +} + +let appReducer = Reducer<AppState, AppAction, AppEnvironment> +{ state, action, env in + switch action { + case .launch(.finished): + state.screen = .home(HomeState()) + return .none + + case .launch(_), .home(_): + return .none + } +} +.presenting( + launchReducer, + state: .keyPath(\.screen.asLaunch), + id: .notNil(), + action: /AppAction.launch, + environment: { $0.launch() } +) +.presenting( + homeReducer, + state: .keyPath(\.screen.asHome), + id: .notNil(), + action: /AppAction.home, + environment: { $0.home() } +) diff --git a/Examples/xx-messenger/Sources/AppFeature/AppView.swift b/Examples/xx-messenger/Sources/AppFeature/AppView.swift new file mode 100644 index 0000000000000000000000000000000000000000..317560a3f749bd2b2eaa923318c7c4f4fc66fb1c --- /dev/null +++ b/Examples/xx-messenger/Sources/AppFeature/AppView.swift @@ -0,0 +1,64 @@ +import ComposableArchitecture +import SwiftUI +import HomeFeature +import LaunchFeature + +struct AppView: View { + let store: Store<AppState, AppAction> + + enum ViewState: Equatable { + case launch + case home + + init(_ state: AppState) { + switch state.screen { + case .launch(_): self = .launch + case .home(_): self = .home + } + } + } + + var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + ZStack { + SwitchStore(store.scope(state: \.screen)) { + CaseLet( + state: /AppState.Screen.launch, + action: AppAction.launch, + then: { store in + LaunchView(store: store) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.opacity) + } + ) + + CaseLet( + state: /AppState.Screen.home, + action: AppAction.home, + then: { store in + HomeView(store: store) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.asymmetric( + insertion: .move(edge: .trailing), + removal: .opacity + )) + } + ) + } + } + .animation(.default, value: viewStore.state) + } + } +} + +#if DEBUG +struct AppView_Previews: PreviewProvider { + static var previews: some View { + AppView(store: Store( + initialState: AppState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..4aad88dac7cb70cac5908b50bce45a6367b91ca8 --- /dev/null +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift @@ -0,0 +1,35 @@ +import ComposableArchitecture +import XXClient +import XXMessengerClient + +public struct HomeState: Equatable { + public init() {} +} + +public enum HomeAction: Equatable { + case start +} + +public struct HomeEnvironment { + public init( + messenger: Messenger + ) { + self.messenger = messenger + } + + public var messenger: Messenger +} + +extension HomeEnvironment { + public static let unimplemented = HomeEnvironment( + messenger: .unimplemented + ) +} + +public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> +{ state, action, env in + switch action { + case .start: + return .none + } +} diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift new file mode 100644 index 0000000000000000000000000000000000000000..0bda29bd8c2ea48b125337dd5a487e63334794f0 --- /dev/null +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift @@ -0,0 +1,39 @@ +import ComposableArchitecture +import SwiftUI + +public struct HomeView: View { + public init(store: Store<HomeState, HomeAction>) { + self.store = store + } + + let store: Store<HomeState, HomeAction> + + struct ViewState: Equatable { + init(state: HomeState) {} + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + NavigationView { + Form { + + } + .navigationTitle("Home") + } + .navigationViewStyle(.stack) + .task { viewStore.send(.start) } + } + } +} + +#if DEBUG +public struct HomeView_Previews: PreviewProvider { + public static var previews: some View { + HomeView(store: Store( + initialState: HomeState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Examples/xx-messenger/Sources/LaunchFeature/LaunchErrorView.swift b/Examples/xx-messenger/Sources/LaunchFeature/LaunchErrorView.swift new file mode 100644 index 0000000000000000000000000000000000000000..893492c0b9670af7389e341713527346eb6e4d26 --- /dev/null +++ b/Examples/xx-messenger/Sources/LaunchFeature/LaunchErrorView.swift @@ -0,0 +1,44 @@ +import ComposableArchitecture +import SwiftUI + +struct LaunchErrorView: View { + var failure: String + var onRetry: () -> Void + + var body: some View { + NavigationView { + VStack(spacing: 0) { + ScrollView { + Text(failure) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + } + + Divider() + + Button { + onRetry() + } label: { + Text("Retry") + .frame(maxWidth: .infinity) + } + .buttonStyle(.borderedProminent) + .controlSize(.large) + .padding() + } + .navigationTitle("Error") + } + .navigationViewStyle(.stack) + } +} + +#if DEBUG +struct LaunchErrorView_Previews: PreviewProvider { + static var previews: some View { + LaunchErrorView( + failure: "Something went wrong...", + onRetry: {} + ) + } +} +#endif diff --git a/Examples/xx-messenger/Sources/LaunchFeature/LaunchFeature.swift b/Examples/xx-messenger/Sources/LaunchFeature/LaunchFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..39e3efdf0c70780ccd3f2343f17a0fc138fdab57 --- /dev/null +++ b/Examples/xx-messenger/Sources/LaunchFeature/LaunchFeature.swift @@ -0,0 +1,172 @@ +import AppCore +import Combine +import ComposableArchitecture +import ComposablePresentation +import RegisterFeature +import RestoreFeature +import WelcomeFeature +import XXMessengerClient +import XXModels + +public struct LaunchState: Equatable { + public enum Screen: Equatable { + case loading + case welcome(WelcomeState) + case restore(RestoreState) + case register(RegisterState) + case failure(String) + } + + public init( + screen: Screen = .loading + ) { + self.screen = screen + } + + @BindableState public var screen: Screen +} + +extension LaunchState.Screen { + var asWelcome: WelcomeState? { + get { (/LaunchState.Screen.welcome).extract(from: self) } + set { if let state = newValue { self = .welcome(state) } } + } + var asRestore: RestoreState? { + get { (/LaunchState.Screen.restore).extract(from: self) } + set { if let state = newValue { self = .restore(state) } } + } + var asRegister: RegisterState? { + get { (/LaunchState.Screen.register).extract(from: self) } + set { if let state = newValue { self = .register(state) } } + } +} + +public enum LaunchAction: Equatable, BindableAction { + case start + case finished + case binding(BindingAction<LaunchState>) + case welcome(WelcomeAction) + case restore(RestoreAction) + case register(RegisterAction) +} + +public struct LaunchEnvironment { + public init( + dbManager: DBManager, + messenger: Messenger, + mainQueue: AnySchedulerOf<DispatchQueue>, + bgQueue: AnySchedulerOf<DispatchQueue>, + welcome: @escaping () -> WelcomeEnvironment, + restore: @escaping () -> RestoreEnvironment, + register: @escaping () -> RegisterEnvironment + ) { + self.dbManager = dbManager + self.messenger = messenger + self.mainQueue = mainQueue + self.bgQueue = bgQueue + self.welcome = welcome + self.restore = restore + self.register = register + } + + public var dbManager: DBManager + public var messenger: Messenger + public var mainQueue: AnySchedulerOf<DispatchQueue> + public var bgQueue: AnySchedulerOf<DispatchQueue> + public var welcome: () -> WelcomeEnvironment + public var restore: () -> RestoreEnvironment + public var register: () -> RegisterEnvironment +} + +extension LaunchEnvironment { + public static let unimplemented = LaunchEnvironment( + dbManager: .unimplemented, + messenger: .unimplemented, + mainQueue: .unimplemented, + bgQueue: .unimplemented, + welcome: { .unimplemented }, + restore: { .unimplemented }, + register: { .unimplemented } + ) +} + +public let launchReducer = Reducer<LaunchState, LaunchAction, LaunchEnvironment> +{ state, action, env in + switch action { + case .start, .welcome(.finished), .restore(.finished), .register(.finished): + state.screen = .loading + return .future { fulfill in + do { + if env.dbManager.hasDB() == false { + try env.dbManager.makeDB() + } + + if env.messenger.isLoaded() == false { + if env.messenger.isCreated() == false { + fulfill(.success(.set(\.$screen, .welcome(WelcomeState())))) + return + } + try env.messenger.load() + } + + try env.messenger.start() + + if env.messenger.isConnected() == false { + try env.messenger.connect() + } + + if env.messenger.isLoggedIn() == false { + if try env.messenger.isRegistered() == false { + fulfill(.success(.set(\.$screen, .register(RegisterState())))) + return + } + try env.messenger.logIn() + } + + fulfill(.success(.finished)) + } + catch { + fulfill(.success(.set(\.$screen, .failure(error.localizedDescription)))) + } + } + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + + case .finished: + return .none + + case .welcome(.restoreTapped): + state.screen = .restore(RestoreState()) + return .none + + case .welcome(.failed(let failure)): + state.screen = .failure(failure) + return .none + + case .binding(_), .welcome(_), .restore(_), .register(_): + return .none + } +} +.binding() +.presenting( + welcomeReducer, + state: .keyPath(\.screen.asWelcome), + id: .notNil(), + action: /LaunchAction.welcome, + environment: { $0.welcome() } +) +.presenting( + restoreReducer, + state: .keyPath(\.screen.asRestore), + id: .notNil(), + action: /LaunchAction.restore, + environment: { $0.restore() } +) +.presenting( + registerReducer, + state: .keyPath(\.screen.asRegister), + id: .notNil(), + action: /LaunchAction.register, + environment: { $0.register() } +) diff --git a/Examples/xx-messenger/Sources/LaunchFeature/LaunchView.swift b/Examples/xx-messenger/Sources/LaunchFeature/LaunchView.swift new file mode 100644 index 0000000000000000000000000000000000000000..ed110302f45ff390664f9af2e939571c9bbb2646 --- /dev/null +++ b/Examples/xx-messenger/Sources/LaunchFeature/LaunchView.swift @@ -0,0 +1,118 @@ +import ComposableArchitecture +import RegisterFeature +import RestoreFeature +import SwiftUI +import WelcomeFeature + +public struct LaunchView: View { + public init(store: Store<LaunchState, LaunchAction>) { + self.store = store + } + + struct ViewState: Equatable { + enum Screen: Equatable { + case loading + case welcome + case restore + case register + case failure(String) + } + + init(_ state: LaunchState) { + switch state.screen { + case .loading: screen = .loading + case .welcome(_): screen = .welcome + case .restore(_): screen = .restore + case .register(_): screen = .register + case .failure(let failure): screen = .failure(failure) + } + } + + var screen: Screen + } + + let store: Store<LaunchState, LaunchAction> + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + ZStack { + switch viewStore.screen { + case .loading: + ProgressView { + Text("Loading") + } + .controlSize(.large) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.opacity) + + case .welcome: + IfLetStore( + store.scope( + state: { (/LaunchState.Screen.welcome).extract(from: $0.screen) }, + action: LaunchAction.welcome + ), + then: WelcomeView.init(store:) + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.asymmetric( + insertion: .move(edge: .trailing), + removal: .opacity + )) + + case .restore: + IfLetStore( + store.scope( + state: { (/LaunchState.Screen.restore).extract(from: $0.screen) }, + action: LaunchAction.restore + ), + then: RestoreView.init(store:) + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.asymmetric( + insertion: .move(edge: .trailing), + removal: .opacity + )) + + case .register: + IfLetStore( + store.scope( + state: { (/LaunchState.Screen.register).extract(from: $0.screen) }, + action: LaunchAction.register + ), + then: RegisterView.init(store:) + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.asymmetric( + insertion: .move(edge: .trailing), + removal: .opacity + )) + + case .failure(let failure): + LaunchErrorView( + failure: failure, + onRetry: { viewStore.send(.start) } + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .transition(.asymmetric( + insertion: .move(edge: .trailing), + removal: .opacity + )) + } + } + .animation(.default, value: viewStore.screen) + .task { viewStore.send(.start) } + } + } +} + +#if DEBUG +public struct LaunchView_Previews: PreviewProvider { + public static var previews: some View { + LaunchView(store: Store( + initialState: LaunchState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..f929d18fd5bdc1a004cd05f4bb366b5c0d835c03 --- /dev/null +++ b/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift @@ -0,0 +1,89 @@ +import ComposableArchitecture +import SwiftUI +import XXMessengerClient + +public struct RegisterState: Equatable { + public enum Field: String, Hashable { + case username + } + + public init( + focusedField: Field? = nil, + username: String = "", + isRegistering: Bool = false + ) { + self.focusedField = focusedField + self.username = username + self.isRegistering = isRegistering + } + + @BindableState public var focusedField: Field? + @BindableState public var username: String + public var isRegistering: Bool + public var failure: String? +} + +public enum RegisterAction: Equatable, BindableAction { + case registerTapped + case failed(String) + case finished + case binding(BindingAction<RegisterState>) +} + +public struct RegisterEnvironment { + public init( + messenger: Messenger, + mainQueue: AnySchedulerOf<DispatchQueue>, + bgQueue: AnySchedulerOf<DispatchQueue> + ) { + self.messenger = messenger + self.mainQueue = mainQueue + self.bgQueue = bgQueue + } + + public var messenger: Messenger + public var mainQueue: AnySchedulerOf<DispatchQueue> + public var bgQueue: AnySchedulerOf<DispatchQueue> +} + +extension RegisterEnvironment { + public static let unimplemented = RegisterEnvironment( + messenger: .unimplemented, + mainQueue: .unimplemented, + bgQueue: .unimplemented + ) +} + +public let registerReducer = Reducer<RegisterState, RegisterAction, RegisterEnvironment> +{ state, action, env in + switch action { + case .binding(_): + return .none + + case .registerTapped: + state.focusedField = nil + state.isRegistering = true + state.failure = nil + return .future { [username = state.username] fulfill in + do { + try env.messenger.register(username: username) + fulfill(.success(.finished)) + } + catch { + fulfill(.success(.failed(error.localizedDescription))) + } + } + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + + case .failed(let failure): + state.isRegistering = false + state.failure = failure + return .none + + case .finished: + return .none + } +} +.binding() diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift new file mode 100644 index 0000000000000000000000000000000000000000..27bc0962780d7214095ff26592d66c9f6703ae52 --- /dev/null +++ b/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift @@ -0,0 +1,87 @@ +import ComposableArchitecture +import SwiftUI + +public struct RegisterView: View { + public init(store: Store<RegisterState, RegisterAction>) { + self.store = store + } + + let store: Store<RegisterState, RegisterAction> + @FocusState var focusedField: RegisterState.Field? + + struct ViewState: Equatable { + init(_ state: RegisterState) { + focusedField = state.focusedField + username = state.username + isRegistering = state.isRegistering + failure = state.failure + } + + var focusedField: RegisterState.Field? + var username: String + var isRegistering: Bool + var failure: String? + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + NavigationView { + Form { + Section { + TextField( + text: viewStore.binding( + get: \.username, + send: { RegisterAction.set(\.$username, $0) } + ), + prompt: Text("Enter username"), + label: { Text("Username") } + ) + .focused($focusedField, equals: .username) + } header: { + Text("Username") + } + + Section { + Button { + viewStore.send(.registerTapped) + } label: { + HStack { + if viewStore.isRegistering { + ProgressView().padding(.trailing) + Text("Registering...") + } else { + Text("Register") + } + } + .frame(maxWidth: .infinity) + } + } + + if let failure = viewStore.failure { + Section { + Text(failure) + } header: { + Text("Error").foregroundColor(.red) + } + } + } + .disabled(viewStore.isRegistering) + .navigationTitle("Register") + .onChange(of: viewStore.focusedField) { focusedField = $0 } + .onChange(of: focusedField) { viewStore.send(.set(\.$focusedField, $0)) } + } + } + } +} + +#if DEBUG +public struct RegisterView_Previews: PreviewProvider { + public static var previews: some View { + RegisterView(store: Store( + initialState: RegisterState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Examples/xx-messenger/Sources/RestoreFeature/RestoreFeature.swift b/Examples/xx-messenger/Sources/RestoreFeature/RestoreFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..6ce31e5d67acfde939a1bc9ba03e28226f4e0761 --- /dev/null +++ b/Examples/xx-messenger/Sources/RestoreFeature/RestoreFeature.swift @@ -0,0 +1,19 @@ +import ComposableArchitecture + +public struct RestoreState: Equatable { + public init() {} +} + +public enum RestoreAction: Equatable { + case finished +} + +public struct RestoreEnvironment { + public init() {} +} + +extension RestoreEnvironment { + public static let unimplemented = RestoreEnvironment() +} + +public let restoreReducer = Reducer<RestoreState, RestoreAction, RestoreEnvironment>.empty diff --git a/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift b/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift new file mode 100644 index 0000000000000000000000000000000000000000..137b55069b5c0e5df287b2351e181309abbe7181 --- /dev/null +++ b/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift @@ -0,0 +1,49 @@ +import ComposableArchitecture +import SwiftUI + +public struct RestoreView: View { + public init(store: Store<RestoreState, RestoreAction>) { + self.store = store + } + + let store: Store<RestoreState, RestoreAction> + + struct ViewState: Equatable { + init(state: RestoreState) {} + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + NavigationView { + Form { + Section { + Text("Not implemented") + } + + Section { + Button { + viewStore.send(.finished) + } label: { + Text("OK") + .frame(maxWidth: .infinity) + } + } + } + .navigationTitle("Restore") + } + .navigationViewStyle(.stack) + } + } +} + +#if DEBUG +public struct RestoreView_Previews: PreviewProvider { + public static var previews: some View { + RestoreView(store: Store( + initialState: RestoreState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeFeature.swift b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeFeature.swift new file mode 100644 index 0000000000000000000000000000000000000000..6c6f2e22aa36e73ff0fc4acc441eb63b91f56e66 --- /dev/null +++ b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeFeature.swift @@ -0,0 +1,75 @@ +import ComposableArchitecture +import SwiftUI +import XXMessengerClient + +public struct WelcomeState: Equatable { + public init( + isCreatingCMix: Bool = false + ) { + self.isCreatingAccount = isCreatingCMix + } + + public var isCreatingAccount: Bool +} + +public enum WelcomeAction: Equatable { + case newAccountTapped + case restoreTapped + case finished + case failed(String) +} + +public struct WelcomeEnvironment { + public init( + messenger: Messenger, + mainQueue: AnySchedulerOf<DispatchQueue>, + bgQueue: AnySchedulerOf<DispatchQueue> + ) { + self.messenger = messenger + self.mainQueue = mainQueue + self.bgQueue = bgQueue + } + + public var messenger: Messenger + public var mainQueue: AnySchedulerOf<DispatchQueue> + public var bgQueue: AnySchedulerOf<DispatchQueue> +} + +extension WelcomeEnvironment { + public static let unimplemented = WelcomeEnvironment( + messenger: .unimplemented, + mainQueue: .unimplemented, + bgQueue: .unimplemented + ) +} + +public let welcomeReducer = Reducer<WelcomeState, WelcomeAction, WelcomeEnvironment> +{ state, action, env in + switch action { + case .newAccountTapped: + state.isCreatingAccount = true + return .future { fulfill in + do { + try env.messenger.create() + fulfill(.success(.finished)) + } + catch { + fulfill(.success(.failed(error.localizedDescription))) + } + } + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + + case .restoreTapped: + return .none + + case .finished: + state.isCreatingAccount = false + return .none + + case .failed(_): + state.isCreatingAccount = false + return .none + } +} diff --git a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift new file mode 100644 index 0000000000000000000000000000000000000000..d3686c5aa5f1b410cea89176feecdcf050dccb3a --- /dev/null +++ b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift @@ -0,0 +1,68 @@ +import ComposableArchitecture +import SwiftUI + +public struct WelcomeView: View { + public init(store: Store<WelcomeState, WelcomeAction>) { + self.store = store + } + + let store: Store<WelcomeState, WelcomeAction> + + struct ViewState: Equatable { + init(_ state: WelcomeState) { + isCreatingAccount = state.isCreatingAccount + } + + var isCreatingAccount: Bool + } + + public var body: some View { + WithViewStore(store.scope(state: ViewState.init)) { viewStore in + NavigationView { + Form { + Section { + Text("xx messenger") + } + + Section { + Button { + viewStore.send(.newAccountTapped) + } label: { + HStack { + if viewStore.isCreatingAccount { + ProgressView().padding(.trailing) + Text("Creating Account...") + } else { + Text("New Account") + } + } + .frame(maxWidth: .infinity) + } + + Button { + viewStore.send(.restoreTapped) + } label: { + Text("Restore from Backup") + .frame(maxWidth: .infinity) + } + } + } + .disabled(viewStore.isCreatingAccount) + .navigationTitle("Welcome") + } + .navigationViewStyle(.stack) + } + } +} + +#if DEBUG +public struct WelcomeView_Previews: PreviewProvider { + public static var previews: some View { + WelcomeView(store: Store( + initialState: WelcomeState(), + reducer: .empty, + environment: () + )) + } +} +#endif diff --git a/Examples/xx-messenger/Tests/AppCoreTests/AppCoreTests.swift b/Examples/xx-messenger/Tests/AppCoreTests/AppCoreTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..9a5658d4caa59cea2181061ff1ca1d2d6d38c5e4 --- /dev/null +++ b/Examples/xx-messenger/Tests/AppCoreTests/AppCoreTests.swift @@ -0,0 +1,9 @@ +import XCTest +@testable import AppCore + +@MainActor +final class AppCoreTests: XCTestCase { + func testExample() async throws { + XCTAssert(true) + } +} diff --git a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift b/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..6563da86bb1d74e25b4cb0c551c7314c20542792 --- /dev/null +++ b/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift @@ -0,0 +1,19 @@ +import ComposableArchitecture +import HomeFeature +import XCTest +@testable import AppFeature + +@MainActor +final class AppFeatureTests: XCTestCase { + func testLaunchFinished() async throws { + let store = TestStore( + initialState: AppState(), + reducer: appReducer, + environment: .unimplemented + ) + + await store.send(.launch(.finished)) { + $0.screen = .home(HomeState()) + } + } +} diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..21a25bb641274dced7d7dd83ebc49f073ada4420 --- /dev/null +++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift @@ -0,0 +1,16 @@ +import ComposableArchitecture +import XCTest +@testable import HomeFeature + +@MainActor +final class HomeFeatureTests: XCTestCase { + func testStart() async throws { + let store = TestStore( + initialState: HomeState(), + reducer: homeReducer, + environment: .unimplemented + ) + + await store.send(.start) + } +} diff --git a/Examples/xx-messenger/Tests/LaunchFeatureTests/LaunchFeatureTests.swift b/Examples/xx-messenger/Tests/LaunchFeatureTests/LaunchFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..b2b3f856f79260ef652664f9c9892382296ede95 --- /dev/null +++ b/Examples/xx-messenger/Tests/LaunchFeatureTests/LaunchFeatureTests.swift @@ -0,0 +1,327 @@ +import AppCore +import ComposableArchitecture +import RegisterFeature +import RestoreFeature +import WelcomeFeature +import XCTest +import XXModels +@testable import LaunchFeature + +@MainActor +final class LaunchFeatureTests: XCTestCase { + func testStart() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + var didMakeDB = 0 + var messengerDidLoad = 0 + var messengerDidStart = 0 + var messengerDidConnect = 0 + var messengerDidLogIn = 0 + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { false } + store.environment.dbManager.makeDB.run = { didMakeDB += 1 } + store.environment.messenger.isLoaded.run = { false } + store.environment.messenger.isCreated.run = { true } + store.environment.messenger.load.run = { messengerDidLoad += 1 } + store.environment.messenger.start.run = { _ in messengerDidStart += 1 } + store.environment.messenger.isConnected.run = { false } + store.environment.messenger.connect.run = { messengerDidConnect += 1 } + store.environment.messenger.isLoggedIn.run = { false } + store.environment.messenger.isRegistered.run = { true } + store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } + + store.send(.start) + + bgQueue.advance() + + XCTAssertNoDifference(didMakeDB, 1) + XCTAssertNoDifference(messengerDidLoad, 1) + XCTAssertNoDifference(messengerDidStart, 1) + XCTAssertNoDifference(messengerDidConnect, 1) + XCTAssertNoDifference(messengerDidLogIn, 1) + + mainQueue.advance() + + store.receive(.finished) + } + + func testStartWithoutMessengerCreated() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { false } + store.environment.messenger.isCreated.run = { false } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .welcome(WelcomeState()))) { + $0.screen = .welcome(WelcomeState()) + } + } + + func testStartUnregistered() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { true } + store.environment.messenger.start.run = { _ in } + store.environment.messenger.isConnected.run = { true } + store.environment.messenger.isLoggedIn.run = { false } + store.environment.messenger.isRegistered.run = { false } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .register(RegisterState()))) { + $0.screen = .register(RegisterState()) + } + } + + func testStartMakeDBFailure() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { false } + store.environment.dbManager.makeDB.run = { throw error } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .failure(error.localizedDescription))) { + $0.screen = .failure(error.localizedDescription) + } + } + + func testStartMessengerLoadFailure() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { false } + store.environment.messenger.isCreated.run = { true } + store.environment.messenger.load.run = { throw error } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .failure(error.localizedDescription))) { + $0.screen = .failure(error.localizedDescription) + } + } + + func testStartMessengerStartFailure() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { true } + store.environment.messenger.start.run = { _ in throw error } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .failure(error.localizedDescription))) { + $0.screen = .failure(error.localizedDescription) + } + } + + func testStartMessengerConnectFailure() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { true } + store.environment.messenger.start.run = { _ in } + store.environment.messenger.isConnected.run = { false } + store.environment.messenger.connect.run = { throw error } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .failure(error.localizedDescription))) { + $0.screen = .failure(error.localizedDescription) + } + } + + func testStartMessengerIsRegisteredFailure() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { true } + store.environment.messenger.start.run = { _ in } + store.environment.messenger.isConnected.run = { true } + store.environment.messenger.isLoggedIn.run = { false } + store.environment.messenger.isRegistered.run = { throw error } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .failure(error.localizedDescription))) { + $0.screen = .failure(error.localizedDescription) + } + } + + func testStartMessengerLogInFailure() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.dbManager.hasDB.run = { true } + store.environment.messenger.isLoaded.run = { true } + store.environment.messenger.start.run = { _ in } + store.environment.messenger.isConnected.run = { true } + store.environment.messenger.isLoggedIn.run = { false } + store.environment.messenger.isRegistered.run = { true } + store.environment.messenger.logIn.run = { throw error } + + store.send(.start) + + bgQueue.advance() + mainQueue.advance() + + store.receive(.set(\.$screen, .failure(error.localizedDescription))) { + $0.screen = .failure(error.localizedDescription) + } + } + + func testWelcomeRestoreTapped() { + let store = TestStore( + initialState: LaunchState( + screen: .welcome(WelcomeState()) + ), + reducer: launchReducer, + environment: .unimplemented + ) + + store.send(.welcome(.restoreTapped)) { + $0.screen = .restore(RestoreState()) + } + } + + func testWelcomeFailed() { + let store = TestStore( + initialState: LaunchState( + screen: .welcome(WelcomeState()) + ), + reducer: launchReducer, + environment: .unimplemented + ) + + let failure = "Something went wrong" + + store.send(.welcome(.failed(failure))) { + $0.screen = .failure(failure) + } + } + + func testFinished() { + let store = TestStore( + initialState: LaunchState(), + reducer: launchReducer, + environment: .unimplemented + ) + + store.send(.finished) + } +} diff --git a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..21d4da16412ab852ff11e1b40da58ac1a5fd5388 --- /dev/null +++ b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift @@ -0,0 +1,72 @@ +import ComposableArchitecture +import XCTest +@testable import RegisterFeature + +@MainActor +final class RegisterFeatureTests: XCTestCase { + func testRegister() throws { + let store = TestStore( + initialState: RegisterState(), + reducer: registerReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + var messengerDidRegisterUsername: [String] = [] + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.messenger.register.run = { username in + messengerDidRegisterUsername.append(username) + } + + store.send(.set(\.$username, "NewUser")) { + $0.username = "NewUser" + } + + store.send(.registerTapped) { + $0.isRegistering = true + } + + XCTAssertNoDifference(messengerDidRegisterUsername, []) + + bgQueue.advance() + + XCTAssertNoDifference(messengerDidRegisterUsername, ["NewUser"]) + + mainQueue.advance() + + store.receive(.finished) + } + + func testRegisterFailure() throws { + struct Error: Swift.Error, Equatable {} + let error = Error() + + let store = TestStore( + initialState: RegisterState(), + reducer: registerReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.messenger.register.run = { _ in throw error } + + store.send(.registerTapped) { + $0.isRegistering = true + } + + bgQueue.advance() + mainQueue.advance() + + store.receive(.failed(error.localizedDescription)) { + $0.isRegistering = false + $0.failure = error.localizedDescription + } + } +} diff --git a/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreFeatureTests.swift b/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..1c3e1c97da4f95ddedf16116d44a155a3c2200da --- /dev/null +++ b/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreFeatureTests.swift @@ -0,0 +1,16 @@ +import ComposableArchitecture +import XCTest +@testable import RestoreFeature + +@MainActor +final class RestoreFeatureTests: XCTestCase { + func testFinish() async throws { + let store = TestStore( + initialState: RestoreState(), + reducer: restoreReducer, + environment: .unimplemented + ) + + await store.send(.finished) + } +} diff --git a/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeFeatureTests.swift b/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeFeatureTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..eb6b08566af0f4193c10b5167271bd86b2897330 --- /dev/null +++ b/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeFeatureTests.swift @@ -0,0 +1,74 @@ +import ComposableArchitecture +import XCTest +@testable import WelcomeFeature + +@MainActor +final class WelcomeFeatureTests: XCTestCase { + func testNewAccountTapped() { + let store = TestStore( + initialState: WelcomeState(), + reducer: welcomeReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + var messengerDidCreate = false + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.messenger.create.run = { messengerDidCreate = true } + + store.send(.newAccountTapped) { + $0.isCreatingAccount = true + } + + bgQueue.advance() + + XCTAssertTrue(messengerDidCreate) + + mainQueue.advance() + + store.receive(.finished) { + $0.isCreatingAccount = false + } + } + + func testNewAccountTappedMessengerCreateFailure() { + let store = TestStore( + initialState: WelcomeState(), + reducer: welcomeReducer, + environment: .unimplemented + ) + + let mainQueue = DispatchQueue.test + let bgQueue = DispatchQueue.test + struct Error: Swift.Error, Equatable {} + let error = Error() + + store.environment.mainQueue = mainQueue.eraseToAnyScheduler() + store.environment.bgQueue = bgQueue.eraseToAnyScheduler() + store.environment.messenger.create.run = { throw error } + + store.send(.newAccountTapped) { + $0.isCreatingAccount = true + } + + bgQueue.advance() + mainQueue.advance() + + store.receive(.failed(error.localizedDescription)) { + $0.isCreatingAccount = false + } + } + + func testRestore() { + let store = TestStore( + initialState: WelcomeState(), + reducer: welcomeReducer, + environment: .unimplemented + ) + + store.send(.restoreTapped) + } +} diff --git a/ElixxirDAppsSDK.xcworkspace/contents.xcworkspacedata b/Examples/xx-messenger/XXMessenger.xcworkspace/contents.xcworkspacedata similarity index 74% rename from ElixxirDAppsSDK.xcworkspace/contents.xcworkspacedata rename to Examples/xx-messenger/XXMessenger.xcworkspace/contents.xcworkspacedata index 9ca22f2ec7dc6801cd91f1e2b68ae885d4abb591..b101ba6c89faef725536d6bee514440ad3b906db 100644 --- a/ElixxirDAppsSDK.xcworkspace/contents.xcworkspacedata +++ b/Examples/xx-messenger/XXMessenger.xcworkspace/contents.xcworkspacedata @@ -5,6 +5,6 @@ location = "group:"> </FileRef> <FileRef - location = "group:Example/Example.xcodeproj"> + location = "group:Project/XXMessenger.xcodeproj"> </FileRef> </Workspace> diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/ElixxirDAppsSDK.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from ElixxirDAppsSDK.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/ElixxirDAppsSDK.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 85% rename from ElixxirDAppsSDK.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6829b18147b9023c7dac8a13beb9615666fcb45a..c330aa9448492ecb8f33d3b05ee260fe3897d417 100644 --- a/ElixxirDAppsSDK.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "client-ios-db", + "kind" : "remoteSourceControl", + "location" : "https://git.xx.network/elixxir/client-ios-db.git", + "state" : { + "revision" : "f8e3e0088de8301d6c4816e12f0aca1d6f02a280", + "version" : "1.1.0" + } + }, { "identity" : "combine-schedulers", "kind" : "remoteSourceControl", @@ -9,6 +18,15 @@ "version" : "0.7.2" } }, + { + "identity" : "grdb.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/groue/GRDB.swift", + "state" : { + "revision" : "23f4254ae36fa19aecd73047c0577a9f49850d1c", + "version" : "5.26.0" + } + }, { "identity" : "keychainaccess", "kind" : "remoteSourceControl", @@ -72,15 +90,6 @@ "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", diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000000000000000000000000000000000000..6741fe9d93c193203c994ec7bd191103eecea29b --- /dev/null +++ b/Package.resolved @@ -0,0 +1,32 @@ +{ + "pins" : [ + { + "identity" : "keychainaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git", + "state" : { + "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7", + "version" : "4.2.2" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump.git", + "state" : { + "revision" : "21ec1d717c07cea5a026979cb0471dd95c7087e7", + "version" : "0.5.0" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay.git", + "state" : { + "revision" : "38bc9242e4388b80bd23ddfdf3071428859e3260", + "version" : "0.4.0" + } + } + ], + "version" : 2 +} diff --git a/README.md b/README.md index a311b5801b5a2b9d5cf53877047b28e862348a85..a6f30248b2545b148158e9aba4eda7ea50dc0515 100644 --- a/README.md +++ b/README.md @@ -8,39 +8,26 @@ - [XXClient Quick Start Guide](Docs/XXClient-quick-start-guide.md) - [XXMessengerClient](Docs/XXMessengerClient.md) -## 📱 Demo +## 📱 Examples -Checkout included example iOS application. +Check out included [examples](Examples). ## 🛠Development -Open `ElixxirDAppsSDK.xcworkspace` in Xcode (≥13.4). +Open `Package.swift` in Xcode (≥13.4). ### Project structure ``` -ElixxirDAppsSDK [Xcode Workspace] - ├─ elixxir-dapps-sdk-swift [Swift Package] - | ├─ XXClient [Library] - | └─ XXMessengerClient [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] +elixxir-dapps-sdk-swift [Swift Package] + ├─ XXClient [Library] + └─ XXMessengerClient [Library] ``` ### Build schemes -- Use `exlixxir-dapps-sdk-swift` scheme to build and test the package. -- 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 `XXClient`, for building and testing individual libraries in isolation. +- Use `exlixxir-dapps-sdk-swift-Package` scheme to build and test the package. +- Use other schemes (like `XXClient`) for building and testing individual libraries in isolation. ## 📄 License