Repository: CassiusPacheco/Swift-CleanArchitecture Branch: master Commit: 0f3babd22878 Files: 77 Total size: 134.1 KB Directory structure: gitextract__82anwzf/ ├── .gitignore ├── App/ │ ├── App.entitlements │ ├── App.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── App.xcscheme │ │ ├── AppIntents.xcscheme │ │ └── AppIntentsUI.xcscheme │ ├── AppCoordinator/ │ │ ├── AppCoordinator.swift │ │ ├── Coordinator.swift │ │ └── SiriShortcutCoordinator.swift │ ├── AppDelegate.swift │ ├── AppIntents/ │ │ ├── AppIntents.entitlements │ │ ├── Info.plist │ │ └── IntentHandler.swift │ ├── AppIntentsUI/ │ │ ├── AppIntentsUI.entitlements │ │ ├── Base.lproj/ │ │ │ └── MainInterface.storyboard │ │ ├── Info.plist │ │ └── IntentViewController.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj/ │ │ └── LaunchScreen.storyboard │ ├── ExtensionIntents/ │ │ ├── CreateProductIntentHandler.swift │ │ ├── ExtensionIntents.h │ │ ├── Info.plist │ │ ├── Intents.intentdefinition │ │ ├── InterfaceBinding.swift │ │ └── NSUserActivity+IntentData.swift │ ├── Features/ │ │ ├── Detail/ │ │ │ ├── Coordinator/ │ │ │ │ └── DetailCoordinator.swift │ │ │ ├── ViewControllers/ │ │ │ │ ├── DetailViewController.swift │ │ │ │ └── DetailViewController.xib │ │ │ └── ViewModel/ │ │ │ └── DetailViewModel.swift │ │ └── Main/ │ │ ├── Coordinator/ │ │ │ └── MainCoordinator.swift │ │ ├── ViewControllers/ │ │ │ ├── MainViewController.swift │ │ │ └── MainViewController.xib │ │ └── ViewModel/ │ │ └── MainViewModel.swift │ ├── Info.plist │ └── Plugins/ │ └── Dependency Injection/ │ ├── DependencyGraph.swift │ └── InterfaceBinding.swift ├── AppTests/ │ ├── AppTests.swift │ └── Info.plist ├── Data/ │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources/ │ │ └── Data/ │ │ ├── CacheInterface.swift │ │ ├── Product/ │ │ │ ├── ProductRepository.swift │ │ │ └── ProductServiceInterface.swift │ │ └── ServiceInterface.swift │ └── Tests/ │ └── DataTests/ │ └── DataTests.swift ├── Domain/ │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources/ │ │ └── Domain/ │ │ ├── Product/ │ │ │ ├── CreateProductUseCase.swift │ │ │ └── ProductsUseCase.swift │ │ └── UseCase.swift │ └── Tests/ │ └── DomainTests/ │ └── DomainTests.swift ├── Entities/ │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources/ │ │ └── Entities/ │ │ └── Product.swift │ └── Tests/ │ └── EntitiesTests/ │ └── EntitiesTests.swift ├── Plugins/ │ ├── NetworkServices/ │ │ ├── .gitignore │ │ ├── Package.swift │ │ ├── README.md │ │ ├── Sources/ │ │ │ └── NetworkServices/ │ │ │ └── ProductService.swift │ │ └── Tests/ │ │ └── NetworkServicesTests/ │ │ └── NetworkServicesTests.swift │ └── Persistence/ │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Sources/ │ │ └── Persistence/ │ │ ├── Persistence.swift │ │ └── UserDefaultsInterface.swift │ └── Tests/ │ └── PersistenceTests/ │ ├── PersistenceTests.swift │ └── UserDefaultsMock.swift ├── README.md └── SwiftCleanArchitecture.xcworkspace/ ├── contents.xcworkspacedata └── xcshareddata/ ├── IDEWorkspaceChecks.plist └── swiftpm/ └── Package.resolved ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate */*.xcodeproj/xcuserdata/ tmp/ testData/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # Pods/ # fastlane files fastlane/report.xml fastlane/test_output/* *.app.dSYM.zip Preview.html #Circle CI files .bundle/ vendor/ reports/ resources/ results.html logs/ test-output/ #AppCode files .idea/ #Carthage Carthage/ #Others compile_time.txt ================================================ FILE: App/App.entitlements ================================================ com.apple.developer.siri com.apple.security.application-groups group.com.cassiuspacheco.Swift-CleanArchitecture ================================================ FILE: App/App.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 52; objects = { /* Begin PBXBuildFile section */ 6A1DF8A5241E4B54003A584E /* DependencyContainer in Frameworks */ = {isa = PBXBuildFile; productRef = 6A1DF8A4241E4B54003A584E /* DependencyContainer */; }; 6A2F96D721DEFA1200864087 /* InterfaceBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A2F96D621DEFA1200864087 /* InterfaceBinding.swift */; }; 6A2F96E021DF11AF00864087 /* SiriShortcutCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A2F96DF21DF11AF00864087 /* SiriShortcutCoordinator.swift */; }; 6A74424C21DEF45900F865FB /* ExtensionIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A74424B21DEF45900F865FB /* ExtensionIntents.framework */; }; 6A74424D21DEF45900F865FB /* ExtensionIntents.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6A74424B21DEF45900F865FB /* ExtensionIntents.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 6A74424E21DEF45900F865FB /* ExtensionIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A74424B21DEF45900F865FB /* ExtensionIntents.framework */; }; 6AAAC15720F1164E007D8CB1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6AAAC14520F1164E007D8CB1 /* Assets.xcassets */; }; 6AAAC15820F1164E007D8CB1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6AAAC14620F1164E007D8CB1 /* LaunchScreen.storyboard */; }; 6AAAC15D20F1164E007D8CB1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AAAC14D20F1164E007D8CB1 /* AppDelegate.swift */; }; 6AAD2E1A240FD669000E6B9A /* RxTest in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E19240FD669000E6B9A /* RxTest */; }; 6AAD2E1C240FD669000E6B9A /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E1B240FD669000E6B9A /* RxRelay */; }; 6AAD2E1E240FD669000E6B9A /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E1D240FD669000E6B9A /* RxCocoa */; }; 6AAD2E20240FD669000E6B9A /* RxBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E1F240FD669000E6B9A /* RxBlocking */; }; 6AAD2E22240FD999000E6B9A /* Entities in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E21240FD999000E6B9A /* Entities */; }; 6AAD2E24240FD9EF000E6B9A /* Data in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E23240FD9EF000E6B9A /* Data */; }; 6AAD2E26240FDAE1000E6B9A /* Domain in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E25240FDAE1000E6B9A /* Domain */; }; 6AAD2E28240FDBBC000E6B9A /* NetworkServices in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E27240FDBBC000E6B9A /* NetworkServices */; }; 6AAD2E2A240FDBC0000E6B9A /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E29240FDBC0000E6B9A /* Persistence */; }; 6AAD2E2C240FDBF6000E6B9A /* Data in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E2B240FDBF6000E6B9A /* Data */; }; 6AAD2E2E240FDBF6000E6B9A /* Entities in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E2D240FDBF6000E6B9A /* Entities */; }; 6AAD2E30240FDBF6000E6B9A /* NetworkServices in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E2F240FDBF6000E6B9A /* NetworkServices */; }; 6AAD2E32240FDBF6000E6B9A /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E31240FDBF6000E6B9A /* Persistence */; }; 6AAD2E34240FDBF6000E6B9A /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E33240FDBF6000E6B9A /* RxSwift */; }; 6AAD2E36240FE320000E6B9A /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 6AAD2E35240FE320000E6B9A /* RxSwift */; }; 6AAD2E3D240FE5C5000E6B9A /* InterfaceBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3934F221C65BD800B12F8F /* InterfaceBinding.swift */; }; 6AB1364721081A53004084DC /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1363021081A53004084DC /* AppCoordinator.swift */; }; 6AB1364821081A53004084DC /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1363121081A53004084DC /* Coordinator.swift */; }; 6AB1364B21081A53004084DC /* DependencyGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1363821081A53004084DC /* DependencyGraph.swift */; }; 6AB1364C21081A53004084DC /* DetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1363C21081A53004084DC /* DetailViewModel.swift */; }; 6AB1364D21081A53004084DC /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1363F21081A53004084DC /* DetailViewController.swift */; }; 6AB1364E21081A53004084DC /* DetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AB1364021081A53004084DC /* DetailViewController.xib */; }; 6AB1365321081A6F004084DC /* DetailCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1365221081A6F004084DC /* DetailCoordinator.swift */; }; 6AB1365921081A9C004084DC /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AB1365521081A9C004084DC /* MainViewController.xib */; }; 6AB1365A21081A9C004084DC /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1365621081A9C004084DC /* MainViewController.swift */; }; 6AB1365C21081AB1004084DC /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1365B21081AB1004084DC /* MainViewModel.swift */; }; 6AB1365E21081B39004084DC /* MainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB1365D21081B39004084DC /* MainCoordinator.swift */; }; 6ACB875820F116DD005A04A7 /* AppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AAAC13320F1164E007D8CB1 /* AppTests.swift */; }; 6AFDC44721DEDF7E00B9BB46 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC44621DEDF7E00B9BB46 /* IntentHandler.swift */; }; 6AFDC44F21DEDF7E00B9BB46 /* IntentsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AFDC44E21DEDF7E00B9BB46 /* IntentsUI.framework */; }; 6AFDC45221DEDF7E00B9BB46 /* IntentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC45121DEDF7E00B9BB46 /* IntentViewController.swift */; }; 6AFDC45521DEDF7E00B9BB46 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6AFDC45321DEDF7E00B9BB46 /* MainInterface.storyboard */; }; 6AFDC45921DEDF7E00B9BB46 /* AppIntentsUI.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6AFDC44D21DEDF7E00B9BB46 /* AppIntentsUI.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6AFDC45C21DEDF7E00B9BB46 /* AppIntents.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6AFDC44421DEDF7E00B9BB46 /* AppIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6AFDC48221DEEB4D00B9BB46 /* ExtensionIntents.h in Headers */ = {isa = PBXBuildFile; fileRef = 6AFDC47221DEEB4D00B9BB46 /* ExtensionIntents.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6AFDC49021DEEC6600B9BB46 /* NSUserActivity+IntentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC48D21DEEC6600B9BB46 /* NSUserActivity+IntentData.swift */; }; 6AFDC49121DEEC6600B9BB46 /* CreateProductIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC48E21DEEC6600B9BB46 /* CreateProductIntentHandler.swift */; }; 6AFDC49221DEEC6600B9BB46 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC48F21DEEC6600B9BB46 /* Intents.intentdefinition */; }; 6AFDC49321DEEC6E00B9BB46 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC48F21DEEC6600B9BB46 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; }; 6AFDC49421DEEC7200B9BB46 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC48F21DEEC6600B9BB46 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; }; 6AFDC49521DEEC7200B9BB46 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 6AFDC48F21DEEC6600B9BB46 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 6A26FB8220F0E54500CDA761 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6A26FB6520F0E54400CDA761 /* Project object */; proxyType = 1; remoteGlobalIDString = 6A26FB6C20F0E54400CDA761; remoteInfo = Project; }; 6AFDC45721DEDF7E00B9BB46 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6A26FB6520F0E54400CDA761 /* Project object */; proxyType = 1; remoteGlobalIDString = 6AFDC44C21DEDF7E00B9BB46; remoteInfo = AppIntentsUI; }; 6AFDC45A21DEDF7E00B9BB46 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6A26FB6520F0E54400CDA761 /* Project object */; proxyType = 1; remoteGlobalIDString = 6AFDC44321DEDF7E00B9BB46; remoteInfo = AppIntents; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 6AFDC46321DEDF7E00B9BB46 /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( 6AFDC45C21DEDF7E00B9BB46 /* AppIntents.appex in Embed App Extensions */, 6AFDC45921DEDF7E00B9BB46 /* AppIntentsUI.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; 6AFDC4A921DEF2AD00B9BB46 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 6A74424D21DEF45900F865FB /* ExtensionIntents.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 6A2F96D621DEFA1200864087 /* InterfaceBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceBinding.swift; sourceTree = ""; }; 6A2F96D821DEFE4500864087 /* AppIntents.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppIntents.entitlements; sourceTree = ""; }; 6A2F96DE21DF0E1F00864087 /* AppIntentsUI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppIntentsUI.entitlements; sourceTree = ""; }; 6A2F96DF21DF11AF00864087 /* SiriShortcutCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriShortcutCoordinator.swift; sourceTree = ""; }; 6A3934F221C65BD800B12F8F /* InterfaceBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterfaceBinding.swift; sourceTree = ""; }; 6A74424B21DEF45900F865FB /* ExtensionIntents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExtensionIntents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6AAAC12320F11572007D8CB1 /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6AAAC13320F1164E007D8CB1 /* AppTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppTests.swift; sourceTree = ""; }; 6AAAC13420F1164E007D8CB1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6AAAC14520F1164E007D8CB1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 6AAAC14720F1164E007D8CB1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 6AAAC14D20F1164E007D8CB1 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 6AAAC14E20F1164E007D8CB1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6AAD2E3E240FE98F000E6B9A /* AppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6AB1363021081A53004084DC /* AppCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; 6AB1363121081A53004084DC /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; 6AB1363821081A53004084DC /* DependencyGraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DependencyGraph.swift; sourceTree = ""; }; 6AB1363C21081A53004084DC /* DetailViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewModel.swift; sourceTree = ""; }; 6AB1363F21081A53004084DC /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 6AB1364021081A53004084DC /* DetailViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DetailViewController.xib; sourceTree = ""; }; 6AB1365221081A6F004084DC /* DetailCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCoordinator.swift; sourceTree = ""; }; 6AB1365521081A9C004084DC /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = ""; }; 6AB1365621081A9C004084DC /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 6AB1365B21081AB1004084DC /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = ""; }; 6AB1365D21081B39004084DC /* MainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = ""; }; 6AFDC44421DEDF7E00B9BB46 /* AppIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AppIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 6AFDC44621DEDF7E00B9BB46 /* IntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; 6AFDC44821DEDF7E00B9BB46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6AFDC44D21DEDF7E00B9BB46 /* AppIntentsUI.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AppIntentsUI.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 6AFDC44E21DEDF7E00B9BB46 /* IntentsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IntentsUI.framework; path = System/Library/Frameworks/IntentsUI.framework; sourceTree = SDKROOT; }; 6AFDC45121DEDF7E00B9BB46 /* IntentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentViewController.swift; sourceTree = ""; }; 6AFDC45421DEDF7E00B9BB46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; 6AFDC45621DEDF7E00B9BB46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6AFDC46A21DEE86D00B9BB46 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = ""; }; 6AFDC47221DEEB4D00B9BB46 /* ExtensionIntents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExtensionIntents.h; sourceTree = ""; }; 6AFDC47321DEEB4D00B9BB46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6AFDC48D21DEEC6600B9BB46 /* NSUserActivity+IntentData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+IntentData.swift"; sourceTree = ""; }; 6AFDC48E21DEEC6600B9BB46 /* CreateProductIntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateProductIntentHandler.swift; sourceTree = ""; }; 6AFDC48F21DEEC6600B9BB46 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 6A26FB6A20F0E54400CDA761 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 6A74424C21DEF45900F865FB /* ExtensionIntents.framework in Frameworks */, 6AAD2E1E240FD669000E6B9A /* RxCocoa in Frameworks */, 6AAD2E36240FE320000E6B9A /* RxSwift in Frameworks */, 6AAD2E1C240FD669000E6B9A /* RxRelay in Frameworks */, 6AAD2E22240FD999000E6B9A /* Entities in Frameworks */, 6A1DF8A5241E4B54003A584E /* DependencyContainer in Frameworks */, 6AAD2E2A240FDBC0000E6B9A /* Persistence in Frameworks */, 6AAD2E26240FDAE1000E6B9A /* Domain in Frameworks */, 6AAD2E28240FDBBC000E6B9A /* NetworkServices in Frameworks */, 6AAD2E24240FD9EF000E6B9A /* Data in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 6A26FB7E20F0E54500CDA761 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 6AAD2E1A240FD669000E6B9A /* RxTest in Frameworks */, 6AAD2E20240FD669000E6B9A /* RxBlocking in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC44121DEDF7E00B9BB46 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 6A74424E21DEF45900F865FB /* ExtensionIntents.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC44A21DEDF7E00B9BB46 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 6AFDC44F21DEDF7E00B9BB46 /* IntentsUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC46D21DEEB4D00B9BB46 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 6AAD2E2E240FDBF6000E6B9A /* Entities in Frameworks */, 6AAD2E34240FDBF6000E6B9A /* RxSwift in Frameworks */, 6AAD2E2C240FDBF6000E6B9A /* Data in Frameworks */, 6AAD2E30240FDBF6000E6B9A /* NetworkServices in Frameworks */, 6AAD2E32240FDBF6000E6B9A /* Persistence in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 6A26FB6420F0E54400CDA761 = { isa = PBXGroup; children = ( 6AAAC13520F1164E007D8CB1 /* App */, 6AFDC44521DEDF7E00B9BB46 /* AppIntents */, 6AFDC45021DEDF7E00B9BB46 /* AppIntentsUI */, 6AAAC13220F1164E007D8CB1 /* AppTests */, 6AFDC47121DEEB4D00B9BB46 /* ExtensionIntents */, 6A3934D921C6539D00B12F8F /* Frameworks */, 6AAAC12320F11572007D8CB1 /* App.app */, 6AFDC44421DEDF7E00B9BB46 /* AppIntents.appex */, 6AFDC44D21DEDF7E00B9BB46 /* AppIntentsUI.appex */, 6A74424B21DEF45900F865FB /* ExtensionIntents.framework */, 6AAD2E3E240FE98F000E6B9A /* AppTests.xctest */, ); sourceTree = ""; }; 6A3934D921C6539D00B12F8F /* Frameworks */ = { isa = PBXGroup; children = ( 6AFDC44E21DEDF7E00B9BB46 /* IntentsUI.framework */, ); name = Frameworks; sourceTree = ""; }; 6AAAC13220F1164E007D8CB1 /* AppTests */ = { isa = PBXGroup; children = ( 6AAAC13320F1164E007D8CB1 /* AppTests.swift */, 6AAAC13420F1164E007D8CB1 /* Info.plist */, ); name = AppTests; path = ../AppTests; sourceTree = ""; }; 6AAAC13520F1164E007D8CB1 /* App */ = { isa = PBXGroup; children = ( 6AFDC46A21DEE86D00B9BB46 /* App.entitlements */, 6AB1362F21081A53004084DC /* AppCoordinator */, 6AB1363921081A53004084DC /* Features */, 6AB1363221081A53004084DC /* Plugins */, 6AAAC14520F1164E007D8CB1 /* Assets.xcassets */, 6AAAC14620F1164E007D8CB1 /* LaunchScreen.storyboard */, 6AAAC14D20F1164E007D8CB1 /* AppDelegate.swift */, 6AAAC14E20F1164E007D8CB1 /* Info.plist */, ); name = App; sourceTree = ""; }; 6AB1362F21081A53004084DC /* AppCoordinator */ = { isa = PBXGroup; children = ( 6AB1363021081A53004084DC /* AppCoordinator.swift */, 6AB1363121081A53004084DC /* Coordinator.swift */, 6A2F96DF21DF11AF00864087 /* SiriShortcutCoordinator.swift */, ); path = AppCoordinator; sourceTree = ""; }; 6AB1363221081A53004084DC /* Plugins */ = { isa = PBXGroup; children = ( 6AB1363721081A53004084DC /* Dependency Injection */, ); path = Plugins; sourceTree = ""; }; 6AB1363721081A53004084DC /* Dependency Injection */ = { isa = PBXGroup; children = ( 6AB1363821081A53004084DC /* DependencyGraph.swift */, 6A3934F221C65BD800B12F8F /* InterfaceBinding.swift */, ); path = "Dependency Injection"; sourceTree = ""; }; 6AB1363921081A53004084DC /* Features */ = { isa = PBXGroup; children = ( 6AB1363A21081A53004084DC /* Detail */, 6AB1364121081A53004084DC /* Main */, ); path = Features; sourceTree = ""; }; 6AB1363A21081A53004084DC /* Detail */ = { isa = PBXGroup; children = ( 6AB1363D21081A53004084DC /* Coordinator */, 6AB1363E21081A53004084DC /* ViewControllers */, 6AB1363B21081A53004084DC /* ViewModel */, ); path = Detail; sourceTree = ""; }; 6AB1363B21081A53004084DC /* ViewModel */ = { isa = PBXGroup; children = ( 6AB1363C21081A53004084DC /* DetailViewModel.swift */, ); path = ViewModel; sourceTree = ""; }; 6AB1363D21081A53004084DC /* Coordinator */ = { isa = PBXGroup; children = ( 6AB1365221081A6F004084DC /* DetailCoordinator.swift */, ); path = Coordinator; sourceTree = ""; }; 6AB1363E21081A53004084DC /* ViewControllers */ = { isa = PBXGroup; children = ( 6AB1363F21081A53004084DC /* DetailViewController.swift */, 6AB1364021081A53004084DC /* DetailViewController.xib */, ); path = ViewControllers; sourceTree = ""; }; 6AB1364121081A53004084DC /* Main */ = { isa = PBXGroup; children = ( 6AB1365721081A9C004084DC /* Coordinator */, 6AB1365421081A9C004084DC /* ViewControllers */, 6AB1365821081A9C004084DC /* ViewModel */, ); path = Main; sourceTree = ""; }; 6AB1365421081A9C004084DC /* ViewControllers */ = { isa = PBXGroup; children = ( 6AB1365521081A9C004084DC /* MainViewController.xib */, 6AB1365621081A9C004084DC /* MainViewController.swift */, ); path = ViewControllers; sourceTree = ""; }; 6AB1365721081A9C004084DC /* Coordinator */ = { isa = PBXGroup; children = ( 6AB1365D21081B39004084DC /* MainCoordinator.swift */, ); path = Coordinator; sourceTree = ""; }; 6AB1365821081A9C004084DC /* ViewModel */ = { isa = PBXGroup; children = ( 6AB1365B21081AB1004084DC /* MainViewModel.swift */, ); path = ViewModel; sourceTree = ""; }; 6AFDC44521DEDF7E00B9BB46 /* AppIntents */ = { isa = PBXGroup; children = ( 6A2F96D821DEFE4500864087 /* AppIntents.entitlements */, 6AFDC44621DEDF7E00B9BB46 /* IntentHandler.swift */, 6AFDC44821DEDF7E00B9BB46 /* Info.plist */, ); path = AppIntents; sourceTree = ""; }; 6AFDC45021DEDF7E00B9BB46 /* AppIntentsUI */ = { isa = PBXGroup; children = ( 6A2F96DE21DF0E1F00864087 /* AppIntentsUI.entitlements */, 6AFDC45121DEDF7E00B9BB46 /* IntentViewController.swift */, 6AFDC45321DEDF7E00B9BB46 /* MainInterface.storyboard */, 6AFDC45621DEDF7E00B9BB46 /* Info.plist */, ); path = AppIntentsUI; sourceTree = ""; }; 6AFDC47121DEEB4D00B9BB46 /* ExtensionIntents */ = { isa = PBXGroup; children = ( 6AFDC47221DEEB4D00B9BB46 /* ExtensionIntents.h */, 6AFDC48F21DEEC6600B9BB46 /* Intents.intentdefinition */, 6AFDC47321DEEB4D00B9BB46 /* Info.plist */, 6AFDC48E21DEEC6600B9BB46 /* CreateProductIntentHandler.swift */, 6A2F96D621DEFA1200864087 /* InterfaceBinding.swift */, 6AFDC48D21DEEC6600B9BB46 /* NSUserActivity+IntentData.swift */, ); path = ExtensionIntents; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 6AFDC46B21DEEB4D00B9BB46 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 6AFDC48221DEEB4D00B9BB46 /* ExtensionIntents.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 6A26FB6C20F0E54400CDA761 /* App */ = { isa = PBXNativeTarget; buildConfigurationList = 6A26FB8A20F0E54500CDA761 /* Build configuration list for PBXNativeTarget "App" */; buildPhases = ( 6A26FB6920F0E54400CDA761 /* Sources */, 6A26FB6A20F0E54400CDA761 /* Frameworks */, 6A26FB6B20F0E54400CDA761 /* Resources */, 6AFDC46321DEDF7E00B9BB46 /* Embed App Extensions */, 6AFDC4A921DEF2AD00B9BB46 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 6AFDC45821DEDF7E00B9BB46 /* PBXTargetDependency */, 6AFDC45B21DEDF7E00B9BB46 /* PBXTargetDependency */, ); name = App; packageProductDependencies = ( 6AAD2E1B240FD669000E6B9A /* RxRelay */, 6AAD2E1D240FD669000E6B9A /* RxCocoa */, 6AAD2E21240FD999000E6B9A /* Entities */, 6AAD2E23240FD9EF000E6B9A /* Data */, 6AAD2E25240FDAE1000E6B9A /* Domain */, 6AAD2E27240FDBBC000E6B9A /* NetworkServices */, 6AAD2E29240FDBC0000E6B9A /* Persistence */, 6AAD2E35240FE320000E6B9A /* RxSwift */, 6A1DF8A4241E4B54003A584E /* DependencyContainer */, ); productName = Project; productReference = 6AAAC12320F11572007D8CB1 /* App.app */; productType = "com.apple.product-type.application"; }; 6A26FB8020F0E54500CDA761 /* AppTests */ = { isa = PBXNativeTarget; buildConfigurationList = 6A26FB8D20F0E54500CDA761 /* Build configuration list for PBXNativeTarget "AppTests" */; buildPhases = ( 6A26FB7D20F0E54500CDA761 /* Sources */, 6A26FB7E20F0E54500CDA761 /* Frameworks */, 6A26FB7F20F0E54500CDA761 /* Resources */, ); buildRules = ( ); dependencies = ( 6A26FB8320F0E54500CDA761 /* PBXTargetDependency */, ); name = AppTests; packageProductDependencies = ( 6AAD2E19240FD669000E6B9A /* RxTest */, 6AAD2E1F240FD669000E6B9A /* RxBlocking */, ); productName = ProjectTests; productReference = 6AAD2E3E240FE98F000E6B9A /* AppTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 6AFDC44321DEDF7E00B9BB46 /* AppIntents */ = { isa = PBXNativeTarget; buildConfigurationList = 6AFDC46021DEDF7E00B9BB46 /* Build configuration list for PBXNativeTarget "AppIntents" */; buildPhases = ( 6AFDC44021DEDF7E00B9BB46 /* Sources */, 6AFDC44121DEDF7E00B9BB46 /* Frameworks */, 6AFDC44221DEDF7E00B9BB46 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = AppIntents; productName = AppIntents; productReference = 6AFDC44421DEDF7E00B9BB46 /* AppIntents.appex */; productType = "com.apple.product-type.app-extension"; }; 6AFDC44C21DEDF7E00B9BB46 /* AppIntentsUI */ = { isa = PBXNativeTarget; buildConfigurationList = 6AFDC45D21DEDF7E00B9BB46 /* Build configuration list for PBXNativeTarget "AppIntentsUI" */; buildPhases = ( 6AFDC44921DEDF7E00B9BB46 /* Sources */, 6AFDC44A21DEDF7E00B9BB46 /* Frameworks */, 6AFDC44B21DEDF7E00B9BB46 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = AppIntentsUI; productName = AppIntentsUI; productReference = 6AFDC44D21DEDF7E00B9BB46 /* AppIntentsUI.appex */; productType = "com.apple.product-type.app-extension"; }; 6AFDC46F21DEEB4D00B9BB46 /* ExtensionIntents */ = { isa = PBXNativeTarget; buildConfigurationList = 6AFDC48721DEEB4D00B9BB46 /* Build configuration list for PBXNativeTarget "ExtensionIntents" */; buildPhases = ( 6AFDC46B21DEEB4D00B9BB46 /* Headers */, 6AFDC46C21DEEB4D00B9BB46 /* Sources */, 6AFDC46D21DEEB4D00B9BB46 /* Frameworks */, 6AFDC46E21DEEB4D00B9BB46 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = ExtensionIntents; packageProductDependencies = ( 6AAD2E2B240FDBF6000E6B9A /* Data */, 6AAD2E2D240FDBF6000E6B9A /* Entities */, 6AAD2E2F240FDBF6000E6B9A /* NetworkServices */, 6AAD2E31240FDBF6000E6B9A /* Persistence */, 6AAD2E33240FDBF6000E6B9A /* RxSwift */, ); productName = ExtensionIntents; productReference = 6A74424B21DEF45900F865FB /* ExtensionIntents.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 6A26FB6520F0E54400CDA761 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1010; LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Cassius Pacheco"; TargetAttributes = { 6A26FB6C20F0E54400CDA761 = { CreatedOnToolsVersion = 9.3; LastSwiftMigration = 1130; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { enabled = 1; }; com.apple.Siri = { enabled = 1; }; }; }; 6A26FB8020F0E54500CDA761 = { CreatedOnToolsVersion = 9.3; LastSwiftMigration = 1130; }; 6AFDC44321DEDF7E00B9BB46 = { CreatedOnToolsVersion = 10.1; LastSwiftMigration = 1130; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { enabled = 1; }; }; }; 6AFDC44C21DEDF7E00B9BB46 = { CreatedOnToolsVersion = 10.1; LastSwiftMigration = 1130; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { enabled = 1; }; }; }; 6AFDC46F21DEEB4D00B9BB46 = { CreatedOnToolsVersion = 10.1; LastSwiftMigration = 1130; }; }; }; buildConfigurationList = 6A26FB6820F0E54400CDA761 /* Build configuration list for PBXProject "App" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 6A26FB6420F0E54400CDA761; packageReferences = ( 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */, 6A1DF8A3241E4B53003A584E /* XCRemoteSwiftPackageReference "DependencyContainer" */, ); productRefGroup = 6A26FB6420F0E54400CDA761; projectDirPath = ""; projectRoot = ""; targets = ( 6A26FB6C20F0E54400CDA761 /* App */, 6A26FB8020F0E54500CDA761 /* AppTests */, 6AFDC44321DEDF7E00B9BB46 /* AppIntents */, 6AFDC44C21DEDF7E00B9BB46 /* AppIntentsUI */, 6AFDC46F21DEEB4D00B9BB46 /* ExtensionIntents */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 6A26FB6B20F0E54400CDA761 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 6AAAC15820F1164E007D8CB1 /* LaunchScreen.storyboard in Resources */, 6AB1364E21081A53004084DC /* DetailViewController.xib in Resources */, 6AAAC15720F1164E007D8CB1 /* Assets.xcassets in Resources */, 6AB1365921081A9C004084DC /* MainViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6A26FB7F20F0E54500CDA761 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC44221DEDF7E00B9BB46 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC44B21DEDF7E00B9BB46 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 6AFDC45521DEDF7E00B9BB46 /* MainInterface.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC46E21DEEB4D00B9BB46 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 6A26FB6920F0E54400CDA761 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 6AB1364721081A53004084DC /* AppCoordinator.swift in Sources */, 6AB1365E21081B39004084DC /* MainCoordinator.swift in Sources */, 6AB1364C21081A53004084DC /* DetailViewModel.swift in Sources */, 6A2F96E021DF11AF00864087 /* SiriShortcutCoordinator.swift in Sources */, 6AB1365321081A6F004084DC /* DetailCoordinator.swift in Sources */, 6AAD2E3D240FE5C5000E6B9A /* InterfaceBinding.swift in Sources */, 6AB1364B21081A53004084DC /* DependencyGraph.swift in Sources */, 6AFDC49321DEEC6E00B9BB46 /* Intents.intentdefinition in Sources */, 6AAAC15D20F1164E007D8CB1 /* AppDelegate.swift in Sources */, 6AB1364821081A53004084DC /* Coordinator.swift in Sources */, 6AB1364D21081A53004084DC /* DetailViewController.swift in Sources */, 6AB1365A21081A9C004084DC /* MainViewController.swift in Sources */, 6AB1365C21081AB1004084DC /* MainViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6A26FB7D20F0E54500CDA761 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 6ACB875820F116DD005A04A7 /* AppTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC44021DEDF7E00B9BB46 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 6AFDC49421DEEC7200B9BB46 /* Intents.intentdefinition in Sources */, 6AFDC44721DEDF7E00B9BB46 /* IntentHandler.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC44921DEDF7E00B9BB46 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 6AFDC49521DEEC7200B9BB46 /* Intents.intentdefinition in Sources */, 6AFDC45221DEDF7E00B9BB46 /* IntentViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6AFDC46C21DEEB4D00B9BB46 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 6AFDC49221DEEC6600B9BB46 /* Intents.intentdefinition in Sources */, 6A2F96D721DEFA1200864087 /* InterfaceBinding.swift in Sources */, 6AFDC49121DEEC6600B9BB46 /* CreateProductIntentHandler.swift in Sources */, 6AFDC49021DEEC6600B9BB46 /* NSUserActivity+IntentData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 6A26FB8320F0E54500CDA761 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6A26FB6C20F0E54400CDA761 /* App */; targetProxy = 6A26FB8220F0E54500CDA761 /* PBXContainerItemProxy */; }; 6AFDC45821DEDF7E00B9BB46 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6AFDC44C21DEDF7E00B9BB46 /* AppIntentsUI */; targetProxy = 6AFDC45721DEDF7E00B9BB46 /* PBXContainerItemProxy */; }; 6AFDC45B21DEDF7E00B9BB46 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6AFDC44321DEDF7E00B9BB46 /* AppIntents */; targetProxy = 6AFDC45A21DEDF7E00B9BB46 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 6AAAC14620F1164E007D8CB1 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 6AAAC14720F1164E007D8CB1 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; 6AFDC45321DEDF7E00B9BB46 /* MainInterface.storyboard */ = { isa = PBXVariantGroup; children = ( 6AFDC45421DEDF7E00B9BB46 /* Base */, ); name = MainInterface.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 6A26FB8820F0E54500CDA761 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 6A26FB8920F0E54500CDA761 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 6A26FB8B20F0E54500CDA761 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = App.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2LT888MY4H; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.App; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 6A26FB8C20F0E54500CDA761 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = App.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2LT888MY4H; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.App; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 6A26FB8E20F0E54500CDA761 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 2LT888MY4H; INFOPLIST_FILE = "$(SRCROOT)/../AppTests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.AppTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 6A26FB8F20F0E54500CDA761 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = 2LT888MY4H; INFOPLIST_FILE = "$(SRCROOT)/../AppTests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.AppTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 6AFDC45E21DEDF7E00B9BB46 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = AppIntentsUI/AppIntentsUI.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2LT888MY4H; INFOPLIST_FILE = AppIntentsUI/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.App.AppIntentsUI; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 6AFDC45F21DEDF7E00B9BB46 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = AppIntentsUI/AppIntentsUI.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2LT888MY4H; INFOPLIST_FILE = AppIntentsUI/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.App.AppIntentsUI; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 6AFDC46121DEDF7E00B9BB46 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = AppIntents/AppIntents.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2LT888MY4H; INFOPLIST_FILE = AppIntents/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.App.AppIntents; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 6AFDC46221DEDF7E00B9BB46 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = AppIntents/AppIntents.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2LT888MY4H; INFOPLIST_FILE = AppIntents/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.App.AppIntents; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 6AFDC48821DEEB4D00B9BB46 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 2LT888MY4H; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = ExtensionIntents/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.ExtensionIntents; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 6AFDC48921DEEB4D00B9BB46 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 2LT888MY4H; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = ExtensionIntents/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.cassiuspacheco.ExtensionIntents; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 6A26FB6820F0E54400CDA761 /* Build configuration list for PBXProject "App" */ = { isa = XCConfigurationList; buildConfigurations = ( 6A26FB8820F0E54500CDA761 /* Debug */, 6A26FB8920F0E54500CDA761 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6A26FB8A20F0E54500CDA761 /* Build configuration list for PBXNativeTarget "App" */ = { isa = XCConfigurationList; buildConfigurations = ( 6A26FB8B20F0E54500CDA761 /* Debug */, 6A26FB8C20F0E54500CDA761 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6A26FB8D20F0E54500CDA761 /* Build configuration list for PBXNativeTarget "AppTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 6A26FB8E20F0E54500CDA761 /* Debug */, 6A26FB8F20F0E54500CDA761 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6AFDC45D21DEDF7E00B9BB46 /* Build configuration list for PBXNativeTarget "AppIntentsUI" */ = { isa = XCConfigurationList; buildConfigurations = ( 6AFDC45E21DEDF7E00B9BB46 /* Debug */, 6AFDC45F21DEDF7E00B9BB46 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6AFDC46021DEDF7E00B9BB46 /* Build configuration list for PBXNativeTarget "AppIntents" */ = { isa = XCConfigurationList; buildConfigurations = ( 6AFDC46121DEDF7E00B9BB46 /* Debug */, 6AFDC46221DEDF7E00B9BB46 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 6AFDC48721DEEB4D00B9BB46 /* Build configuration list for PBXNativeTarget "ExtensionIntents" */ = { isa = XCConfigurationList; buildConfigurations = ( 6AFDC48821DEEB4D00B9BB46 /* Debug */, 6AFDC48921DEEB4D00B9BB46 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ 6A1DF8A3241E4B53003A584E /* XCRemoteSwiftPackageReference "DependencyContainer" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/CassiusPacheco/DependencyContainer"; requirement = { kind = exactVersion; version = 0.1.0; }; }; 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { kind = exactVersion; version = 5.1.0; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 6A1DF8A4241E4B54003A584E /* DependencyContainer */ = { isa = XCSwiftPackageProductDependency; package = 6A1DF8A3241E4B53003A584E /* XCRemoteSwiftPackageReference "DependencyContainer" */; productName = DependencyContainer; }; 6AAD2E19240FD669000E6B9A /* RxTest */ = { isa = XCSwiftPackageProductDependency; package = 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxTest; }; 6AAD2E1B240FD669000E6B9A /* RxRelay */ = { isa = XCSwiftPackageProductDependency; package = 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxRelay; }; 6AAD2E1D240FD669000E6B9A /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxCocoa; }; 6AAD2E1F240FD669000E6B9A /* RxBlocking */ = { isa = XCSwiftPackageProductDependency; package = 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxBlocking; }; 6AAD2E21240FD999000E6B9A /* Entities */ = { isa = XCSwiftPackageProductDependency; productName = Entities; }; 6AAD2E23240FD9EF000E6B9A /* Data */ = { isa = XCSwiftPackageProductDependency; productName = Data; }; 6AAD2E25240FDAE1000E6B9A /* Domain */ = { isa = XCSwiftPackageProductDependency; productName = Domain; }; 6AAD2E27240FDBBC000E6B9A /* NetworkServices */ = { isa = XCSwiftPackageProductDependency; productName = NetworkServices; }; 6AAD2E29240FDBC0000E6B9A /* Persistence */ = { isa = XCSwiftPackageProductDependency; productName = Persistence; }; 6AAD2E2B240FDBF6000E6B9A /* Data */ = { isa = XCSwiftPackageProductDependency; productName = Data; }; 6AAD2E2D240FDBF6000E6B9A /* Entities */ = { isa = XCSwiftPackageProductDependency; productName = Entities; }; 6AAD2E2F240FDBF6000E6B9A /* NetworkServices */ = { isa = XCSwiftPackageProductDependency; productName = NetworkServices; }; 6AAD2E31240FDBF6000E6B9A /* Persistence */ = { isa = XCSwiftPackageProductDependency; productName = Persistence; }; 6AAD2E33240FDBF6000E6B9A /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; 6AAD2E35240FE320000E6B9A /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 6AAD2E16240FD669000E6B9A /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 6A26FB6520F0E54400CDA761 /* Project object */; } ================================================ FILE: App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme ================================================ ================================================ FILE: App/App.xcodeproj/xcshareddata/xcschemes/AppIntents.xcscheme ================================================ ================================================ FILE: App/App.xcodeproj/xcshareddata/xcschemes/AppIntentsUI.xcscheme ================================================ ================================================ FILE: App/AppCoordinator/AppCoordinator.swift ================================================ // // AppCoordinator.swift // Project // // Created by Cassius Pacheco on 8/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import UIKit import DependencyContainer protocol AppCoordinatorInterface: Coordinator { var siriShortcutCoordinator: SiriShortcutCoordinatorInterface { get } var window: UIWindow! { get set } func pushDetailViewController() } enum AppChild { case main } class AppCoordinator: AppCoordinatorInterface { private(set) lazy var siriShortcutCoordinator: SiriShortcutCoordinatorInterface = SiriShortcutCoordinator(navigationController: navigationController) private(set) var navigationController: UINavigationController! private let container: DependencyContainer var children = [AppChild: Coordinator]() var window: UIWindow! init(container: DependencyContainer) { self.container = container } func start() { // The rootVC needs to be initialised manually let mainCoordinator = container.resolve(MainCoordinatorInterface.self) var viewModel = container.resolve(MainViewModelInterface.self) viewModel.coordinator = mainCoordinator children[.main] = mainCoordinator let mainViewController = MainViewController(viewModel: viewModel) navigationController = UINavigationController(rootViewController: mainViewController) mainCoordinator.navigationController = navigationController window.rootViewController = navigationController window.makeKeyAndVisible() } func pushDetailViewController() { let isDetailVisible = navigationController.viewControllers.contains(where: { controller -> Bool in return controller is DetailViewController }) guard let mainCoordinator = children[.main] as? MainCoordinatorInterface, !isDetailVisible else { return } mainCoordinator.pushDetailViewController() } } ================================================ FILE: App/AppCoordinator/Coordinator.swift ================================================ // // Coordinator.swift // Project // // Created by Cassius Pacheco on 8/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import UIKit protocol Coordinator { func start() } ================================================ FILE: App/AppCoordinator/SiriShortcutCoordinator.swift ================================================ // // SiriShortcutCoordinator.swift // App // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // import Foundation import IntentsUI import os.log // This coordinator is a bit different of the others because an instance of this coordinator will be kept in the AppCoordinator // to handle all SiriShortcuts presentation/dismissal methods and also handle the Shortcut button delegate calls. protocol SiriShortcutCoordinatorInterface: Coordinator, INUIAddVoiceShortcutButtonDelegate {} final class SiriShortcutCoordinator: NSObject, SiriShortcutCoordinatorInterface { let navigationController: UINavigationController init(navigationController: UINavigationController) { self.navigationController = navigationController } func start() {} } extension SiriShortcutCoordinator: INUIAddVoiceShortcutButtonDelegate { func present(_ addVoiceShortcutViewController: INUIAddVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) { addVoiceShortcutViewController.delegate = self navigationController.present(addVoiceShortcutViewController, animated: true, completion: nil) } func present(_ editVoiceShortcutViewController: INUIEditVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) { editVoiceShortcutViewController.delegate = self navigationController.present(editVoiceShortcutViewController, animated: true, completion: nil) } } extension SiriShortcutCoordinator: INUIAddVoiceShortcutViewControllerDelegate { func addVoiceShortcutViewController(_ controller: INUIAddVoiceShortcutViewController, didFinishWith voiceShortcut: INVoiceShortcut?, error: Error?) { if let error = error as NSError? { os_log("Error adding voice shortcut: %@", log: OSLog.default, type: .error, error) } controller.dismiss(animated: true, completion: nil) } func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) { controller.dismiss(animated: true, completion: nil) } } extension SiriShortcutCoordinator: INUIEditVoiceShortcutViewControllerDelegate { func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didUpdate voiceShortcut: INVoiceShortcut?, error: Error?) { if let error = error as NSError? { os_log("Error adding voice shortcut: %@", log: OSLog.default, type: .error, error) } controller.dismiss(animated: true, completion: nil) } func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID) { controller.dismiss(animated: true, completion: nil) } func editVoiceShortcutViewControllerDidCancel(_ controller: INUIEditVoiceShortcutViewController) { controller.dismiss(animated: true, completion: nil) } } ================================================ FILE: App/AppDelegate.swift ================================================ // // AppDelegate.swift // Project // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import UIKit import DependencyContainer import ExtensionIntents import os.log @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private lazy var dependencyGraph = DependencyGraph(container: DependencyContainer()) var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let mainWindow = UIWindow(frame: UIScreen.main.bounds) self.window = mainWindow dependencyGraph.setupWithMainWindow(mainWindow) var coordinator = dependencyGraph.container.resolve(AppCoordinatorInterface.self) coordinator.window = window coordinator.start() return true } func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivity.createProductActivityType || userActivity.activityType == NSStringFromClass(CreateProductIntent.self) else { return false } let coordinator = dependencyGraph.container.resolve(AppCoordinatorInterface.self) coordinator.pushDetailViewController() return true } } ================================================ FILE: App/AppIntents/AppIntents.entitlements ================================================ com.apple.security.application-groups group.com.cassiuspacheco.Swift-CleanArchitecture ================================================ FILE: App/AppIntents/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName AppIntents CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType XPC! CFBundleShortVersionString 1.0 CFBundleVersion 1 NSExtension NSExtensionAttributes IntentsRestrictedWhileLocked IntentsRestrictedWhileProtectedDataUnavailable IntentsSupported CreateProductIntent NSExtensionPointIdentifier com.apple.intents-service NSExtensionPrincipalClass $(PRODUCT_MODULE_NAME).IntentHandler ================================================ FILE: App/AppIntents/IntentHandler.swift ================================================ // // IntentHandler.swift // AppIntents // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // import Intents import ExtensionIntents class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any { guard intent is CreateProductIntent else { fatalError("Unhandled intent type: \(intent)") } return CreateProductIntentHandler() } } ================================================ FILE: App/AppIntentsUI/AppIntentsUI.entitlements ================================================ com.apple.security.application-groups group.com.cassiuspacheco.Swift-CleanArchitecture ================================================ FILE: App/AppIntentsUI/Base.lproj/MainInterface.storyboard ================================================ ================================================ FILE: App/AppIntentsUI/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName AppIntentsUI CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType XPC! CFBundleShortVersionString 1.0 CFBundleVersion 1 NSExtension NSExtensionAttributes IntentsSupported CreateProductIntent INSendMessageIntent NSExtensionMainStoryboard MainInterface NSExtensionPointIdentifier com.apple.intents-ui-service ================================================ FILE: App/AppIntentsUI/IntentViewController.swift ================================================ // // IntentViewController.swift // AppIntentsUI // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // import IntentsUI // As an example, this extension's Info.plist has been configured to handle interactions for INSendMessageIntent. // You will want to replace this or add other intents as appropriate. // The intents whose interactions you wish to handle must be declared in the extension's Info.plist. // You can test this example integration by saying things to Siri like: // "Send a message using " class IntentViewController: UIViewController, INUIHostedViewControlling { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } // MARK: - INUIHostedViewControlling // Prepare your view controller for the interaction to handle. func configureView(for parameters: Set, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set, CGSize) -> Void) { // Do configuration here, including preparing views and calculating a desired size for presentation. completion(true, parameters, self.desiredSize) } var desiredSize: CGSize { return self.extensionContext!.hostedViewMaximumAllowedSize } } ================================================ FILE: App/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: App/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: App/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: App/ExtensionIntents/CreateProductIntentHandler.swift ================================================ // // CreateProductIntentHandler.swift // ExtensionIntents // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // import Foundation import Entities import Data import Persistence import NetworkServices public final class CreateProductIntentHandler: NSObject, CreateProductIntentHandling { public func handle(intent: CreateProductIntent, completion: @escaping (CreateProductIntentResponse) -> Void) { guard let productName = intent.name else { completion(CreateProductIntentResponse(code: .failure, userActivity: nil)) return } let product = Product(name: productName) let persistence = Persistence(defaults: UserDefaults(suiteName: Persistence.appGroup)!) let productRepository = ProductRepository(cache: persistence, service: ProductService()) productRepository.create(product) let userActivity = NSUserActivity(activityType: NSUserActivity.createProductActivityType) userActivity.addUserInfoEntries(from: [NSUserActivity.ActivityKeys.productName.rawValue: product.name]) let response = CreateProductIntentResponse.success(name: productName) response.userActivity = userActivity completion(response) } } ================================================ FILE: App/ExtensionIntents/ExtensionIntents.h ================================================ // // ExtensionIntents.h // ExtensionIntents // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // #import //! Project version number for ExtensionIntents. FOUNDATION_EXPORT double ExtensionIntentsVersionNumber; //! Project version string for ExtensionIntents. FOUNDATION_EXPORT const unsigned char ExtensionIntentsVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: App/ExtensionIntents/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) ================================================ FILE: App/ExtensionIntents/Intents.intentdefinition ================================================ INEnums INIntentDefinitionModelVersion 1.0 INIntentDefinitionSystemVersion 18C54 INIntentDefinitionToolsBuildVersion 10B61 INIntentDefinitionToolsVersion 10.1 INIntents INIntentCategory create INIntentDescription Create a product INIntentDescriptionID SDCFif INIntentLastParameterTag 1 INIntentName CreateProduct INIntentParameterCombinations name INIntentParameterCombinationIsPrimary INIntentParameterCombinationSubtitle INIntentParameterCombinationSubtitleID 83xzAk INIntentParameterCombinationSupportsBackgroundExecution INIntentParameterCombinationTitle Create product ${name} INIntentParameterCombinationTitleID 22je0c INIntentParameters INIntentParameterDisplayPriority 1 INIntentParameterName name INIntentParameterSupportsMultipleValues INIntentParameterTag 1 INIntentParameterType String INIntentResponse INIntentResponseCodes INIntentResponseCodeFormatString INIntentResponseCodeFormatStringID k1fRYc INIntentResponseCodeName failure INIntentResponseCodeSuccess INIntentResponseCodeFormatString ${name} has been created. INIntentResponseCodeFormatStringID bEcv5p INIntentResponseCodeName success INIntentResponseCodeSuccess INIntentResponseLastParameterTag 1 INIntentResponseParameters INIntentResponseParameterDisplayPriority 1 INIntentResponseParameterName name INIntentResponseParameterSupportsMultipleValues INIntentResponseParameterTag 1 INIntentResponseParameterType String INIntentRestrictions 0 INIntentTitle Create Product INIntentTitleID WZoO6d INIntentType Custom INIntentUserConfirmationRequired INIntentVerb Create ================================================ FILE: App/ExtensionIntents/InterfaceBinding.swift ================================================ // // InterfaceBinding.swift // ExtensionIntents // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // import Foundation import Data import Persistence import NetworkServices // This is a separated file from App's because this usage is much simpler. // Note that the usage of the DependencyContainer framework is optional for // this framework since it shouldn't do much and shouldn't perform many operations. extension Persistence: CacheInterface {} extension UserDefaults: UserDefaultsInterface {} extension ProductService: ProductServiceInterface {} ================================================ FILE: App/ExtensionIntents/NSUserActivity+IntentData.swift ================================================ // // NSUserActivity+IntentData.swift // ExtensionIntents // // Created by Cassius Pacheco on 4/1/19. // Copyright © 2019 Cassius Pacheco. All rights reserved. // import Foundation import MobileCoreServices #if canImport(CoreSpotlight) import CoreSpotlight import UIKit #endif extension NSUserActivity { public enum ActivityKeys: String { case productName } public static let createProductActivityType = "com.cassiuspacheco.ExtensionIntents.createProduct" public static var createProductActivity: NSUserActivity { let userActivity = NSUserActivity(activityType: NSUserActivity.createProductActivityType) userActivity.title = "Create a product" userActivity.isEligibleForPrediction = true #if canImport(CoreSpotlight) let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeContent as String) attributes.keywords = ["Create", "Product"] attributes.displayName = "Create a product" attributes.contentDescription = "Create a product" userActivity.contentAttributeSet = attributes #endif userActivity.suggestedInvocationPhrase = "Create a product" return userActivity } } ================================================ FILE: App/Features/Detail/Coordinator/DetailCoordinator.swift ================================================ // // DetailCoordinator.swift // App // // Created by Cassius Pacheco on 25/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import UIKit import Foundation import DependencyContainer protocol DetailCoordinatorInterface: Coordinator, DetailCoordinatorDelegate { var navigationController: UINavigationController? { get set } } final class DetailCoordinator: DetailCoordinatorInterface { let container: DependencyContainer var navigationController: UINavigationController? init(container: DependencyContainer) { self.container = container } func start() { var viewModel = container.resolve(DetailViewModelInterface.self) let viewController = DetailViewController(viewModel: viewModel) viewModel.coordinator = self if let navigation = navigationController { navigation.pushViewController(viewController, animated: true) } else { // You can either handle the dismissal here or forward the event to the viewModel // or even handle the button creation in the viewController itself. let dismissButton = UIBarButtonItem(title: "Dismiss", style: .plain, target: self, action: #selector(dismissButtonTapped)) let navigation = UINavigationController(rootViewController: viewController) viewController.navigationItem.leftBarButtonItem = dismissButton navigationController = navigation let window = container.resolve(UIWindow.self) window.rootViewController?.present(navigation, animated: true) } } @objc private func dismissButtonTapped() { navigationController?.dismiss(animated: true) } } extension DetailCoordinator { // MARK: - DetailCoordinatorDelegate methods var siriShortcutCoordinator: SiriShortcutCoordinatorInterface { return container.resolve(AppCoordinatorInterface.self).siriShortcutCoordinator } } ================================================ FILE: App/Features/Detail/ViewControllers/DetailViewController.swift ================================================ // // DetailViewController.swift // DependencyContainer // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import UIKit import IntentsUI import ExtensionIntents final class DetailViewController: UIViewController { @IBOutlet private var textField: UITextField! private var viewModel: DetailViewModelInterface // MARK: - Init methods init(viewModel: DetailViewModelInterface) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) self.viewModel.delegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { print("DetailViewController has been deallocated") } // MARK: - View life cycle override func viewDidLoad() { super.viewDidLoad() createAddToSiriButton() textField.addTarget(self, action: #selector(textFieldCharacterDidChange), for: .editingChanged) } // MARK: - Action @IBAction private func createButtonTouchUpInside() { viewModel.createButtonTouchUpInside() } @IBAction private func printAllButtonTouchUpInside() { viewModel.printAllButtonTouchUpInside() } @objc private func textFieldCharacterDidChange() { viewModel.textFieldInput = textField.text ?? "" } private func createAddToSiriButton() { // Note that this is a useless intent. The idea is just to show the concept working. let intent = CreateProductIntent() intent.name = "Test" let shortcutButton = INUIAddVoiceShortcutButton(style: .whiteOutline) shortcutButton.shortcut = INShortcut(intent: intent) shortcutButton.delegate = viewModel.shortcutButtonDelegate navigationItem.setRightBarButton(UIBarButtonItem(customView: shortcutButton), animated: true) } } extension DetailViewController: DetailViewModelDelegate { func productCreated() { textField.text = nil } } ================================================ FILE: App/Features/Detail/ViewControllers/DetailViewController.xib ================================================ ================================================ FILE: App/Features/Detail/ViewModel/DetailViewModel.swift ================================================ // // DetailViewModel.swift // Project // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Domain protocol DetailCoordinatorDelegate: class { // Define other routes from the detail screen. // for example: func presentCreationConfirmationModal() // The Siri Shortcut coordinator should be passed along to be used as the button's delegate. var siriShortcutCoordinator: SiriShortcutCoordinatorInterface { get } } protocol DetailViewModelInterface { var coordinator: DetailCoordinatorDelegate? { get set } var delegate: DetailViewModelDelegate? { get set } var textFieldInput: String { get set } var shortcutButtonDelegate: SiriShortcutCoordinatorInterface? { get } func createButtonTouchUpInside() func printAllButtonTouchUpInside() } protocol DetailViewModelDelegate: NSObjectProtocol { func productCreated() } final class DetailViewModel: DetailViewModelInterface { private let productsUseCase: ProductsUseCaseInterface private let createProductUseCase: CreateProductUseCaseInterface weak var coordinator: DetailCoordinatorDelegate? weak var delegate: DetailViewModelDelegate? var textFieldInput: String = "" var shortcutButtonDelegate: SiriShortcutCoordinatorInterface? { return coordinator?.siriShortcutCoordinator } init(productsUseCase: ProductsUseCaseInterface, createProductUseCase: CreateProductUseCaseInterface) { self.productsUseCase = productsUseCase self.createProductUseCase = createProductUseCase } func createButtonTouchUpInside() { guard !textFieldInput.isEmpty else { return } createProductUseCase.execute(textFieldInput) delegate?.productCreated() } func printAllButtonTouchUpInside() { print("\n--Printing all:\n") productsUseCase.execute().forEach { (product) in print(product.name) } print("\n--") } } ================================================ FILE: App/Features/Main/Coordinator/MainCoordinator.swift ================================================ // // MainCoordinator.swift // App // // Created by Cassius Pacheco on 25/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import UIKit import Foundation import DependencyContainer protocol MainCoordinatorInterface: Coordinator, MainCoordinatorDelegate { var navigationController: UINavigationController? { get set } } enum MainChild { case detail } final class MainCoordinator: MainCoordinatorInterface { let container: DependencyContainer var children = [MainChild: Coordinator]() var navigationController: UINavigationController? init(container: DependencyContainer) { self.container = container } func start() { // not necessary since this is the initial controller of the app } } extension MainCoordinator { func pushDetailViewController() { // if a `navigationController` isn't assigned to the coordinator it will create // one and present the screen as a modal let detailCoordinator = self.container.resolve(DetailCoordinatorInterface.self) detailCoordinator.navigationController = self.navigationController self.children[.detail] = detailCoordinator detailCoordinator.start() } } ================================================ FILE: App/Features/Main/ViewControllers/MainViewController.swift ================================================ // // MainViewController.swift // DependencyContainer // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import UIKit import DependencyContainer final class MainViewController: UIViewController { let viewModel: MainViewModelInterface // MARK: - Init methods init(viewModel: MainViewModelInterface) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Action methods @IBAction private func detailViewControllerButtonTouchUpInside() { viewModel.detailViewControllerButtonTouchUpInside() } } ================================================ FILE: App/Features/Main/ViewControllers/MainViewController.xib ================================================ ================================================ FILE: App/Features/Main/ViewModel/MainViewModel.swift ================================================ // // MainViewModel.swift // App // // Created by Cassius Pacheco on 25/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation protocol MainCoordinatorDelegate: class { func pushDetailViewController() } protocol MainViewModelInterface { var coordinator: MainCoordinatorDelegate? { get set } func detailViewControllerButtonTouchUpInside() } final class MainViewModel: MainViewModelInterface { weak var coordinator: MainCoordinatorDelegate? func detailViewControllerButtonTouchUpInside() { coordinator?.pushDetailViewController() } } ================================================ FILE: App/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS NSUserActivityTypes CreateProductIntent com.cassiuspacheco.ExtensionIntents.createProduct UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: App/Plugins/Dependency Injection/DependencyGraph.swift ================================================ // // DependencyGraph.swift // Project // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import UIKit import Foundation import DependencyContainer import Entities import Domain import Data import Persistence import NetworkServices final class DependencyGraph { let container: DependencyContainer init(container: DependencyContainer) { self.container = container } func setupWithMainWindow(_ window: UIWindow) { setupAppDelegate(window) setupCoordinators() setupServices() setupPersistence() setupRepositories() setupUseCases() setupViewModels() } private func setupAppDelegate(_ window: UIWindow) { container.registerSingleton(UIWindow.self) { di in return window } } private func setupCoordinators() { container.registerSingleton(AppCoordinatorInterface.self) { di in return AppCoordinator(container: di) } container.register(MainCoordinatorInterface.self) { di in return MainCoordinator(container: di) } container.register(DetailCoordinatorInterface.self) { di in return DetailCoordinator(container: di) } } private func setupServices() { container.register(ProductServiceInterface.self) { di in return ProductService() } } private func setupPersistence() { container.registerSingleton(CacheInterface.self) { _ in return Persistence(defaults: UserDefaults(suiteName: Persistence.appGroup)!) } } private func setupRepositories() { container.register(ProductRepositoryInterface.self) { di in return ProductRepository(cache: di.resolve(CacheInterface.self), service: di.resolve(ProductServiceInterface.self)) } } private func setupUseCases() { container.register(ProductsUseCaseInterface.self) { di in return ProductsUseCase(repository: di.resolve(ProductRepositoryInterface.self)) } container.register(CreateProductUseCaseInterface.self) { di in return CreateProductUseCase(repository: di.resolve(ProductRepositoryInterface.self)) } } private func setupViewModels() { container.register(MainViewModelInterface.self) { _ in return MainViewModel() } container.register(DetailViewModelInterface.self) { di in return DetailViewModel(productsUseCase: di.resolve(ProductsUseCaseInterface.self), createProductUseCase: di.resolve(CreateProductUseCaseInterface.self)) } } } ================================================ FILE: App/Plugins/Dependency Injection/InterfaceBinding.swift ================================================ // // InterfaceBinding.swift // App // // Created by Cassius Pacheco on 16/12/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Persistence import NetworkServices import Data import ExtensionIntents // Put all interface binding that are not contained in the `ExtensionIntents` here. ================================================ FILE: AppTests/AppTests.swift ================================================ // // AppTests.swift // AppTests // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import XCTest @testable import App class AppTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: AppTests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: Data/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Data/Package.swift ================================================ // swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Data", products: [ .library( name: "Data", targets: ["Data"]), ], dependencies: [ .package(path: "../Entities") ], targets: [ .target( name: "Data", dependencies: ["Entities"]), .testTarget( name: "DataTests", dependencies: ["Data", "Entities"]), ] ) ================================================ FILE: Data/README.md ================================================ # Data A description of this package. ================================================ FILE: Data/Sources/Data/CacheInterface.swift ================================================ // // CacheInterface.swift // Data // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation public protocol CacheInterface { func removeValue(for key: String) func save(_ value: Value?, for key: String) func value(for key: String) -> Value? func bool(for key: String) -> Bool func saveEncoded(_ value: Value?, for key: String) func decodedValue(for key: String) -> Value? } public extension CacheInterface { static var appGroup: String { return "group.com.cassiuspacheco.Swift-CleanArchitecture" } } ================================================ FILE: Data/Sources/Data/Product/ProductRepository.swift ================================================ // // ProductRepository.swift // Data // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Entities public protocol ProductRepositoryInterface { var products: [Product] { get } func create(_ product: Product) } public class ProductRepository: ProductRepositoryInterface { struct keys { static let products = "products" } private let cache: CacheInterface private let service: ProductServiceInterface public init(cache: CacheInterface, service: ProductServiceInterface) { self.cache = cache self.service = service } public var products: [Product] { return cache.decodedValue(for: keys.products) ?? [] } public func create(_ product: Product) { service.create(product) cache.saveEncoded(products + [product], for: keys.products) } } ================================================ FILE: Data/Sources/Data/Product/ProductServiceInterface.swift ================================================ // // ProductServiceInterface.swift // Data // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Entities public protocol ProductServiceInterface: ServiceInterface { // Some service code to fetch products or something func create(_ product: Product) } ================================================ FILE: Data/Sources/Data/ServiceInterface.swift ================================================ // // ServiceInterface.swift // Data // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation public protocol ServiceInterface { // Some network base methods } ================================================ FILE: Data/Tests/DataTests/DataTests.swift ================================================ import XCTest @testable import Data final class DataTests: XCTestCase {} ================================================ FILE: Domain/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Domain/Package.swift ================================================ // swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Domain", products: [ .library( name: "Domain", targets: ["Domain"]), ], dependencies: [ .package(path: "../Entities"), .package(path: "../Data") ], targets: [ .target( name: "Domain", dependencies: ["Entities", "Data"]), .testTarget( name: "DomainTests", dependencies: ["Domain", "Entities", "Data"]), ] ) ================================================ FILE: Domain/README.md ================================================ # Domain A description of this package. ================================================ FILE: Domain/Sources/Domain/Product/CreateProductUseCase.swift ================================================ // // CreateProductUseCase.swift // Domain // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Entities import Data public protocol CreateProductUseCaseInterface { @discardableResult func execute(_ input: String) -> Product } public class CreateProductUseCase: UseCase, CreateProductUseCaseInterface { private let repository: ProductRepositoryInterface public init(repository: ProductRepositoryInterface) { self.repository = repository } @discardableResult override public func execute(_ input: String) -> Product { let product = Product(name: input) repository.create(product) return product } } ================================================ FILE: Domain/Sources/Domain/Product/ProductsUseCase.swift ================================================ // // ProductsUseCase.swift // Domain // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Entities import Data public protocol ProductsUseCaseInterface { @discardableResult func execute() -> [Product] } public class ProductsUseCase: VoidUseCase<[Product]>, ProductsUseCaseInterface { private let repository: ProductRepositoryInterface public init(repository: ProductRepositoryInterface) { self.repository = repository } @discardableResult override public func execute() -> [Product] { return repository.products } } ================================================ FILE: Domain/Sources/Domain/UseCase.swift ================================================ // // UseCase.swift // Tests // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import UIKit public class VoidUseCase { @discardableResult public func execute() -> Output { fatalError("this should be overridden") } } public class UseCase { @discardableResult public func execute(_ input: Input) -> Output { fatalError("this should be overridden") } } ================================================ FILE: Domain/Tests/DomainTests/DomainTests.swift ================================================ // // DomainTests.swift // DomainTests // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import XCTest @testable import Domain class DomainTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: Entities/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Entities/Package.swift ================================================ // swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Entities", products: [ .library( name: "Entities", targets: ["Entities"]), ], dependencies: [], targets: [ .target( name: "Entities", dependencies: []), .testTarget( name: "EntitiesTests", dependencies: ["Entities"]), ] ) ================================================ FILE: Entities/README.md ================================================ # Entities A description of this package. ================================================ FILE: Entities/Sources/Entities/Product.swift ================================================ // // Product.swift // DependencyContainer // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation public struct Product: Codable { public let name: String public init(name: String) { self.name = name } } ================================================ FILE: Entities/Tests/EntitiesTests/EntitiesTests.swift ================================================ import XCTest @testable import Entities final class EntitiesTests: XCTestCase {} ================================================ FILE: Plugins/NetworkServices/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Plugins/NetworkServices/Package.swift ================================================ // swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "NetworkServices", products: [ .library( name: "NetworkServices", targets: ["NetworkServices"]), ], dependencies: [ .package(path: "../../Entities") ], targets: [ .target( name: "NetworkServices", dependencies: ["Entities"]), .testTarget( name: "NetworkServicesTests", dependencies: ["NetworkServices", "Entities"]), ] ) ================================================ FILE: Plugins/NetworkServices/README.md ================================================ # NetworkServices A description of this package. ================================================ FILE: Plugins/NetworkServices/Sources/NetworkServices/ProductService.swift ================================================ // // ProductService.swift // NetworkServices // // Created by Cassius Pacheco on 7/7/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import Entities public final class ProductService { // implement the methods defined in the protocol in the Data Layer public init() {} public func create(_ product: Product) { // hit API to create the product } } ================================================ FILE: Plugins/NetworkServices/Tests/NetworkServicesTests/NetworkServicesTests.swift ================================================ // // NetworkServicesTests.swift // NetworkServicesTests // // Created by Cassius Pacheco on 16/12/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import XCTest @testable import NetworkServices class NetworkServicesTests: XCTestCase { override func setUp() { // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: Plugins/Persistence/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ ================================================ FILE: Plugins/Persistence/Package.swift ================================================ // swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Persistence", products: [ .library( name: "Persistence", targets: ["Persistence"]), ], dependencies: [], targets: [ .target( name: "Persistence", dependencies: []), .testTarget( name: "PersistenceTests", dependencies: ["Persistence"]), ] ) ================================================ FILE: Plugins/Persistence/README.md ================================================ # Persistence A description of this package. ================================================ FILE: Plugins/Persistence/Sources/Persistence/Persistence.swift ================================================ // // Persistence.swift // Persistence // // Created by Cassius Pacheco on 16/12/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation public final class Persistence { private lazy var queue: DispatchQueue = DispatchQueue(label: "com.cassius.Persistence", qos: .userInitiated, attributes: .concurrent) private let defaults: UserDefaultsInterface public init(defaults: UserDefaultsInterface) { self.defaults = defaults } public func removeValue(for key: String) { queue.async(flags: .barrier) { self.defaults.removeObject(forKey: key) } } public func save(_ value: Value?, for key: String) { queue.async(flags: .barrier) { self.defaults.set(value, forKey: key) } } public func value(for key: String) -> Value? { var value: Value? queue.sync { // This ensures the `Any?` object returned is not `nil` // otherwise casting a nil `Any?` into something else will // return a `.some(nil)` result, which is different of `nil`. if let object = self.defaults.object(forKey: key) { value = object as? Value } } return value } public func bool(for key: String) -> Bool { var value: Bool = false queue.sync { value = self.defaults.bool(forKey: key) } return value } public func saveEncoded(_ value: Value?, for key: String) { queue.async(flags: .barrier) { let data = try? JSONEncoder().encode(value) self.defaults.set(data, forKey: key) } } public func decodedValue(for key: String) -> Value? { var value: Value? queue.sync { guard let data: Data = self.value(for: key) else { return } value = try? JSONDecoder().decode(Value.self, from: data) } return value } } ================================================ FILE: Plugins/Persistence/Sources/Persistence/UserDefaultsInterface.swift ================================================ // // UserDefaultsInterface.swift // Persistence // // Created by Cassius Pacheco on 16/12/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation public protocol UserDefaultsInterface { func integer(forKey defaultName: String) -> Int func dictionary(forKey defaultName: String) -> [String: Any]? func bool(forKey defaultName: String) -> Bool func string(forKey defaultName: String) -> String? func set(_ value: Any?, forKey defaultName: String) func removeObject(forKey defaultName: String) func object(forKey defaultName: String) -> Any? func dictionaryRepresentation() -> [String: Any] func array(forKey defaultName: String) -> [Any]? } ================================================ FILE: Plugins/Persistence/Tests/PersistenceTests/PersistenceTests.swift ================================================ // // PersistenceTests.swift // PersistenceTests // // Created by Cassius Pacheco on 16/12/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import XCTest @testable import Persistence final class PersistenceTests: XCTestCase { struct DummySerializableObject: Codable { let name: String let age: Int } func data(forName name: String, age: Int) -> Data { return try! JSONSerialization.data(withJSONObject: ["name": name, "age": age], options: []) } func testWriteAndReadFoundationObject() { let object = "My Object" let key = "test" let defaults = UserDefaultsMock() let persistence = Persistence(defaults: defaults) XCTAssertNil(persistence.value(for: key)) persistence.save(object, for: key) XCTAssertEqual(persistence.value(for: key), object) } func testWriteAndReadSerializableObject() { let object = DummySerializableObject(name: "João", age: 18) let key = "test" let defaults = UserDefaultsMock() let persistence = Persistence(defaults: defaults) var returnedObject: DummySerializableObject? = persistence.decodedValue(for: key) XCTAssertNil(returnedObject) persistence.saveEncoded(object, for: key) returnedObject = persistence.decodedValue(for: key) XCTAssertEqual(returnedObject?.name, object.name) XCTAssertEqual(returnedObject?.age, object.age) } func testWriteAndReadSerializableObjectsArray() { let object1 = DummySerializableObject(name: "Jana", age: 18) let object2 = DummySerializableObject(name: "Marcos", age: 25) let object3 = DummySerializableObject(name: "Oprah", age: 54) let array = [object1, object2, object3] let key = "test" let defaults = UserDefaultsMock() let persistence = Persistence(defaults: defaults) var returnedObjects: [DummySerializableObject]? = persistence.decodedValue(for: key) XCTAssertNil(returnedObjects) persistence.saveEncoded(array, for: key) returnedObjects = persistence.decodedValue(for: key) XCTAssertEqual(returnedObjects!.first!.name, object1.name) XCTAssertEqual(returnedObjects!.first!.age, object1.age) XCTAssertEqual(returnedObjects![2].name, object3.name) XCTAssertEqual(returnedObjects![2].age, object3.age) } func testBoolForKey() { let key = "test" let defaults = UserDefaultsMock() defaults.mockDictionary[key] = true let persistence = Persistence(defaults: defaults) XCTAssertEqual(defaults.bool(forKey: key), true) persistence.save(false, for: key) XCTAssertEqual(defaults.bool(forKey: key), true) persistence.removeValue(for: key) XCTAssertNil(persistence.value(for: key), "read value from persistence to ensure the barrier queue has finished removing the value") } func testRemoveValue() { let key = "test" let defaults = UserDefaultsMock() defaults.mockDictionary[key] = "abc" let persistence = Persistence(defaults: defaults) XCTAssertEqual(defaults.string(forKey: key), "abc") XCTAssertEqual(defaults.object(forKey: key) as? String, "abc") persistence.removeValue(for: key) XCTAssertNil(persistence.value(for: key), "read value from persistence to ensure the barrier queue has finished removing the value") } } ================================================ FILE: Plugins/Persistence/Tests/PersistenceTests/UserDefaultsMock.swift ================================================ // // UserDefaultsMock.swift // PersistenceTests // // Created by Cassius Pacheco on 16/12/18. // Copyright © 2018 Cassius Pacheco. All rights reserved. // import Foundation import XCTest @testable import Persistence class UserDefaultsMock: UserDefaultsInterface { var savedKeys = [String]() var mockDictionary = [String: Any]() func integer(forKey defaultName: String) -> Int { return value(for: defaultName) ?? 0 } func dictionary(forKey defaultName: String) -> [String : Any]? { return value(for: defaultName) } func bool(forKey defaultName: String) -> Bool { return value(for: defaultName) ?? false } func string(forKey defaultName: String) -> String? { return value(for: defaultName) } func set(_ value: Any?, forKey defaultName: String) { savedKeys.append(defaultName) mockDictionary[defaultName] = value } func removeObject(forKey defaultName: String) { mockDictionary.removeValue(forKey: defaultName) } func object(forKey defaultName: String) -> Any? { return value(for: defaultName) } func dictionaryRepresentation() -> [String : Any] { return mockDictionary } func array(forKey defaultName: String) -> [Any]? { return value(for: defaultName) } // This wrapper ensures the `Any?` object returned is not `nil` // otherwise casting a nil `Any?` into something else will // return a `.some(nil)` result, which is different of `nil`. private func value(for key: String) -> Value? { guard let object = mockDictionary[key] else { return nil } return object as? Value } } ================================================ FILE: README.md ================================================ # The Clean Architecture This sample project is inspired on [The Clean Architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) by Uncle Bob. Its main goal is to follow the [SOLID principles](https://en.wikipedia.org/wiki/SOLID) in order to keep the software well organised, testable, easy to maintain and more importantly easy to extend/change. As explained in the posts above and in [the book](https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164), databases, network clients and frameworks are abstracted in the upper layers (Entities, Data and Domain layers) and should not be imported/referenced into them, instead they should be injected in the App level as dependencies through the interfaces exposed by the upper layers. ![graph]( https://8thlight.com/blog/assets/posts/2012-08-13-the-clean-architecture/CleanArchitecture-8d1fe066e8f7fa9c7d8e84c1a6b0e2b74b2c670ff8052828f4a7e73fcbbc698c.jpg) ## Modules In this sample app we have the following modules/layers: ### DependencyInjector Framework responsible for linking the Interfaces to the `Factories`. This should only the referenced/used in the `App Module`. ### Entities Top level module. This should not have access to any other module in the system and must be the simplest possible. ### Data Responsible for exposing a few Interfaces for external plugins implementations such as databases, memory caches, network, bluetooth, etc. This should not reference any concrete class related to these. It also contains the `Repositories` which are another abstraction for the use of the plugins mentioned above. This module can only access `Entities` layer, nothing else. ### Domain This module exposes `UseCases` to process business logic using the [Command Pattern](https://en.wikipedia.org/wiki/Command_pattern). The use cases must be simple and have only one responsibility. They usually communicate with `Repositories` from the `Data layer` through Interfaces. Ideally `UseCases` would return `DataStructures` instead of `Entities` to the lower layers, since these could contain UI-specific data which isn't part of the `Entities` layer. ### App In this layer we have all the code that's specific to the platform of development. In this sample project the platform is iOS, furthermore a concrete implementation of the `CacheInterface` exposed by the `Data layer` was implemented in the `App layer` using `UserDefaults` as the persistence choice, for example. This layer may have `ViewModels`, `Coordinators`, `ViewControllers`, `Views`, third party frameworks and, finally, is also responsible for setting up the dependency graph for the project by linking all the interfaces to their respective concrete classes. ## Tests Even though I haven't written any tests yet, the whole project is completely testable, since all layers are being isolated by interfaces, as suggested by the SOLID principles. ## Getting Started ### Prerequisites - Xcode 11.3.1 - iOS 12 - Swift 5.1 ### Installing The project uses Swift Package Manager for dependency management. Just open `SwiftCleanArchitecture.xcworkspace`, select the `App` target and run it. To check out the legacy example built with Carthage, have a look at this [branch](https://github.com/CassiusPacheco/Swift-CleanArchitecture/tree/carthage). ================================================ FILE: SwiftCleanArchitecture.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: SwiftCleanArchitecture.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: SwiftCleanArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "object": { "pins": [ { "package": "DependencyContainer", "repositoryURL": "https://github.com/CassiusPacheco/DependencyContainer", "state": { "branch": null, "revision": "15049d95d20dabca6337c4c63c1ff98f3b776e07", "version": "0.1.0" } }, { "package": "RxSwift", "repositoryURL": "https://github.com/ReactiveX/RxSwift", "state": { "branch": null, "revision": "c1bd31b397d87a54467af4161dde9d6b27720c19", "version": "5.1.0" } } ] }, "version": 1 }