├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── README.md ├── iOSCleanArchitecture.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── iOSCleanArchitecture ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Commons │ ├── ApplicationContext │ │ ├── AbstractApplicationContext+Extension.swift │ │ ├── ApplicationContext.swift │ │ └── ServiceContext │ │ │ ├── PostApplicationContext.swift │ │ │ └── TestPostApplicationContext.swift │ ├── Base │ │ ├── BaseNavigator.swift │ │ ├── BaseViewController.swift │ │ ├── BaseViewModel.swift │ │ ├── DataBinding.swift │ │ └── TraceError.swift │ ├── Cache │ │ └── Cache.swift │ ├── IOC │ │ ├── AbstractApplicationContext.swift │ │ └── Assembler.swift │ ├── Logger │ │ ├── LSLogger.swift │ │ └── Logger.swift │ └── Utils │ │ ├── DispatchQueue.swift │ │ ├── EventService.swift │ │ ├── Extensions.swift │ │ ├── Queue.swift │ │ ├── WeakRef.swift │ │ └── Zip3.swift ├── Domain │ ├── Entity │ │ └── Post.swift │ ├── Model │ │ ├── AddPostModel.swift │ │ └── AllPostModel.swift │ └── RepositoryInterface │ │ ├── DAO.swift │ │ └── PostInterface.swift ├── Info.plist ├── Repository │ ├── CommonRealmDAO.swift │ ├── RealmPostDAO.swift │ └── TestPostDAO.swift ├── Scenes │ ├── .DS_Store │ ├── AddPost │ │ ├── AddPost.storyboard │ │ ├── AddPostNavigator.swift │ │ ├── AddPostViewController.swift │ │ └── AddPostViewModel.swift │ ├── AllPost │ │ ├── AllPost.storyboard │ │ ├── AllPostNavigator.swift │ │ ├── AllPostViewController.swift │ │ └── AllPostViewModel.swift │ └── DetailPost │ │ ├── DetailPost.storyboard │ │ ├── DetailPostNavigator.swift │ │ ├── DetailPostViewController.swift │ │ └── DetailPostViewModel.swift └── ViewController.swift ├── iOSCleanArchitectureTests ├── Info.plist └── iOSCleanArchitectureTests.swift └── iOSCleanArchitectureUITests ├── Info.plist └── iOSCleanArchitectureUITests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | Carthage/* 2 | xcuserdata 3 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | # RxSwift 2 | github "ReactiveX/RxSwift" == 4.4.2 3 | 4 | # Realm DB (TV Plugin) 5 | github "realm/realm-cocoa" == 3.14.1 6 | 7 | # Custom Log Manager 8 | github "CocoaLumberjack/CocoaLumberjack" == 3.5.2 9 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "CocoaLumberjack/CocoaLumberjack" "3.5.2" 2 | github "ReactiveX/RxSwift" "4.4.2" 3 | github "realm/realm-cocoa" "v3.14.1" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOSCleanArchitecture 2 | iOS Clean Architecture with Swift. (RxSwift) 3 | 4 | 5 | carthage update --platform iOS 6 | 7 | https://develogs.tistory.com/7?category=589493 8 | -------------------------------------------------------------------------------- /iOSCleanArchitecture.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A4A71F7822D9B32200E9FF79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71F7722D9B32200E9FF79 /* AppDelegate.swift */; }; 11 | A4A71F7A22D9B32200E9FF79 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71F7922D9B32200E9FF79 /* ViewController.swift */; }; 12 | A4A71F7D22D9B32200E9FF79 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A4A71F7B22D9B32200E9FF79 /* Main.storyboard */; }; 13 | A4A71F7F22D9B32300E9FF79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A4A71F7E22D9B32300E9FF79 /* Assets.xcassets */; }; 14 | A4A71F8222D9B32300E9FF79 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A4A71F8022D9B32300E9FF79 /* LaunchScreen.storyboard */; }; 15 | A4A71F8D22D9B32400E9FF79 /* iOSCleanArchitectureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71F8C22D9B32400E9FF79 /* iOSCleanArchitectureTests.swift */; }; 16 | A4A71F9822D9B32400E9FF79 /* iOSCleanArchitectureUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71F9722D9B32400E9FF79 /* iOSCleanArchitectureUITests.swift */; }; 17 | A4A71FAC22D9BB3300E9FF79 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A71FA822D9BB3200E9FF79 /* RxCocoa.framework */; }; 18 | A4A71FAD22D9BB3300E9FF79 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A71FA922D9BB3200E9FF79 /* RealmSwift.framework */; }; 19 | A4A71FAE22D9BB3300E9FF79 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A71FAA22D9BB3300E9FF79 /* RxSwift.framework */; }; 20 | A4A71FAF22D9BB3300E9FF79 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A71FAB22D9BB3300E9FF79 /* Realm.framework */; }; 21 | A4A7202222D9C19500E9FF79 /* CommonRealmDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FB122D9C19400E9FF79 /* CommonRealmDAO.swift */; }; 22 | A4A7203B22D9C19500E9FF79 /* DAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FD322D9C19500E9FF79 /* DAO.swift */; }; 23 | A4A7203D22D9C19500E9FF79 /* LSLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FD822D9C19500E9FF79 /* LSLogger.swift */; }; 24 | A4A7203E22D9C19500E9FF79 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FD922D9C19500E9FF79 /* Logger.swift */; }; 25 | A4A7203F22D9C19500E9FF79 /* Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FDB22D9C19500E9FF79 /* Cache.swift */; }; 26 | A4A7204122D9C19500E9FF79 /* EventService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FDE22D9C19500E9FF79 /* EventService.swift */; }; 27 | A4A7204222D9C19500E9FF79 /* DispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FDF22D9C19500E9FF79 /* DispatchQueue.swift */; }; 28 | A4A7204322D9C19500E9FF79 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FE022D9C19500E9FF79 /* Extensions.swift */; }; 29 | A4A7204522D9C19500E9FF79 /* WeakRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FE222D9C19500E9FF79 /* WeakRef.swift */; }; 30 | A4A7204622D9C19500E9FF79 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FE322D9C19500E9FF79 /* Queue.swift */; }; 31 | A4A7204722D9C19500E9FF79 /* Zip3.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FE522D9C19500E9FF79 /* Zip3.swift */; }; 32 | A4A7204822D9C19500E9FF79 /* ApplicationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FE722D9C19500E9FF79 /* ApplicationContext.swift */; }; 33 | A4A7204922D9C19500E9FF79 /* PostApplicationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FE922D9C19500E9FF79 /* PostApplicationContext.swift */; }; 34 | A4A7204A22D9C19500E9FF79 /* TestPostApplicationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FEA22D9C19500E9FF79 /* TestPostApplicationContext.swift */; }; 35 | A4A7204B22D9C19500E9FF79 /* AbstractApplicationContext+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FEB22D9C19500E9FF79 /* AbstractApplicationContext+Extension.swift */; }; 36 | A4A7204C22D9C19500E9FF79 /* AbstractApplicationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FED22D9C19500E9FF79 /* AbstractApplicationContext.swift */; }; 37 | A4A7204D22D9C19500E9FF79 /* Assembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FEE22D9C19500E9FF79 /* Assembler.swift */; }; 38 | A4A7204E22D9C19500E9FF79 /* DataBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FF022D9C19500E9FF79 /* DataBinding.swift */; }; 39 | A4A7204F22D9C19500E9FF79 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FF122D9C19500E9FF79 /* BaseViewController.swift */; }; 40 | A4A7205022D9C19500E9FF79 /* BaseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FF222D9C19500E9FF79 /* BaseViewModel.swift */; }; 41 | A4A7205222D9C19500E9FF79 /* TraceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FF422D9C19500E9FF79 /* TraceError.swift */; }; 42 | A4A7205322D9C19500E9FF79 /* BaseNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A71FF522D9C19500E9FF79 /* BaseNavigator.swift */; }; 43 | A4A7208622D9C67E00E9FF79 /* AllPostNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7207822D9C67D00E9FF79 /* AllPostNavigator.swift */; }; 44 | A4A7208722D9C67E00E9FF79 /* AllPostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7207922D9C67D00E9FF79 /* AllPostViewModel.swift */; }; 45 | A4A7208822D9C67E00E9FF79 /* AllPost.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A4A7207A22D9C67D00E9FF79 /* AllPost.storyboard */; }; 46 | A4A7208922D9C67E00E9FF79 /* AllPostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7207B22D9C67D00E9FF79 /* AllPostViewController.swift */; }; 47 | A4A7209322D9CB4F00E9FF79 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7209222D9CB4F00E9FF79 /* Post.swift */; }; 48 | A4A7209522D9CBBE00E9FF79 /* AllPostModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7209422D9CBBE00E9FF79 /* AllPostModel.swift */; }; 49 | A4A7209722D9CBEE00E9FF79 /* PostInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7209622D9CBEE00E9FF79 /* PostInterface.swift */; }; 50 | A4A7209922D9CC7E00E9FF79 /* TestPostDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A7209822D9CC7E00E9FF79 /* TestPostDAO.swift */; }; 51 | A4A7209C22D9D0F500E9FF79 /* CocoaLumberjackSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A7209A22D9D0F500E9FF79 /* CocoaLumberjackSwift.framework */; }; 52 | A4A7209D22D9D0F500E9FF79 /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4A7209B22D9D0F500E9FF79 /* CocoaLumberjack.framework */; }; 53 | A4A720A122D9E1E100E9FF79 /* AddPostModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A720A022D9E1E100E9FF79 /* AddPostModel.swift */; }; 54 | A4A720A722D9E29900E9FF79 /* AddPostNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A720A322D9E29900E9FF79 /* AddPostNavigator.swift */; }; 55 | A4A720A822D9E29900E9FF79 /* AddPost.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A4A720A422D9E29900E9FF79 /* AddPost.storyboard */; }; 56 | A4A720A922D9E29900E9FF79 /* AddPostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A720A522D9E29900E9FF79 /* AddPostViewModel.swift */; }; 57 | A4A720AA22D9E29900E9FF79 /* AddPostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A720A622D9E29900E9FF79 /* AddPostViewController.swift */; }; 58 | A4A720AC22D9E4E400E9FF79 /* RealmPostDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A720AB22D9E4E400E9FF79 /* RealmPostDAO.swift */; }; 59 | /* End PBXBuildFile section */ 60 | 61 | /* Begin PBXContainerItemProxy section */ 62 | A4A71F8922D9B32400E9FF79 /* PBXContainerItemProxy */ = { 63 | isa = PBXContainerItemProxy; 64 | containerPortal = A4A71F6C22D9B32200E9FF79 /* Project object */; 65 | proxyType = 1; 66 | remoteGlobalIDString = A4A71F7322D9B32200E9FF79; 67 | remoteInfo = iOSCleanArchitecture; 68 | }; 69 | A4A71F9422D9B32400E9FF79 /* PBXContainerItemProxy */ = { 70 | isa = PBXContainerItemProxy; 71 | containerPortal = A4A71F6C22D9B32200E9FF79 /* Project object */; 72 | proxyType = 1; 73 | remoteGlobalIDString = A4A71F7322D9B32200E9FF79; 74 | remoteInfo = iOSCleanArchitecture; 75 | }; 76 | /* End PBXContainerItemProxy section */ 77 | 78 | /* Begin PBXFileReference section */ 79 | A4A71F7422D9B32200E9FF79 /* iOSCleanArchitecture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSCleanArchitecture.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | A4A71F7722D9B32200E9FF79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 81 | A4A71F7922D9B32200E9FF79 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 82 | A4A71F7C22D9B32200E9FF79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 83 | A4A71F7E22D9B32300E9FF79 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 84 | A4A71F8122D9B32300E9FF79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 85 | A4A71F8322D9B32300E9FF79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | A4A71F8822D9B32400E9FF79 /* iOSCleanArchitectureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSCleanArchitectureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | A4A71F8C22D9B32400E9FF79 /* iOSCleanArchitectureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSCleanArchitectureTests.swift; sourceTree = ""; }; 88 | A4A71F8E22D9B32400E9FF79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | A4A71F9322D9B32400E9FF79 /* iOSCleanArchitectureUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSCleanArchitectureUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | A4A71F9722D9B32400E9FF79 /* iOSCleanArchitectureUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSCleanArchitectureUITests.swift; sourceTree = ""; }; 91 | A4A71F9922D9B32400E9FF79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 92 | A4A71FA822D9BB3200E9FF79 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 93 | A4A71FA922D9BB3200E9FF79 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = ""; }; 94 | A4A71FAA22D9BB3300E9FF79 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; 95 | A4A71FAB22D9BB3300E9FF79 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = ""; }; 96 | A4A71FB122D9C19400E9FF79 /* CommonRealmDAO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonRealmDAO.swift; sourceTree = ""; }; 97 | A4A71FD322D9C19500E9FF79 /* DAO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DAO.swift; sourceTree = ""; }; 98 | A4A71FD822D9C19500E9FF79 /* LSLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LSLogger.swift; sourceTree = ""; }; 99 | A4A71FD922D9C19500E9FF79 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 100 | A4A71FDB22D9C19500E9FF79 /* Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cache.swift; sourceTree = ""; }; 101 | A4A71FDE22D9C19500E9FF79 /* EventService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventService.swift; sourceTree = ""; }; 102 | A4A71FDF22D9C19500E9FF79 /* DispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchQueue.swift; sourceTree = ""; }; 103 | A4A71FE022D9C19500E9FF79 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 104 | A4A71FE222D9C19500E9FF79 /* WeakRef.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakRef.swift; sourceTree = ""; }; 105 | A4A71FE322D9C19500E9FF79 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 106 | A4A71FE522D9C19500E9FF79 /* Zip3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zip3.swift; sourceTree = ""; }; 107 | A4A71FE722D9C19500E9FF79 /* ApplicationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationContext.swift; sourceTree = ""; }; 108 | A4A71FE922D9C19500E9FF79 /* PostApplicationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostApplicationContext.swift; sourceTree = ""; }; 109 | A4A71FEA22D9C19500E9FF79 /* TestPostApplicationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestPostApplicationContext.swift; sourceTree = ""; }; 110 | A4A71FEB22D9C19500E9FF79 /* AbstractApplicationContext+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AbstractApplicationContext+Extension.swift"; sourceTree = ""; }; 111 | A4A71FED22D9C19500E9FF79 /* AbstractApplicationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AbstractApplicationContext.swift; sourceTree = ""; }; 112 | A4A71FEE22D9C19500E9FF79 /* Assembler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assembler.swift; sourceTree = ""; }; 113 | A4A71FF022D9C19500E9FF79 /* DataBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBinding.swift; sourceTree = ""; }; 114 | A4A71FF122D9C19500E9FF79 /* BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 115 | A4A71FF222D9C19500E9FF79 /* BaseViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewModel.swift; sourceTree = ""; }; 116 | A4A71FF422D9C19500E9FF79 /* TraceError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TraceError.swift; sourceTree = ""; }; 117 | A4A71FF522D9C19500E9FF79 /* BaseNavigator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseNavigator.swift; sourceTree = ""; }; 118 | A4A7207822D9C67D00E9FF79 /* AllPostNavigator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllPostNavigator.swift; sourceTree = ""; }; 119 | A4A7207922D9C67D00E9FF79 /* AllPostViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllPostViewModel.swift; sourceTree = ""; }; 120 | A4A7207A22D9C67D00E9FF79 /* AllPost.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = AllPost.storyboard; sourceTree = ""; }; 121 | A4A7207B22D9C67D00E9FF79 /* AllPostViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllPostViewController.swift; sourceTree = ""; }; 122 | A4A7209222D9CB4F00E9FF79 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; 123 | A4A7209422D9CBBE00E9FF79 /* AllPostModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPostModel.swift; sourceTree = ""; }; 124 | A4A7209622D9CBEE00E9FF79 /* PostInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostInterface.swift; sourceTree = ""; }; 125 | A4A7209822D9CC7E00E9FF79 /* TestPostDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestPostDAO.swift; sourceTree = ""; }; 126 | A4A7209A22D9D0F500E9FF79 /* CocoaLumberjackSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjackSwift.framework; path = Carthage/Build/iOS/CocoaLumberjackSwift.framework; sourceTree = ""; }; 127 | A4A7209B22D9D0F500E9FF79 /* CocoaLumberjack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjack.framework; path = Carthage/Build/iOS/CocoaLumberjack.framework; sourceTree = ""; }; 128 | A4A720A022D9E1E100E9FF79 /* AddPostModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPostModel.swift; sourceTree = ""; }; 129 | A4A720A322D9E29900E9FF79 /* AddPostNavigator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPostNavigator.swift; sourceTree = ""; }; 130 | A4A720A422D9E29900E9FF79 /* AddPost.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = AddPost.storyboard; sourceTree = ""; }; 131 | A4A720A522D9E29900E9FF79 /* AddPostViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPostViewModel.swift; sourceTree = ""; }; 132 | A4A720A622D9E29900E9FF79 /* AddPostViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPostViewController.swift; sourceTree = ""; }; 133 | A4A720AB22D9E4E400E9FF79 /* RealmPostDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmPostDAO.swift; sourceTree = ""; }; 134 | /* End PBXFileReference section */ 135 | 136 | /* Begin PBXFrameworksBuildPhase section */ 137 | A4A71F7122D9B32200E9FF79 /* Frameworks */ = { 138 | isa = PBXFrameworksBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | A4A71FAC22D9BB3300E9FF79 /* RxCocoa.framework in Frameworks */, 142 | A4A71FAD22D9BB3300E9FF79 /* RealmSwift.framework in Frameworks */, 143 | A4A71FAE22D9BB3300E9FF79 /* RxSwift.framework in Frameworks */, 144 | A4A71FAF22D9BB3300E9FF79 /* Realm.framework in Frameworks */, 145 | A4A7209C22D9D0F500E9FF79 /* CocoaLumberjackSwift.framework in Frameworks */, 146 | A4A7209D22D9D0F500E9FF79 /* CocoaLumberjack.framework in Frameworks */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | A4A71F8522D9B32400E9FF79 /* Frameworks */ = { 151 | isa = PBXFrameworksBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | A4A71F9022D9B32400E9FF79 /* Frameworks */ = { 158 | isa = PBXFrameworksBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | }; 164 | /* End PBXFrameworksBuildPhase section */ 165 | 166 | /* Begin PBXGroup section */ 167 | A4A71F6B22D9B32200E9FF79 = { 168 | isa = PBXGroup; 169 | children = ( 170 | A4A71F7622D9B32200E9FF79 /* iOSCleanArchitecture */, 171 | A4A71F8B22D9B32400E9FF79 /* iOSCleanArchitectureTests */, 172 | A4A71F9622D9B32400E9FF79 /* iOSCleanArchitectureUITests */, 173 | A4A71F7522D9B32200E9FF79 /* Products */, 174 | A4A71FA722D9BB3200E9FF79 /* Frameworks */, 175 | ); 176 | sourceTree = ""; 177 | }; 178 | A4A71F7522D9B32200E9FF79 /* Products */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | A4A71F7422D9B32200E9FF79 /* iOSCleanArchitecture.app */, 182 | A4A71F8822D9B32400E9FF79 /* iOSCleanArchitectureTests.xctest */, 183 | A4A71F9322D9B32400E9FF79 /* iOSCleanArchitectureUITests.xctest */, 184 | ); 185 | name = Products; 186 | sourceTree = ""; 187 | }; 188 | A4A71F7622D9B32200E9FF79 /* iOSCleanArchitecture */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | A4A71FD422D9C19500E9FF79 /* Commons */, 192 | A4A71FF622D9C19500E9FF79 /* Scenes */, 193 | A4A71FC922D9C19500E9FF79 /* Domain */, 194 | A4A71FB022D9C19400E9FF79 /* Repository */, 195 | A4A71FB822D9C19500E9FF79 /* DataSource */, 196 | A4A71F7722D9B32200E9FF79 /* AppDelegate.swift */, 197 | A4A71F7922D9B32200E9FF79 /* ViewController.swift */, 198 | A4A71F7B22D9B32200E9FF79 /* Main.storyboard */, 199 | A4A71F7E22D9B32300E9FF79 /* Assets.xcassets */, 200 | A4A71F8022D9B32300E9FF79 /* LaunchScreen.storyboard */, 201 | A4A71F8322D9B32300E9FF79 /* Info.plist */, 202 | ); 203 | path = iOSCleanArchitecture; 204 | sourceTree = ""; 205 | }; 206 | A4A71F8B22D9B32400E9FF79 /* iOSCleanArchitectureTests */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | A4A71F8C22D9B32400E9FF79 /* iOSCleanArchitectureTests.swift */, 210 | A4A71F8E22D9B32400E9FF79 /* Info.plist */, 211 | ); 212 | path = iOSCleanArchitectureTests; 213 | sourceTree = ""; 214 | }; 215 | A4A71F9622D9B32400E9FF79 /* iOSCleanArchitectureUITests */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | A4A71F9722D9B32400E9FF79 /* iOSCleanArchitectureUITests.swift */, 219 | A4A71F9922D9B32400E9FF79 /* Info.plist */, 220 | ); 221 | path = iOSCleanArchitectureUITests; 222 | sourceTree = ""; 223 | }; 224 | A4A71FA722D9BB3200E9FF79 /* Frameworks */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | A4A7209B22D9D0F500E9FF79 /* CocoaLumberjack.framework */, 228 | A4A7209A22D9D0F500E9FF79 /* CocoaLumberjackSwift.framework */, 229 | A4A71FAB22D9BB3300E9FF79 /* Realm.framework */, 230 | A4A71FA922D9BB3200E9FF79 /* RealmSwift.framework */, 231 | A4A71FA822D9BB3200E9FF79 /* RxCocoa.framework */, 232 | A4A71FAA22D9BB3300E9FF79 /* RxSwift.framework */, 233 | ); 234 | name = Frameworks; 235 | sourceTree = ""; 236 | }; 237 | A4A71FB022D9C19400E9FF79 /* Repository */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | A4A71FB122D9C19400E9FF79 /* CommonRealmDAO.swift */, 241 | A4A7209822D9CC7E00E9FF79 /* TestPostDAO.swift */, 242 | A4A720AB22D9E4E400E9FF79 /* RealmPostDAO.swift */, 243 | ); 244 | path = Repository; 245 | sourceTree = ""; 246 | }; 247 | A4A71FB822D9C19500E9FF79 /* DataSource */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | ); 251 | path = DataSource; 252 | sourceTree = ""; 253 | }; 254 | A4A71FC922D9C19500E9FF79 /* Domain */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | A4A71FCA22D9C19500E9FF79 /* Entity */, 258 | A4A71FCC22D9C19500E9FF79 /* Model */, 259 | A4A71FCF22D9C19500E9FF79 /* RepositoryInterface */, 260 | ); 261 | path = Domain; 262 | sourceTree = ""; 263 | }; 264 | A4A71FCA22D9C19500E9FF79 /* Entity */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | A4A7209222D9CB4F00E9FF79 /* Post.swift */, 268 | ); 269 | path = Entity; 270 | sourceTree = ""; 271 | }; 272 | A4A71FCC22D9C19500E9FF79 /* Model */ = { 273 | isa = PBXGroup; 274 | children = ( 275 | A4A7209422D9CBBE00E9FF79 /* AllPostModel.swift */, 276 | A4A720A022D9E1E100E9FF79 /* AddPostModel.swift */, 277 | ); 278 | path = Model; 279 | sourceTree = ""; 280 | }; 281 | A4A71FCF22D9C19500E9FF79 /* RepositoryInterface */ = { 282 | isa = PBXGroup; 283 | children = ( 284 | A4A71FD322D9C19500E9FF79 /* DAO.swift */, 285 | A4A7209622D9CBEE00E9FF79 /* PostInterface.swift */, 286 | ); 287 | path = RepositoryInterface; 288 | sourceTree = ""; 289 | }; 290 | A4A71FD422D9C19500E9FF79 /* Commons */ = { 291 | isa = PBXGroup; 292 | children = ( 293 | A4A71FD522D9C19500E9FF79 /* Extension */, 294 | A4A71FD722D9C19500E9FF79 /* Logger */, 295 | A4A71FDA22D9C19500E9FF79 /* Cache */, 296 | A4A71FDC22D9C19500E9FF79 /* Utils */, 297 | A4A71FE622D9C19500E9FF79 /* ApplicationContext */, 298 | A4A71FEC22D9C19500E9FF79 /* IOC */, 299 | A4A71FEF22D9C19500E9FF79 /* Base */, 300 | ); 301 | path = Commons; 302 | sourceTree = ""; 303 | }; 304 | A4A71FD522D9C19500E9FF79 /* Extension */ = { 305 | isa = PBXGroup; 306 | children = ( 307 | ); 308 | path = Extension; 309 | sourceTree = ""; 310 | }; 311 | A4A71FD722D9C19500E9FF79 /* Logger */ = { 312 | isa = PBXGroup; 313 | children = ( 314 | A4A71FD822D9C19500E9FF79 /* LSLogger.swift */, 315 | A4A71FD922D9C19500E9FF79 /* Logger.swift */, 316 | ); 317 | path = Logger; 318 | sourceTree = ""; 319 | }; 320 | A4A71FDA22D9C19500E9FF79 /* Cache */ = { 321 | isa = PBXGroup; 322 | children = ( 323 | A4A71FDB22D9C19500E9FF79 /* Cache.swift */, 324 | ); 325 | path = Cache; 326 | sourceTree = ""; 327 | }; 328 | A4A71FDC22D9C19500E9FF79 /* Utils */ = { 329 | isa = PBXGroup; 330 | children = ( 331 | A4A71FDE22D9C19500E9FF79 /* EventService.swift */, 332 | A4A71FDF22D9C19500E9FF79 /* DispatchQueue.swift */, 333 | A4A71FE022D9C19500E9FF79 /* Extensions.swift */, 334 | A4A71FE222D9C19500E9FF79 /* WeakRef.swift */, 335 | A4A71FE322D9C19500E9FF79 /* Queue.swift */, 336 | A4A71FE522D9C19500E9FF79 /* Zip3.swift */, 337 | ); 338 | path = Utils; 339 | sourceTree = ""; 340 | }; 341 | A4A71FE622D9C19500E9FF79 /* ApplicationContext */ = { 342 | isa = PBXGroup; 343 | children = ( 344 | A4A71FE722D9C19500E9FF79 /* ApplicationContext.swift */, 345 | A4A71FEB22D9C19500E9FF79 /* AbstractApplicationContext+Extension.swift */, 346 | A4A71FE822D9C19500E9FF79 /* ServiceContext */, 347 | ); 348 | path = ApplicationContext; 349 | sourceTree = ""; 350 | }; 351 | A4A71FE822D9C19500E9FF79 /* ServiceContext */ = { 352 | isa = PBXGroup; 353 | children = ( 354 | A4A71FE922D9C19500E9FF79 /* PostApplicationContext.swift */, 355 | A4A71FEA22D9C19500E9FF79 /* TestPostApplicationContext.swift */, 356 | ); 357 | path = ServiceContext; 358 | sourceTree = ""; 359 | }; 360 | A4A71FEC22D9C19500E9FF79 /* IOC */ = { 361 | isa = PBXGroup; 362 | children = ( 363 | A4A71FED22D9C19500E9FF79 /* AbstractApplicationContext.swift */, 364 | A4A71FEE22D9C19500E9FF79 /* Assembler.swift */, 365 | ); 366 | path = IOC; 367 | sourceTree = ""; 368 | }; 369 | A4A71FEF22D9C19500E9FF79 /* Base */ = { 370 | isa = PBXGroup; 371 | children = ( 372 | A4A71FF022D9C19500E9FF79 /* DataBinding.swift */, 373 | A4A71FF122D9C19500E9FF79 /* BaseViewController.swift */, 374 | A4A71FF222D9C19500E9FF79 /* BaseViewModel.swift */, 375 | A4A71FF422D9C19500E9FF79 /* TraceError.swift */, 376 | A4A71FF522D9C19500E9FF79 /* BaseNavigator.swift */, 377 | ); 378 | path = Base; 379 | sourceTree = ""; 380 | }; 381 | A4A71FF622D9C19500E9FF79 /* Scenes */ = { 382 | isa = PBXGroup; 383 | children = ( 384 | A4A7207722D9C67D00E9FF79 /* AllPost */, 385 | A4A720A222D9E29900E9FF79 /* AddPost */, 386 | ); 387 | path = Scenes; 388 | sourceTree = ""; 389 | }; 390 | A4A7207722D9C67D00E9FF79 /* AllPost */ = { 391 | isa = PBXGroup; 392 | children = ( 393 | A4A7207A22D9C67D00E9FF79 /* AllPost.storyboard */, 394 | A4A7207B22D9C67D00E9FF79 /* AllPostViewController.swift */, 395 | A4A7207922D9C67D00E9FF79 /* AllPostViewModel.swift */, 396 | A4A7207822D9C67D00E9FF79 /* AllPostNavigator.swift */, 397 | ); 398 | path = AllPost; 399 | sourceTree = ""; 400 | }; 401 | A4A720A222D9E29900E9FF79 /* AddPost */ = { 402 | isa = PBXGroup; 403 | children = ( 404 | A4A720A422D9E29900E9FF79 /* AddPost.storyboard */, 405 | A4A720A622D9E29900E9FF79 /* AddPostViewController.swift */, 406 | A4A720A522D9E29900E9FF79 /* AddPostViewModel.swift */, 407 | A4A720A322D9E29900E9FF79 /* AddPostNavigator.swift */, 408 | ); 409 | path = AddPost; 410 | sourceTree = ""; 411 | }; 412 | /* End PBXGroup section */ 413 | 414 | /* Begin PBXNativeTarget section */ 415 | A4A71F7322D9B32200E9FF79 /* iOSCleanArchitecture */ = { 416 | isa = PBXNativeTarget; 417 | buildConfigurationList = A4A71F9C22D9B32400E9FF79 /* Build configuration list for PBXNativeTarget "iOSCleanArchitecture" */; 418 | buildPhases = ( 419 | A4A71F7022D9B32200E9FF79 /* Sources */, 420 | A4A71F7122D9B32200E9FF79 /* Frameworks */, 421 | A4A71F7222D9B32200E9FF79 /* Resources */, 422 | A4A71FA622D9BAB800E9FF79 /* Copy Frameworks Managed by Carthage */, 423 | ); 424 | buildRules = ( 425 | ); 426 | dependencies = ( 427 | ); 428 | name = iOSCleanArchitecture; 429 | productName = iOSCleanArchitecture; 430 | productReference = A4A71F7422D9B32200E9FF79 /* iOSCleanArchitecture.app */; 431 | productType = "com.apple.product-type.application"; 432 | }; 433 | A4A71F8722D9B32400E9FF79 /* iOSCleanArchitectureTests */ = { 434 | isa = PBXNativeTarget; 435 | buildConfigurationList = A4A71F9F22D9B32400E9FF79 /* Build configuration list for PBXNativeTarget "iOSCleanArchitectureTests" */; 436 | buildPhases = ( 437 | A4A71F8422D9B32400E9FF79 /* Sources */, 438 | A4A71F8522D9B32400E9FF79 /* Frameworks */, 439 | A4A71F8622D9B32400E9FF79 /* Resources */, 440 | ); 441 | buildRules = ( 442 | ); 443 | dependencies = ( 444 | A4A71F8A22D9B32400E9FF79 /* PBXTargetDependency */, 445 | ); 446 | name = iOSCleanArchitectureTests; 447 | productName = iOSCleanArchitectureTests; 448 | productReference = A4A71F8822D9B32400E9FF79 /* iOSCleanArchitectureTests.xctest */; 449 | productType = "com.apple.product-type.bundle.unit-test"; 450 | }; 451 | A4A71F9222D9B32400E9FF79 /* iOSCleanArchitectureUITests */ = { 452 | isa = PBXNativeTarget; 453 | buildConfigurationList = A4A71FA222D9B32400E9FF79 /* Build configuration list for PBXNativeTarget "iOSCleanArchitectureUITests" */; 454 | buildPhases = ( 455 | A4A71F8F22D9B32400E9FF79 /* Sources */, 456 | A4A71F9022D9B32400E9FF79 /* Frameworks */, 457 | A4A71F9122D9B32400E9FF79 /* Resources */, 458 | ); 459 | buildRules = ( 460 | ); 461 | dependencies = ( 462 | A4A71F9522D9B32400E9FF79 /* PBXTargetDependency */, 463 | ); 464 | name = iOSCleanArchitectureUITests; 465 | productName = iOSCleanArchitectureUITests; 466 | productReference = A4A71F9322D9B32400E9FF79 /* iOSCleanArchitectureUITests.xctest */; 467 | productType = "com.apple.product-type.bundle.ui-testing"; 468 | }; 469 | /* End PBXNativeTarget section */ 470 | 471 | /* Begin PBXProject section */ 472 | A4A71F6C22D9B32200E9FF79 /* Project object */ = { 473 | isa = PBXProject; 474 | attributes = { 475 | LastSwiftUpdateCheck = 1010; 476 | LastUpgradeCheck = 1010; 477 | ORGANIZATIONNAME = "Lee YoungJu"; 478 | TargetAttributes = { 479 | A4A71F7322D9B32200E9FF79 = { 480 | CreatedOnToolsVersion = 10.1; 481 | }; 482 | A4A71F8722D9B32400E9FF79 = { 483 | CreatedOnToolsVersion = 10.1; 484 | TestTargetID = A4A71F7322D9B32200E9FF79; 485 | }; 486 | A4A71F9222D9B32400E9FF79 = { 487 | CreatedOnToolsVersion = 10.1; 488 | TestTargetID = A4A71F7322D9B32200E9FF79; 489 | }; 490 | }; 491 | }; 492 | buildConfigurationList = A4A71F6F22D9B32200E9FF79 /* Build configuration list for PBXProject "iOSCleanArchitecture" */; 493 | compatibilityVersion = "Xcode 9.3"; 494 | developmentRegion = en; 495 | hasScannedForEncodings = 0; 496 | knownRegions = ( 497 | en, 498 | Base, 499 | ); 500 | mainGroup = A4A71F6B22D9B32200E9FF79; 501 | productRefGroup = A4A71F7522D9B32200E9FF79 /* Products */; 502 | projectDirPath = ""; 503 | projectRoot = ""; 504 | targets = ( 505 | A4A71F7322D9B32200E9FF79 /* iOSCleanArchitecture */, 506 | A4A71F8722D9B32400E9FF79 /* iOSCleanArchitectureTests */, 507 | A4A71F9222D9B32400E9FF79 /* iOSCleanArchitectureUITests */, 508 | ); 509 | }; 510 | /* End PBXProject section */ 511 | 512 | /* Begin PBXResourcesBuildPhase section */ 513 | A4A71F7222D9B32200E9FF79 /* Resources */ = { 514 | isa = PBXResourcesBuildPhase; 515 | buildActionMask = 2147483647; 516 | files = ( 517 | A4A7208822D9C67E00E9FF79 /* AllPost.storyboard in Resources */, 518 | A4A71F8222D9B32300E9FF79 /* LaunchScreen.storyboard in Resources */, 519 | A4A720A822D9E29900E9FF79 /* AddPost.storyboard in Resources */, 520 | A4A71F7F22D9B32300E9FF79 /* Assets.xcassets in Resources */, 521 | A4A71F7D22D9B32200E9FF79 /* Main.storyboard in Resources */, 522 | ); 523 | runOnlyForDeploymentPostprocessing = 0; 524 | }; 525 | A4A71F8622D9B32400E9FF79 /* Resources */ = { 526 | isa = PBXResourcesBuildPhase; 527 | buildActionMask = 2147483647; 528 | files = ( 529 | ); 530 | runOnlyForDeploymentPostprocessing = 0; 531 | }; 532 | A4A71F9122D9B32400E9FF79 /* Resources */ = { 533 | isa = PBXResourcesBuildPhase; 534 | buildActionMask = 2147483647; 535 | files = ( 536 | ); 537 | runOnlyForDeploymentPostprocessing = 0; 538 | }; 539 | /* End PBXResourcesBuildPhase section */ 540 | 541 | /* Begin PBXShellScriptBuildPhase section */ 542 | A4A71FA622D9BAB800E9FF79 /* Copy Frameworks Managed by Carthage */ = { 543 | isa = PBXShellScriptBuildPhase; 544 | buildActionMask = 2147483647; 545 | files = ( 546 | ); 547 | inputFileListPaths = ( 548 | ); 549 | inputPaths = ( 550 | "$(SRCROOT)/Carthage/Build/iOS/RxSwift.framework", 551 | "$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework", 552 | "$(SRCROOT)/Carthage/Build/iOS/Realm.framework", 553 | "$(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework", 554 | "$(SRCROOT)/Carthage/Build/iOS/CocoaLumberjack.framework", 555 | "$(SRCROOT)/Carthage/Build/iOS/CocoaLumberjackSwift.framework", 556 | ); 557 | name = "Copy Frameworks Managed by Carthage"; 558 | outputFileListPaths = ( 559 | ); 560 | outputPaths = ( 561 | ); 562 | runOnlyForDeploymentPostprocessing = 0; 563 | shellPath = /bin/sh; 564 | shellScript = "/usr/local/bin/carthage copy-frameworks\n"; 565 | }; 566 | /* End PBXShellScriptBuildPhase section */ 567 | 568 | /* Begin PBXSourcesBuildPhase section */ 569 | A4A71F7022D9B32200E9FF79 /* Sources */ = { 570 | isa = PBXSourcesBuildPhase; 571 | buildActionMask = 2147483647; 572 | files = ( 573 | A4A720A922D9E29900E9FF79 /* AddPostViewModel.swift in Sources */, 574 | A4A7209722D9CBEE00E9FF79 /* PostInterface.swift in Sources */, 575 | A4A7203E22D9C19500E9FF79 /* Logger.swift in Sources */, 576 | A4A720A722D9E29900E9FF79 /* AddPostNavigator.swift in Sources */, 577 | A4A7204922D9C19500E9FF79 /* PostApplicationContext.swift in Sources */, 578 | A4A7203B22D9C19500E9FF79 /* DAO.swift in Sources */, 579 | A4A720AA22D9E29900E9FF79 /* AddPostViewController.swift in Sources */, 580 | A4A7204122D9C19500E9FF79 /* EventService.swift in Sources */, 581 | A4A7204622D9C19500E9FF79 /* Queue.swift in Sources */, 582 | A4A7204B22D9C19500E9FF79 /* AbstractApplicationContext+Extension.swift in Sources */, 583 | A4A7202222D9C19500E9FF79 /* CommonRealmDAO.swift in Sources */, 584 | A4A720AC22D9E4E400E9FF79 /* RealmPostDAO.swift in Sources */, 585 | A4A7208622D9C67E00E9FF79 /* AllPostNavigator.swift in Sources */, 586 | A4A7204A22D9C19500E9FF79 /* TestPostApplicationContext.swift in Sources */, 587 | A4A71F7A22D9B32200E9FF79 /* ViewController.swift in Sources */, 588 | A4A7205222D9C19500E9FF79 /* TraceError.swift in Sources */, 589 | A4A7203D22D9C19500E9FF79 /* LSLogger.swift in Sources */, 590 | A4A720A122D9E1E100E9FF79 /* AddPostModel.swift in Sources */, 591 | A4A7205322D9C19500E9FF79 /* BaseNavigator.swift in Sources */, 592 | A4A7209922D9CC7E00E9FF79 /* TestPostDAO.swift in Sources */, 593 | A4A7204322D9C19500E9FF79 /* Extensions.swift in Sources */, 594 | A4A7205022D9C19500E9FF79 /* BaseViewModel.swift in Sources */, 595 | A4A7204822D9C19500E9FF79 /* ApplicationContext.swift in Sources */, 596 | A4A7204522D9C19500E9FF79 /* WeakRef.swift in Sources */, 597 | A4A7208722D9C67E00E9FF79 /* AllPostViewModel.swift in Sources */, 598 | A4A7209322D9CB4F00E9FF79 /* Post.swift in Sources */, 599 | A4A7204F22D9C19500E9FF79 /* BaseViewController.swift in Sources */, 600 | A4A7204E22D9C19500E9FF79 /* DataBinding.swift in Sources */, 601 | A4A7208922D9C67E00E9FF79 /* AllPostViewController.swift in Sources */, 602 | A4A7204C22D9C19500E9FF79 /* AbstractApplicationContext.swift in Sources */, 603 | A4A7203F22D9C19500E9FF79 /* Cache.swift in Sources */, 604 | A4A71F7822D9B32200E9FF79 /* AppDelegate.swift in Sources */, 605 | A4A7204D22D9C19500E9FF79 /* Assembler.swift in Sources */, 606 | A4A7204722D9C19500E9FF79 /* Zip3.swift in Sources */, 607 | A4A7209522D9CBBE00E9FF79 /* AllPostModel.swift in Sources */, 608 | A4A7204222D9C19500E9FF79 /* DispatchQueue.swift in Sources */, 609 | ); 610 | runOnlyForDeploymentPostprocessing = 0; 611 | }; 612 | A4A71F8422D9B32400E9FF79 /* Sources */ = { 613 | isa = PBXSourcesBuildPhase; 614 | buildActionMask = 2147483647; 615 | files = ( 616 | A4A71F8D22D9B32400E9FF79 /* iOSCleanArchitectureTests.swift in Sources */, 617 | ); 618 | runOnlyForDeploymentPostprocessing = 0; 619 | }; 620 | A4A71F8F22D9B32400E9FF79 /* Sources */ = { 621 | isa = PBXSourcesBuildPhase; 622 | buildActionMask = 2147483647; 623 | files = ( 624 | A4A71F9822D9B32400E9FF79 /* iOSCleanArchitectureUITests.swift in Sources */, 625 | ); 626 | runOnlyForDeploymentPostprocessing = 0; 627 | }; 628 | /* End PBXSourcesBuildPhase section */ 629 | 630 | /* Begin PBXTargetDependency section */ 631 | A4A71F8A22D9B32400E9FF79 /* PBXTargetDependency */ = { 632 | isa = PBXTargetDependency; 633 | target = A4A71F7322D9B32200E9FF79 /* iOSCleanArchitecture */; 634 | targetProxy = A4A71F8922D9B32400E9FF79 /* PBXContainerItemProxy */; 635 | }; 636 | A4A71F9522D9B32400E9FF79 /* PBXTargetDependency */ = { 637 | isa = PBXTargetDependency; 638 | target = A4A71F7322D9B32200E9FF79 /* iOSCleanArchitecture */; 639 | targetProxy = A4A71F9422D9B32400E9FF79 /* PBXContainerItemProxy */; 640 | }; 641 | /* End PBXTargetDependency section */ 642 | 643 | /* Begin PBXVariantGroup section */ 644 | A4A71F7B22D9B32200E9FF79 /* Main.storyboard */ = { 645 | isa = PBXVariantGroup; 646 | children = ( 647 | A4A71F7C22D9B32200E9FF79 /* Base */, 648 | ); 649 | name = Main.storyboard; 650 | sourceTree = ""; 651 | }; 652 | A4A71F8022D9B32300E9FF79 /* LaunchScreen.storyboard */ = { 653 | isa = PBXVariantGroup; 654 | children = ( 655 | A4A71F8122D9B32300E9FF79 /* Base */, 656 | ); 657 | name = LaunchScreen.storyboard; 658 | sourceTree = ""; 659 | }; 660 | /* End PBXVariantGroup section */ 661 | 662 | /* Begin XCBuildConfiguration section */ 663 | A4A71F9A22D9B32400E9FF79 /* Debug */ = { 664 | isa = XCBuildConfiguration; 665 | buildSettings = { 666 | ALWAYS_SEARCH_USER_PATHS = NO; 667 | CLANG_ANALYZER_NONNULL = YES; 668 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 669 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 670 | CLANG_CXX_LIBRARY = "libc++"; 671 | CLANG_ENABLE_MODULES = YES; 672 | CLANG_ENABLE_OBJC_ARC = YES; 673 | CLANG_ENABLE_OBJC_WEAK = YES; 674 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 675 | CLANG_WARN_BOOL_CONVERSION = YES; 676 | CLANG_WARN_COMMA = YES; 677 | CLANG_WARN_CONSTANT_CONVERSION = YES; 678 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 679 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 680 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 681 | CLANG_WARN_EMPTY_BODY = YES; 682 | CLANG_WARN_ENUM_CONVERSION = YES; 683 | CLANG_WARN_INFINITE_RECURSION = YES; 684 | CLANG_WARN_INT_CONVERSION = YES; 685 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 686 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 687 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 688 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 689 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 690 | CLANG_WARN_STRICT_PROTOTYPES = YES; 691 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 692 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 693 | CLANG_WARN_UNREACHABLE_CODE = YES; 694 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 695 | CODE_SIGN_IDENTITY = "iPhone Developer"; 696 | COPY_PHASE_STRIP = NO; 697 | DEBUG_INFORMATION_FORMAT = dwarf; 698 | ENABLE_STRICT_OBJC_MSGSEND = YES; 699 | ENABLE_TESTABILITY = YES; 700 | GCC_C_LANGUAGE_STANDARD = gnu11; 701 | GCC_DYNAMIC_NO_PIC = NO; 702 | GCC_NO_COMMON_BLOCKS = YES; 703 | GCC_OPTIMIZATION_LEVEL = 0; 704 | GCC_PREPROCESSOR_DEFINITIONS = ( 705 | "DEBUG=1", 706 | "$(inherited)", 707 | ); 708 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 709 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 710 | GCC_WARN_UNDECLARED_SELECTOR = YES; 711 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 712 | GCC_WARN_UNUSED_FUNCTION = YES; 713 | GCC_WARN_UNUSED_VARIABLE = YES; 714 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 715 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 716 | MTL_FAST_MATH = YES; 717 | ONLY_ACTIVE_ARCH = YES; 718 | SDKROOT = iphoneos; 719 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 720 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 721 | }; 722 | name = Debug; 723 | }; 724 | A4A71F9B22D9B32400E9FF79 /* Release */ = { 725 | isa = XCBuildConfiguration; 726 | buildSettings = { 727 | ALWAYS_SEARCH_USER_PATHS = NO; 728 | CLANG_ANALYZER_NONNULL = YES; 729 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 730 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 731 | CLANG_CXX_LIBRARY = "libc++"; 732 | CLANG_ENABLE_MODULES = YES; 733 | CLANG_ENABLE_OBJC_ARC = YES; 734 | CLANG_ENABLE_OBJC_WEAK = YES; 735 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 736 | CLANG_WARN_BOOL_CONVERSION = YES; 737 | CLANG_WARN_COMMA = YES; 738 | CLANG_WARN_CONSTANT_CONVERSION = YES; 739 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 740 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 741 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 742 | CLANG_WARN_EMPTY_BODY = YES; 743 | CLANG_WARN_ENUM_CONVERSION = YES; 744 | CLANG_WARN_INFINITE_RECURSION = YES; 745 | CLANG_WARN_INT_CONVERSION = YES; 746 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 747 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 748 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 749 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 750 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 751 | CLANG_WARN_STRICT_PROTOTYPES = YES; 752 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 753 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 754 | CLANG_WARN_UNREACHABLE_CODE = YES; 755 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 756 | CODE_SIGN_IDENTITY = "iPhone Developer"; 757 | COPY_PHASE_STRIP = NO; 758 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 759 | ENABLE_NS_ASSERTIONS = NO; 760 | ENABLE_STRICT_OBJC_MSGSEND = YES; 761 | GCC_C_LANGUAGE_STANDARD = gnu11; 762 | GCC_NO_COMMON_BLOCKS = YES; 763 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 764 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 765 | GCC_WARN_UNDECLARED_SELECTOR = YES; 766 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 767 | GCC_WARN_UNUSED_FUNCTION = YES; 768 | GCC_WARN_UNUSED_VARIABLE = YES; 769 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 770 | MTL_ENABLE_DEBUG_INFO = NO; 771 | MTL_FAST_MATH = YES; 772 | SDKROOT = iphoneos; 773 | SWIFT_COMPILATION_MODE = wholemodule; 774 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 775 | VALIDATE_PRODUCT = YES; 776 | }; 777 | name = Release; 778 | }; 779 | A4A71F9D22D9B32400E9FF79 /* Debug */ = { 780 | isa = XCBuildConfiguration; 781 | buildSettings = { 782 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 783 | CODE_SIGN_STYLE = Automatic; 784 | DEVELOPMENT_TEAM = SA8S3TPST5; 785 | FRAMEWORK_SEARCH_PATHS = ( 786 | "$(inherited)", 787 | "$(PROJECT_DIR)/Carthage/Build/iOS", 788 | ); 789 | INFOPLIST_FILE = iOSCleanArchitecture/Info.plist; 790 | LD_RUNPATH_SEARCH_PATHS = ( 791 | "$(inherited)", 792 | "@executable_path/Frameworks", 793 | ); 794 | PRODUCT_BUNDLE_IDENTIFIER = org.heung.iOSCleanArchitecture; 795 | PRODUCT_NAME = "$(TARGET_NAME)"; 796 | SWIFT_VERSION = 4.2; 797 | TARGETED_DEVICE_FAMILY = "1,2"; 798 | }; 799 | name = Debug; 800 | }; 801 | A4A71F9E22D9B32400E9FF79 /* Release */ = { 802 | isa = XCBuildConfiguration; 803 | buildSettings = { 804 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 805 | CODE_SIGN_STYLE = Automatic; 806 | DEVELOPMENT_TEAM = SA8S3TPST5; 807 | FRAMEWORK_SEARCH_PATHS = ( 808 | "$(inherited)", 809 | "$(PROJECT_DIR)/Carthage/Build/iOS", 810 | ); 811 | INFOPLIST_FILE = iOSCleanArchitecture/Info.plist; 812 | LD_RUNPATH_SEARCH_PATHS = ( 813 | "$(inherited)", 814 | "@executable_path/Frameworks", 815 | ); 816 | PRODUCT_BUNDLE_IDENTIFIER = org.heung.iOSCleanArchitecture; 817 | PRODUCT_NAME = "$(TARGET_NAME)"; 818 | SWIFT_VERSION = 4.2; 819 | TARGETED_DEVICE_FAMILY = "1,2"; 820 | }; 821 | name = Release; 822 | }; 823 | A4A71FA022D9B32400E9FF79 /* Debug */ = { 824 | isa = XCBuildConfiguration; 825 | buildSettings = { 826 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 827 | BUNDLE_LOADER = "$(TEST_HOST)"; 828 | CODE_SIGN_STYLE = Automatic; 829 | INFOPLIST_FILE = iOSCleanArchitectureTests/Info.plist; 830 | LD_RUNPATH_SEARCH_PATHS = ( 831 | "$(inherited)", 832 | "@executable_path/Frameworks", 833 | "@loader_path/Frameworks", 834 | ); 835 | PRODUCT_BUNDLE_IDENTIFIER = org.heung.iOSCleanArchitectureTests; 836 | PRODUCT_NAME = "$(TARGET_NAME)"; 837 | SWIFT_VERSION = 4.2; 838 | TARGETED_DEVICE_FAMILY = "1,2"; 839 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSCleanArchitecture.app/iOSCleanArchitecture"; 840 | }; 841 | name = Debug; 842 | }; 843 | A4A71FA122D9B32400E9FF79 /* Release */ = { 844 | isa = XCBuildConfiguration; 845 | buildSettings = { 846 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 847 | BUNDLE_LOADER = "$(TEST_HOST)"; 848 | CODE_SIGN_STYLE = Automatic; 849 | INFOPLIST_FILE = iOSCleanArchitectureTests/Info.plist; 850 | LD_RUNPATH_SEARCH_PATHS = ( 851 | "$(inherited)", 852 | "@executable_path/Frameworks", 853 | "@loader_path/Frameworks", 854 | ); 855 | PRODUCT_BUNDLE_IDENTIFIER = org.heung.iOSCleanArchitectureTests; 856 | PRODUCT_NAME = "$(TARGET_NAME)"; 857 | SWIFT_VERSION = 4.2; 858 | TARGETED_DEVICE_FAMILY = "1,2"; 859 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSCleanArchitecture.app/iOSCleanArchitecture"; 860 | }; 861 | name = Release; 862 | }; 863 | A4A71FA322D9B32400E9FF79 /* Debug */ = { 864 | isa = XCBuildConfiguration; 865 | buildSettings = { 866 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 867 | CODE_SIGN_STYLE = Automatic; 868 | INFOPLIST_FILE = iOSCleanArchitectureUITests/Info.plist; 869 | LD_RUNPATH_SEARCH_PATHS = ( 870 | "$(inherited)", 871 | "@executable_path/Frameworks", 872 | "@loader_path/Frameworks", 873 | ); 874 | PRODUCT_BUNDLE_IDENTIFIER = org.heung.iOSCleanArchitectureUITests; 875 | PRODUCT_NAME = "$(TARGET_NAME)"; 876 | SWIFT_VERSION = 4.2; 877 | TARGETED_DEVICE_FAMILY = "1,2"; 878 | TEST_TARGET_NAME = iOSCleanArchitecture; 879 | }; 880 | name = Debug; 881 | }; 882 | A4A71FA422D9B32400E9FF79 /* Release */ = { 883 | isa = XCBuildConfiguration; 884 | buildSettings = { 885 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 886 | CODE_SIGN_STYLE = Automatic; 887 | INFOPLIST_FILE = iOSCleanArchitectureUITests/Info.plist; 888 | LD_RUNPATH_SEARCH_PATHS = ( 889 | "$(inherited)", 890 | "@executable_path/Frameworks", 891 | "@loader_path/Frameworks", 892 | ); 893 | PRODUCT_BUNDLE_IDENTIFIER = org.heung.iOSCleanArchitectureUITests; 894 | PRODUCT_NAME = "$(TARGET_NAME)"; 895 | SWIFT_VERSION = 4.2; 896 | TARGETED_DEVICE_FAMILY = "1,2"; 897 | TEST_TARGET_NAME = iOSCleanArchitecture; 898 | }; 899 | name = Release; 900 | }; 901 | /* End XCBuildConfiguration section */ 902 | 903 | /* Begin XCConfigurationList section */ 904 | A4A71F6F22D9B32200E9FF79 /* Build configuration list for PBXProject "iOSCleanArchitecture" */ = { 905 | isa = XCConfigurationList; 906 | buildConfigurations = ( 907 | A4A71F9A22D9B32400E9FF79 /* Debug */, 908 | A4A71F9B22D9B32400E9FF79 /* Release */, 909 | ); 910 | defaultConfigurationIsVisible = 0; 911 | defaultConfigurationName = Release; 912 | }; 913 | A4A71F9C22D9B32400E9FF79 /* Build configuration list for PBXNativeTarget "iOSCleanArchitecture" */ = { 914 | isa = XCConfigurationList; 915 | buildConfigurations = ( 916 | A4A71F9D22D9B32400E9FF79 /* Debug */, 917 | A4A71F9E22D9B32400E9FF79 /* Release */, 918 | ); 919 | defaultConfigurationIsVisible = 0; 920 | defaultConfigurationName = Release; 921 | }; 922 | A4A71F9F22D9B32400E9FF79 /* Build configuration list for PBXNativeTarget "iOSCleanArchitectureTests" */ = { 923 | isa = XCConfigurationList; 924 | buildConfigurations = ( 925 | A4A71FA022D9B32400E9FF79 /* Debug */, 926 | A4A71FA122D9B32400E9FF79 /* Release */, 927 | ); 928 | defaultConfigurationIsVisible = 0; 929 | defaultConfigurationName = Release; 930 | }; 931 | A4A71FA222D9B32400E9FF79 /* Build configuration list for PBXNativeTarget "iOSCleanArchitectureUITests" */ = { 932 | isa = XCConfigurationList; 933 | buildConfigurations = ( 934 | A4A71FA322D9B32400E9FF79 /* Debug */, 935 | A4A71FA422D9B32400E9FF79 /* Release */, 936 | ); 937 | defaultConfigurationIsVisible = 0; 938 | defaultConfigurationName = Release; 939 | }; 940 | /* End XCConfigurationList section */ 941 | }; 942 | rootObject = A4A71F6C22D9B32200E9FF79 /* Project object */; 943 | } 944 | -------------------------------------------------------------------------------- /iOSCleanArchitecture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOSCleanArchitecture.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | var navigator: BaseNavigatorInterface? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | ApplicationContext.initialize(.test) 19 | let window = UIWindow(frame: UIScreen.main.bounds) 20 | let navigationViewController = UINavigationController(rootViewController: UIViewController()) 21 | navigationViewController.isNavigationBarHidden = true 22 | window.rootViewController = navigationViewController 23 | navigator = AllPostNavigator() 24 | navigator?.pushViewController(from: navigationViewController.topViewController!) 25 | 26 | self.window = window 27 | self.window?.makeKeyAndVisible() 28 | Log.setupDDLog() 29 | return true 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /iOSCleanArchitecture/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /iOSCleanArchitecture/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/ApplicationContext/AbstractApplicationContext+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AbstractApplicationContext+Extension.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension AbstractApplicationContext { 13 | func initializeViewControllerAndViewModel(storyboardName: String, identifier: String) -> ViewInfo { 14 | var vc: T1 = initializeViewController(storyboardName: storyboardName, identifier: identifier) 15 | var vm: T2 = self.resolve() 16 | vm.baseViewController = vc 17 | vc.baseViewModel = vm 18 | 19 | return ViewInfo(view: vc, viewModel: vm) 20 | } 21 | 22 | func initializeViewController(storyboardName: String, identifier: String) -> T { 23 | guard let vc = UIStoryboard(name: storyboardName, bundle: nil).instantiateViewController(withIdentifier: identifier) as? T else { 24 | fatalError("initialize failed: \(storyboardName) \(identifier)") 25 | } 26 | 27 | return vc 28 | } 29 | } 30 | 31 | class ViewInfo { 32 | let view: T1 33 | let viewModel: T2 34 | init(view: T1, viewModel: T2) { 35 | self.view = view 36 | self.viewModel = viewModel 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/ApplicationContext/ApplicationContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationContext.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ApplicationContext: AbstractApplicationContext { 12 | private static var soleInstance: AbstractApplicationContext? 13 | 14 | static private func getInstance() -> AbstractApplicationContext { 15 | guard let soleInstance = self.soleInstance else { 16 | fatalError("ApplicationContext is not initialized") 17 | } 18 | 19 | return soleInstance 20 | } 21 | 22 | static func initialize(_ source: ApplicationContextType = .prod) { 23 | soleInstance = ApplicationContext() 24 | 25 | switch source { 26 | case .prod: 27 | getInstance().registerSingleton({ PostApplicationContext() }) 28 | //getInstance().registerSingleton({ ServiceName1Context() }) 29 | case .test: 30 | getInstance().registerSingleton({ TestPostApplicationContext() }) 31 | //getInstance().registerSingleton({ ServiceName1TestContext() }) 32 | } 33 | } 34 | 35 | static func destroy() { 36 | soleInstance = nil 37 | } 38 | 39 | static func getObject(key: String) -> T { 40 | for (_, element) in getInstance().singletonMap.enumerated() { 41 | if let context = element.value as? AbstractApplicationContext, context.contains(key: key) { 42 | return context.resolve() 43 | } 44 | } 45 | 46 | fatalError("not found \(key)") 47 | } 48 | 49 | static func resolve() -> T { 50 | return getObject(key: String(describing: T.self)) 51 | } 52 | } 53 | 54 | enum ApplicationContextType { 55 | case prod 56 | case test 57 | } 58 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/ApplicationContext/ServiceContext/PostApplicationContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProdApplicationContext.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class PostApplicationContext: AbstractApplicationContext { 12 | override func configure() { 13 | registerSingleton({ return EventService() }) 14 | registerSingleton(key: "realmPostDAO", { return RealmPostDAO() }) 15 | 16 | register({ return AllPostModel(allPostDAO: self.resolve(key: "realmPostDAO")) as AllPostModelInterface }) 17 | register({ return AllPostViewModel(allPostModel: self.resolve()) }) 18 | register({ () -> AllPostViewController in 19 | let result: ViewInfo = self.initializeViewControllerAndViewModel(storyboardName: "AllPost", identifier: "AllPostViewController") 20 | return result.view 21 | }) 22 | 23 | register({ return AddPostModel(postDao: self.resolve(key: "realmPostDAO")) as AddPostModelInterface }) 24 | register({ return AddPostViewModel(addPostModel: self.resolve()) }) 25 | register({ () -> AddPostViewController in 26 | let result: ViewInfo = self.initializeViewControllerAndViewModel(storyboardName: "AddPost", identifier: "AddPostViewController") 27 | return result.view 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/ApplicationContext/ServiceContext/TestPostApplicationContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestApplicationContext.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TestPostApplicationContext: AbstractApplicationContext { 12 | override func configure() { 13 | registerSingleton({ return EventService() }) 14 | registerSingleton(key: "TestPostDAO", { return TestPostDAO() }) 15 | 16 | register({ return AllPostModel(allPostDAO: self.resolve(key: "TestPostDAO")) as AllPostModelInterface }) 17 | register({ return AllPostViewModel(allPostModel: self.resolve()) }) 18 | 19 | register({ () -> AllPostViewController in 20 | let result: ViewInfo = self.initializeViewControllerAndViewModel(storyboardName: "AllPost", identifier: "AllPostViewController") 21 | return result.view 22 | }) 23 | 24 | register({ return AddPostModel(postDao: self.resolve(key: "TestPostDAO")) as AddPostModelInterface }) 25 | register({ return AddPostViewModel(addPostModel: self.resolve()) }) 26 | register({ () -> AddPostViewController in 27 | let result: ViewInfo = self.initializeViewControllerAndViewModel(storyboardName: "AddPost", identifier: "AddPostViewController") 28 | return result.view 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Base/BaseNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseNavigator.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol BaseNavigatorInterface { 13 | func pushViewController(from: UIViewController) 14 | func presentViewController(from: UIViewController) 15 | func popViewController() 16 | func dismiss() 17 | func returnToHome() 18 | } 19 | 20 | class BaseNavigator: BaseNavigatorInterface { 21 | var topViewController: UIViewController! 22 | 23 | func pushViewController(from: UIViewController) { 24 | mainThread { 25 | let vc: T = ApplicationContext.resolve() 26 | vc.baseViewModel.baseNavigator = self 27 | from.navigationController?.pushViewController(vc, animated: true) 28 | self.topViewController = vc 29 | } 30 | } 31 | 32 | func presentViewController(from: UIViewController) { 33 | mainThread { 34 | let vc: T = ApplicationContext.resolve() 35 | vc.baseViewModel.baseNavigator = self 36 | from.present(vc, animated: true) 37 | self.topViewController = vc 38 | } 39 | } 40 | 41 | func popViewController() { 42 | mainThread { 43 | self.topViewController.navigationController?.popViewController(animated: true) 44 | } 45 | } 46 | 47 | func dismiss() { 48 | mainThread { 49 | self.topViewController.dismiss(animated: true) 50 | } 51 | } 52 | 53 | func returnToHome() { 54 | 55 | } 56 | 57 | func initializeViewController(storyboardName: String, identifier: String) -> T { 58 | guard let vc = UIStoryboard(name: storyboardName, bundle: nil).instantiateViewController(withIdentifier: identifier) as? T else { 59 | fatalError("initialize failed: \(storyboardName) \(identifier)") 60 | } 61 | 62 | return vc 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import RxSwift 12 | 13 | protocol BaseViewInterface { 14 | var baseViewModel: BaseViewModelInterface! { get set } 15 | func showToast(message: String) 16 | func showPopup(title: String?, message: String?, buttonText: String?, buttonAction: (() -> Void)?) 17 | func hidePopup() 18 | } 19 | 20 | class BaseViewController: UIViewController { 21 | var baseViewModel: BaseViewModelInterface! 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | baseViewModel.viewDidLoad() 27 | } 28 | 29 | override func viewWillAppear(_ animated: Bool) { 30 | super.viewWillAppear(animated) 31 | LSLogTrace() 32 | baseViewModel.viewWillAppear(animated) 33 | } 34 | 35 | override func viewDidAppear(_ animated: Bool) { 36 | super.viewDidAppear(animated) 37 | LSLogTrace() 38 | baseViewModel.viewDidAppear(animated) 39 | } 40 | 41 | override func viewWillDisappear(_ animated: Bool) { 42 | super.viewWillDisappear(animated) 43 | LSLogTrace() 44 | baseViewModel.viewWillDisappear(animated) 45 | } 46 | 47 | override func viewDidDisappear(_ animated: Bool) { 48 | super.viewDidDisappear(animated) 49 | LSLogTrace() 50 | baseViewModel.viewDidDisappear(animated) 51 | } 52 | } 53 | 54 | extension BaseViewController: BaseViewInterface { 55 | func showToast(message: String) { 56 | let toastLabel: UILabel = UILabel(frame: CGRect(x: view.frame.size.width / 2 - 150, y: view.frame.size.height - 150, width: 300, height: 40)) 57 | toastLabel.backgroundColor = UIColor.init(red: 50 / 255.0, green: 65 / 255.0, blue: 117 / 255.0, alpha: 1.0) 58 | toastLabel.textColor = UIColor.white 59 | toastLabel.textAlignment = NSTextAlignment.center 60 | view.addSubview(toastLabel) 61 | 62 | toastLabel.text = message 63 | toastLabel.font = UIFont.boldSystemFont(ofSize: 15) 64 | toastLabel.alpha = 1.0 65 | toastLabel.layer.cornerRadius = 10 66 | toastLabel.clipsToBounds = true 67 | 68 | UIView.animate(withDuration: 3.0, animations: { 69 | toastLabel.alpha = 0.0 70 | }, completion: { 71 | (isBool) -> Void in 72 | self.dismiss(animated: true, completion: nil) 73 | }) 74 | } 75 | 76 | func showPopup(title: String?, message: String?, buttonText: String?, buttonAction: (() -> Void)?) { 77 | 78 | } 79 | 80 | func hidePopup() { 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Base/BaseViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewModel.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol BaseViewModelInterface { 12 | var baseNavigator: BaseNavigatorInterface! { get set } 13 | var baseViewController: BaseViewInterface! { get set } 14 | func viewDidLoad() 15 | func viewWillAppear(_ animated: Bool) 16 | func viewDidAppear(_ animated: Bool) 17 | func viewWillDisappear(_ animated: Bool) 18 | func viewDidDisappear(_ animated: Bool) 19 | func popViewController() 20 | } 21 | 22 | class BaseViewModel: BaseViewModelInterface { 23 | var baseNavigator: BaseNavigatorInterface! 24 | var baseViewController: BaseViewInterface! 25 | let eventService = ApplicationContext.resolve() as EventService 26 | 27 | func viewDidLoad() { 28 | eventService.addEventListener(self) 29 | } 30 | 31 | func viewWillAppear(_ animated: Bool) { 32 | 33 | } 34 | 35 | func viewDidAppear(_ animated: Bool) { 36 | 37 | } 38 | 39 | func viewWillDisappear(_ animated: Bool) { 40 | 41 | } 42 | 43 | func viewDidDisappear(_ animated: Bool) { 44 | 45 | } 46 | 47 | func popViewController() { 48 | baseNavigator.popViewController() 49 | } 50 | } 51 | 52 | extension BaseViewModel: EventServiceListener { 53 | func onEvent(event: EventService.EventType) { 54 | switch event { 55 | case .netwrokConnect: 56 | baseViewController.showToast(message: "networkConnect!") 57 | case .netwrokDisConnect: 58 | baseViewController.showToast(message: "networkDisConnect!") 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Base/DataBinding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataBinding.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol DataBinding { 12 | associatedtype Input 13 | associatedtype Output 14 | 15 | func bind(input: Input) -> Output 16 | } 17 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Base/TraceError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TraceError.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TraceError: Error { 12 | let message: String 13 | let code: Int 14 | let userInfo: Any? 15 | let prevError: TraceError? 16 | private let errorPlace: String 17 | 18 | init(message: String, code: Int, userInfo: Any? = nil, prevError: TraceError? = nil, file: String = #file, function: String = #function, line: UInt = #line) { 19 | self.message = message 20 | self.code = code 21 | self.userInfo = userInfo 22 | self.prevError = prevError 23 | self.errorPlace = "\(file) \(line): \(function) -" 24 | } 25 | 26 | func getStackTrace() -> String { 27 | return getStackTrace(depth: 1) 28 | } 29 | 30 | private func getStackTrace(depth: Int) -> String { 31 | let prefix = depth == 1 ? "🔶[ERROR TRACE]🔶\n\t 🔶 " : "" 32 | return "\(prefix)\(errorPlace) \(String(describing: self)): \(message) \(prevError != nil ? "\n\t 🔶" : "") \(prevError?.getStackTrace(depth: depth + 1) ?? "")" 33 | } 34 | 35 | func getSourceError() -> TraceError { 36 | return prevError?.getSourceError() ?? self 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Cache/Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Cache.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Cache { 12 | associatedtype CachedDataType 13 | func add(key: String, value: CachedDataType) 14 | func get(key: String) throws -> CachedDataType 15 | func remove(key: String) 16 | func getOrAdd(key: String, execute: () throws -> CachedDataType?) throws -> CachedDataType 17 | func getOrAddAsync(key: String, result: @escaping (CachedDataType) -> Void, execute: (@escaping (CachedDataType) -> Void) -> Void) 18 | func getAndRenewOrAddAsync(key: String, result: @escaping (CachedDataType) -> Void, renew: @escaping (@escaping (CachedDataType) -> Void) -> Void, execute: (@escaping (CachedDataType) -> Void) -> Void) 19 | } 20 | 21 | class MemoryCache: Cache { 22 | enum CacheError: Error { 23 | case notFound(message: String) 24 | } 25 | private var cachedDataMap = [String: T]() 26 | private var cachePriority = ArrayQueue() 27 | private(set) var limitMaxSize = 100 28 | 29 | init(limitMaxSize: Int) { 30 | self.limitMaxSize = limitMaxSize 31 | } 32 | 33 | func add(key: String, value: CachedDataType) { 34 | cachedDataMap[key] = value 35 | cachePriority.push(key) 36 | removeOldData() 37 | } 38 | 39 | func get(key: String) throws -> T { 40 | guard let cachedData = cachedDataMap[key] else { 41 | throw CacheError.notFound(message: "Not Found: \(key)") 42 | } 43 | 44 | cachePriority.goBack(key) 45 | return cachedData 46 | } 47 | 48 | func remove(key: String) { 49 | cachePriority.pop() 50 | cachedDataMap.removeValue(forKey: key) 51 | } 52 | 53 | func getOrAdd(key: String, execute: () throws -> T?) throws -> T { 54 | do { 55 | let cachedData = try get(key: key) 56 | return cachedData 57 | } catch { 58 | guard let cachedData = try execute() else { throw CacheError.notFound(message: "execute return is nil.") } 59 | add(key: key, value: cachedData) 60 | return cachedData 61 | } 62 | } 63 | 64 | func getOrAddAsync(key: String, result: @escaping (T) -> Void, execute: (@escaping (T) -> Void) -> Void) { 65 | do { 66 | result(try get(key: key)) 67 | } catch { 68 | execute({ [weak self] value in 69 | guard let self = self else { return } 70 | self.add(key: key, value: value) 71 | result(value) 72 | }) 73 | } 74 | } 75 | 76 | func getAndRenewOrAddAsync(key: String, result: @escaping (T) -> Void, renew: @escaping (@escaping (T) -> Void) -> Void, execute: (@escaping (T) -> Void) -> Void) { 77 | do { 78 | result(try get(key: key)) 79 | renew({ [weak self] value in 80 | guard let self = self else { return } 81 | self.remove(key: key) 82 | self.add(key: key, value: value) 83 | }) 84 | } catch { 85 | execute({ [weak self] value in 86 | guard let self = self else { return } 87 | self.add(key: key, value: value) 88 | result(value) 89 | }) 90 | } 91 | } 92 | 93 | private func removeOldData() { 94 | while(isLimit()) { 95 | guard let key = cachePriority.front() else { return } 96 | remove(key: key) 97 | } 98 | } 99 | 100 | func isLimit() -> Bool { 101 | return cachedDataMap.count > limitMaxSize 102 | } 103 | 104 | typealias CachedDataType = T 105 | } 106 | 107 | class ImageMemoryCache: MemoryCache { 108 | private(set) var currentSize = 0 109 | 110 | override func add(key: String, value: Data) { 111 | super.add(key: key, value: value) 112 | currentSize += value.count 113 | } 114 | 115 | override func remove(key: String) { 116 | let value = try? get(key: key) 117 | currentSize -= value?.count ?? 0 118 | super.remove(key: key) 119 | } 120 | 121 | override func isLimit() -> Bool { 122 | return currentSize > limitMaxSize 123 | } 124 | } 125 | 126 | class RealmCache { 127 | 128 | } 129 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/IOC/AbstractApplicationContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationContext.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ApplicationContextInterface { 12 | func resolve() -> T 13 | func resolve(key: String) -> T 14 | func contains(key: String) -> Bool 15 | } 16 | 17 | protocol AbstractApplicationContextInterface: ApplicationContextInterface { 18 | func configure() 19 | func register(_ assemble: @escaping () -> T) 20 | func register(key: String, _ assemble: @escaping () -> T) 21 | func registerSingleton(_ assemble: @escaping () -> T) 22 | func registerSingleton(key: String, _ assemble: @escaping () -> T) 23 | } 24 | 25 | class AbstractApplicationContext: AbstractApplicationContextInterface { 26 | private(set) var factory = Assembler() 27 | private(set) var singletonMap = [String: Any]() 28 | init() { 29 | configure() 30 | } 31 | 32 | func resolve() -> T { 33 | let key = String(describing: T.self) 34 | guard let object = singletonMap[key] as? T else { 35 | return factory.resolve() as T 36 | } 37 | return object 38 | } 39 | 40 | func resolve(key: String) -> T { 41 | guard let object = singletonMap[key] as? T else { 42 | return factory.resolve(key: key) 43 | } 44 | return object 45 | } 46 | 47 | func configure() { 48 | 49 | } 50 | 51 | func register(_ assemble: @escaping () -> T) { 52 | factory.register(assemble) 53 | } 54 | 55 | func register(key: String, _ assemble: @escaping () -> T) { 56 | factory.register(key: key, assemble) 57 | } 58 | 59 | func registerSingleton(_ assemble: @escaping () -> T) { 60 | self.register(assemble) 61 | let key = String(describing: T.self) 62 | singletonMap[key] = factory.resolve(key: key) as T 63 | } 64 | 65 | func registerSingleton(key: String, _ assemble: @escaping () -> T) { 66 | self.register(key: key, assemble) 67 | singletonMap[key] = factory.resolve(key: key) as T 68 | } 69 | 70 | func contains(key: String) -> Bool { 71 | return factory.contains(key: key) || singletonMap[key] != nil 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/IOC/Assembler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Assembler.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Assembler { 12 | var assembles = [String: Any]() 13 | 14 | func register(_ assemble: @escaping () -> T) { 15 | let key = String(describing: T.self) 16 | register(key: key, assemble) 17 | } 18 | 19 | func register(key: String, _ assemble: @escaping () -> T) { 20 | guard assembles.keys.contains(key) == false else { 21 | fatalError("Already registered: \(key)") 22 | } 23 | assembles[key] = assemble 24 | } 25 | 26 | func resolve() -> T { 27 | let key = String(describing: T.self) 28 | return resolve(key: key) 29 | } 30 | 31 | func resolve(key: String) -> T { 32 | guard let maker = assembles[key] as? () -> T else { 33 | fatalError("not found \(key)") 34 | } 35 | 36 | return maker() 37 | } 38 | 39 | func contains(key: String) -> Bool { 40 | return assembles[key] != nil 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Logger/LSLogger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LSLogger.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by yonghoon park on 2019. 6. 11.. 6 | // Copyright © 2019년 yonghoon park All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum Service: String { 12 | case Core = "[Core]" 13 | case Ambient = "[Ambient]" 14 | case Test = "[Test]" 15 | case IBeacon = "[IBeacon]" 16 | case NONE = "" 17 | } 18 | 19 | enum LogLevel: Int { 20 | case All = 0 21 | case Trace 22 | case Debug 23 | case Info 24 | case Warning 25 | case Error 26 | case Off 27 | } 28 | 29 | #if DEBUG 30 | var LSLogLevel = LogLevel.All 31 | #else 32 | var LSLogLevel = LogLevel.Debug 33 | #endif 34 | 35 | func LSLogTrace(_ service: Service? = .NONE, _ logText: @autoclosure () -> String = "Enter", file: String = #file, function: String = #function, line: UInt = #line) { 36 | guard LSLogLevel.rawValue == LogLevel.All.rawValue || LSLogLevel.rawValue == LogLevel.Trace.rawValue else { return } 37 | Log.trace(wrap(logText(), service: service), file: file, function: function, line: line) 38 | } 39 | 40 | func LSLogDebug(_ service: Service?, _ logText: @autoclosure () -> String, file: String = #file, function: String = #function, line: UInt = #line) { 41 | guard LSLogLevel.rawValue == LogLevel.All.rawValue || LSLogLevel.rawValue == LogLevel.Debug.rawValue else { return } 42 | Log.debug(wrap(logText(), service: service), file: file, function: function, line: line) 43 | } 44 | 45 | func LSLogInfo(_ service: Service?, _ logText: @autoclosure () -> String, file: String = #file, function: String = #function, line: UInt = #line) { 46 | guard LSLogLevel.rawValue == LogLevel.All.rawValue || LSLogLevel.rawValue == LogLevel.Info.rawValue else { return } 47 | Log.info(wrap(logText(), service: service), file: file, function: function, line: line) 48 | } 49 | 50 | func LSLogWarn(_ service: Service?, _ logText: @autoclosure () -> String, file: String = #file, function: String = #function, line: UInt = #line) { 51 | guard LSLogLevel.rawValue == LogLevel.All.rawValue || LSLogLevel.rawValue == LogLevel.Warning.rawValue else { return } 52 | Log.warning(wrap(logText(), service: service), file: file, function: function, line: line) 53 | } 54 | 55 | func LSLogError(_ service: Service?, _ logText: @autoclosure () -> String, file: String = #file, function: String = #function, line: UInt = #line) { 56 | guard LSLogLevel.rawValue == LogLevel.All.rawValue || LSLogLevel.rawValue == LogLevel.Error.rawValue else { return } 57 | Log.error(wrap(logText(), service: service), file: file, function: function, line: line) 58 | } 59 | 60 | private func wrap(_ message: String, service: Service?) -> String { 61 | if let service = service { 62 | return "\(service.rawValue) \(message)" 63 | } else { 64 | return "\(message)" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Logger/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by yonghoon park on 2019. 6. 11.. 6 | // Copyright © 2019년 yonghoon park All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CocoaLumberjackSwift 11 | 12 | enum LogEvent: String { 13 | case trace = "TRACE" 14 | case debug = "DEBUG" 15 | case info = "INFO" 16 | case warning = "WARNING" 17 | case error = "ERROR" 18 | } 19 | 20 | class Log { 21 | class func trace( _ object: Any, file: String = #file, function: String = #function, line: UInt = #line) { 22 | DDLogVerbose("⚪️ \(LogEvent.trace.rawValue) \(sourceFileName(filePath: file))(\(line)): \(function) - \(object)") 23 | } 24 | 25 | class func debug( _ object: Any, file: String = #file, function: String = #function, line: UInt = #line) { 26 | DDLogDebug("☑️ \(LogEvent.debug.rawValue) \(sourceFileName(filePath: file))(\(line)): \(function) - \(object)") 27 | } 28 | 29 | class func info( _ object: Any, file: String = #file, function: String = #function, line: UInt = #line) { 30 | DDLogInfo("🔵 \(LogEvent.info.rawValue) \(sourceFileName(filePath: file))(\(line)): \(function) - \(object)") 31 | } 32 | 33 | class func warning( _ object: Any, file: String = #file, function: String = #function, line: UInt = #line) { 34 | DDLogWarn("🔶 \(LogEvent.warning.rawValue) \(sourceFileName(filePath: file))(\(line)): \(function) - \(object)") 35 | } 36 | 37 | class func error( _ object: Any, file: String = #file, function: String = #function, line: UInt = #line) { 38 | DDLogError("🔴 \(LogEvent.error.rawValue) \(sourceFileName(filePath: file))(\(line)): \(function) - \(object)") 39 | } 40 | 41 | private class func sourceFileName(filePath: String) -> String { 42 | guard let fullFileName = filePath.components(separatedBy: "/").last, let fileName = fullFileName.components(separatedBy: ".").first else { return "" } 43 | return fileName 44 | } 45 | 46 | } 47 | 48 | // MARK: CocoaLumberjack - DDLog 49 | extension Log { 50 | class var fullLogData: NSMutableData { 51 | let fileLoger = DDFileLogger() 52 | let sortedLogFileInfos = fileLoger.logFileManager.sortedLogFileInfos.reversed() 53 | 54 | let logData = NSMutableData() 55 | DDLog.flushLog() 56 | 57 | for logFileInfo in sortedLogFileInfos { 58 | if let fileData: NSData = NSData(contentsOfFile: logFileInfo.filePath) { 59 | logData.append(fileData as Data) 60 | } 61 | } 62 | return logData 63 | } 64 | 65 | class func setupDDLog() { 66 | removeLogStack() 67 | 68 | DDLog.add(DDOSLogger.sharedInstance) 69 | let fileLogger: DDFileLogger = DDFileLogger() // File Logger 70 | fileLogger.rollingFrequency = TimeInterval(60*60*24) // 24 hours 71 | fileLogger.logFileManager.maximumNumberOfLogFiles = 10 72 | fileLogger.logFileManager.logFilesDiskQuota = (UInt64(10 * 1024 * 1024)) // 10 MB 73 | fileLogger.maximumFileSize = 1024 * 1024 // 1 MB 74 | DDLog.add(fileLogger) 75 | } 76 | 77 | class func saveLogFile() { 78 | let filePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0].appending("/LifeStyle.txt") 79 | 80 | fullLogData.write(toFile: filePath, atomically: true) 81 | } 82 | 83 | class func removeLogStack() { 84 | let fileLogger = DDFileLogger() 85 | fileLogger.rollLogFile(withCompletion: { 86 | for filename: String in fileLogger.logFileManager.sortedLogFilePaths { 87 | do { 88 | try FileManager.default.removeItem(atPath: filename) 89 | } catch { 90 | LSLogError(.NONE, error.localizedDescription) 91 | } 92 | } 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Utils/DispatchQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by LYJ on 28/05/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func mainThread(_ execute: @escaping () -> Void) { 12 | DispatchQueue.main.async(execute: { 13 | execute() 14 | }) 15 | } 16 | 17 | func mainThreadAsyncAfter(_ seconds: Double, execute: @escaping () -> Void) { 18 | DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { 19 | execute() 20 | } 21 | } 22 | 23 | func mainThreadAsyncAfter(_ seconds: Double, execute: DispatchWorkItem) { 24 | DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: execute) 25 | } 26 | 27 | func backgroundThread(_ execute: @escaping () -> Void) { 28 | DispatchQueue.global(qos: .background).async(execute: { 29 | execute() 30 | }) 31 | } 32 | 33 | func backgroundThreadAsyncAfter(_ seconds: Double, execute: @escaping () -> Void) { 34 | DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + seconds) { 35 | execute() 36 | } 37 | } 38 | 39 | func backgroundThreadAsyncAfter(_ seconds: Double, execute: DispatchWorkItem) { 40 | DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + seconds, execute: execute) 41 | } 42 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Utils/EventService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventService.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by LYJ on 30/05/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class EventService { 12 | enum EventType { 13 | case netwrokConnect 14 | case netwrokDisConnect 15 | } 16 | 17 | private var listeners = [WeakRef]() 18 | 19 | func addEventListener(_ listener: EventServiceListener) { 20 | listeners.append(WeakRef(value: listener as AnyObject)) 21 | 22 | } 23 | 24 | func removeListener(_ listener: EventServiceListener) { 25 | 26 | } 27 | 28 | private func removeListener(_ weakRefListener: WeakRef) { 29 | 30 | } 31 | 32 | func notifyEvent(event: EventType) { 33 | listeners.forEach { weakRefListener in 34 | mainThread { 35 | guard let listener = weakRefListener.value as? EventServiceListener else { 36 | self.removeListener(weakRefListener) 37 | return 38 | } 39 | listener.onEvent(event: event) 40 | } 41 | } 42 | } 43 | } 44 | 45 | protocol EventServiceListener: AnyObject { 46 | func onEvent(event: EventService.EventType) 47 | } 48 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Utils/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by LYJ on 21/05/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Array { 12 | subscript (safe index: Int) -> Element? { 13 | return indices ~= index ? self[index] : nil 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Utils/Queue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Queue.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by LYJ on 21/05/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Queue { 12 | associatedtype T 13 | mutating func push(_ element: T) 14 | mutating func front() -> T? 15 | mutating func pop() -> T? 16 | mutating func popAll() -> [T] 17 | mutating func goBack(_ element: T) 18 | func size() -> Int 19 | func isEmpty() -> Bool 20 | } 21 | 22 | struct ArrayQueue: Queue { 23 | private var elements = Array() 24 | 25 | mutating func push(_ element: T) { 26 | elements.append(element) 27 | } 28 | 29 | mutating func front() -> T? { 30 | return elements.first 31 | } 32 | 33 | @discardableResult 34 | mutating func pop() -> T? { 35 | return elements.isEmpty ? nil : elements.removeFirst() 36 | } 37 | 38 | @discardableResult 39 | mutating func popAll() -> [T] { 40 | let result = elements 41 | elements.removeAll() 42 | return result 43 | } 44 | 45 | mutating func goBack(_ element: T) { 46 | if let index = elements.firstIndex(where: { return $0 == element }) { 47 | elements.remove(at: index) 48 | } 49 | push(element) 50 | } 51 | 52 | func size() -> Int { 53 | return elements.count 54 | } 55 | 56 | func isEmpty() -> Bool { 57 | return elements.isEmpty 58 | } 59 | } 60 | 61 | extension ArrayQueue: Sequence, IteratorProtocol { 62 | mutating func next() -> T? { return pop() } 63 | } 64 | 65 | struct ThreadSafeArrayQueue: Queue { 66 | private let lock = NSLock() 67 | private var queue = ArrayQueue() 68 | 69 | mutating func push(_ element: T) { 70 | lock.lock() 71 | defer { lock.unlock() } 72 | queue.push(element) 73 | } 74 | 75 | mutating func front() -> T? { 76 | return queue.front() 77 | } 78 | 79 | @discardableResult 80 | mutating func pop() -> T? { 81 | lock.lock() 82 | defer { lock.unlock() } 83 | return queue.pop() 84 | } 85 | 86 | mutating func popAll() -> [T] { 87 | lock.lock() 88 | defer { lock.unlock() } 89 | return queue.popAll() 90 | } 91 | 92 | mutating func goBack(_ element: T) { 93 | lock.lock() 94 | defer { lock.unlock() } 95 | queue.goBack(element) 96 | } 97 | 98 | func size() -> Int { 99 | lock.lock() 100 | defer { lock.unlock() } 101 | return queue.size() 102 | } 103 | 104 | func isEmpty() -> Bool { 105 | lock.lock() 106 | defer { lock.unlock() } 107 | return queue.isEmpty() 108 | } 109 | } 110 | 111 | extension ThreadSafeArrayQueue: Sequence, IteratorProtocol { 112 | mutating func next() -> T? { return pop() } 113 | } 114 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Utils/WeakRef.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeakRef.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by LYJ on 21/05/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct WeakRef where T: AnyObject { 12 | private(set) weak var value: T? 13 | 14 | init(value: T?) { 15 | self.value = value 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Commons/Utils/Zip3.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Zip3.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by LYJ on 21/05/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Zip3Sequence: Sequence, IteratorProtocol { 12 | private let _next: () -> (E1, E2, E3)? 13 | 14 | init(_ s1: S1, _ s2: S2, _ s3: S3) where S1.Element == E1, S2.Element == E2, S3.Element == E3 { 15 | var it1 = s1.makeIterator() 16 | var it2 = s2.makeIterator() 17 | var it3 = s3.makeIterator() 18 | _next = { 19 | guard let e1 = it1.next(), let e2 = it2.next(), let e3 = it3.next() else { return nil } 20 | return (e1, e2, e3) 21 | } 22 | } 23 | 24 | mutating func next() -> (E1, E2, E3)? { 25 | return _next() 26 | } 27 | } 28 | 29 | func zip3(_ s1: S1, _ s2: S2, _ s3: S3) -> Zip3Sequence { 30 | return Zip3Sequence(s1, s2, s3) 31 | } 32 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Domain/Entity/Post.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Post.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Post { 12 | var id: Int 13 | var title: String 14 | var contents: String 15 | } 16 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Domain/Model/AddPostModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPostModel.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | protocol AddPostModelInterface { 13 | func addPost(post: Post) 14 | } 15 | 16 | class AddPostModel: AddPostModelInterface { 17 | private let postDao: PostInterface! 18 | 19 | init(postDao: PostInterface) { 20 | self.postDao = postDao 21 | } 22 | 23 | func addPost(post: Post) { 24 | return postDao.addPost(post:post) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Domain/Model/AllPostModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AllPostModel.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | protocol AllPostModelInterface { 13 | func getAllPost() -> BehaviorSubject<[Post]> 14 | } 15 | 16 | class AllPostModel: AllPostModelInterface { 17 | private let allPostDAO: PostInterface! 18 | 19 | init(allPostDAO: PostInterface) { 20 | self.allPostDAO = allPostDAO 21 | } 22 | 23 | func getAllPost() -> BehaviorSubject<[Post]> { 24 | return allPostDAO.getAllPost() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Domain/RepositoryInterface/DAO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DAO.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum DAOError: Error { 12 | case createFail(String) 13 | case readFail(String) 14 | case updateFail(String) 15 | case deleteFail(String) 16 | } 17 | 18 | protocol DAO { 19 | associatedtype KEY 20 | associatedtype VALUE 21 | 22 | func create(value: VALUE) throws 23 | func read(key: KEY) throws -> VALUE 24 | func update(value: VALUE) throws 25 | func delete(key: KEY) throws 26 | } 27 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Domain/RepositoryInterface/PostInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AllPostDAOInterface.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | protocol PostInterface { 13 | func getAllPost() -> BehaviorSubject<[Post]> 14 | func getPost(id: Int) throws -> Post 15 | func addPost(post: Post) 16 | } 17 | 18 | class NotFoundPost: TraceError { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Repository/CommonRealmDAO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonRealmDAO.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | import RxSwift 12 | import RxCocoa 13 | import UIKit 14 | 15 | protocol RealmEntity { 16 | associatedtype ENTITY 17 | var query: String { get set } 18 | func mapping(entity: ENTITY) 19 | func getEntity() -> ENTITY 20 | } 21 | 22 | protocol RealmEntityMapper { 23 | associatedtype REALM_ENTITY 24 | func getRealmEntity() -> REALM_ENTITY 25 | } 26 | 27 | class CommonRealmDAO: DAO where VALUE.REALM_ENTITY: Object, VALUE.REALM_ENTITY: RealmEntity { 28 | typealias KEY = String 29 | var realm: Realm! 30 | 31 | init() { 32 | guard let m = try? Realm(configuration: getRealmConfig()) else { 33 | fatalError("initialize failed.") 34 | } 35 | realm = m 36 | } 37 | 38 | func getRealmConfig() -> Realm.Configuration { 39 | return Realm.Configuration() 40 | } 41 | 42 | func create(value: VALUE) throws { 43 | do { 44 | try realm.write { 45 | realm.add(value.getRealmEntity()) 46 | } 47 | } catch let error as NSError { 48 | throw DAOError.createFail(error.description) 49 | } 50 | } 51 | 52 | func read(key: KEY) throws -> VALUE { 53 | let value: VALUE.REALM_ENTITY = try read(key: key) 54 | if let result = value.getEntity() as? VALUE { 55 | return result 56 | } 57 | 58 | throw DAOError.readFail("not found \(key)/ getEntity failed.") 59 | } 60 | 61 | private func read(key: KEY) throws -> VALUE.REALM_ENTITY { 62 | if let result = realm.object(ofType: VALUE.REALM_ENTITY.self, forPrimaryKey: key) { 63 | return result 64 | } 65 | 66 | throw DAOError.readFail("not found \(key)") 67 | } 68 | 69 | func update(value: VALUE) throws { 70 | do { 71 | try realm.write { 72 | realm.add(value.getRealmEntity(), update: true) 73 | } 74 | } catch let error as NSError { 75 | throw DAOError.updateFail(error.description) 76 | } 77 | } 78 | 79 | func delete(key: KEY) throws { 80 | do { 81 | try realm.write { 82 | let value: VALUE.REALM_ENTITY = try read(key: key) 83 | realm.delete(value) 84 | } 85 | } catch let error as NSError { 86 | throw DAOError.deleteFail(error.description) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Repository/RealmPostDAO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReamPostDAO.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RealmSwift 12 | 13 | class RealmPostDAO: PostInterface { 14 | fileprivate let data = BehaviorSubject<[Post]>(value: []) 15 | private var dataStore = CommonRealmDAO() 16 | init() { 17 | 18 | } 19 | 20 | func getAllPost() -> BehaviorSubject<[Post]> { 21 | return data 22 | } 23 | 24 | func getPost(id: Int) throws -> Post { 25 | throw NotFoundPost(message: "not found post: \(id)", code: 0) 26 | } 27 | 28 | func addPost(post: Post) { 29 | 30 | } 31 | } 32 | 33 | class PostRealmEntity: Object, RealmEntity { 34 | typealias ENTITY = Post 35 | @objc dynamic var id: Int = 0 36 | @objc dynamic var title: String = "" 37 | @objc dynamic var contents: String = "" 38 | 39 | var query: String = "" 40 | 41 | func mapping(entity: ENTITY) { 42 | id = entity.id 43 | title = entity.title 44 | contents = entity.contents 45 | } 46 | 47 | func getEntity() -> ENTITY { 48 | return Post(id: id, title: title, contents: contents) 49 | } 50 | 51 | override static func primaryKey() -> String? { 52 | return "id" 53 | } 54 | } 55 | 56 | extension Post: RealmEntityMapper { 57 | typealias REALM_ENTITY = PostRealmEntity 58 | func getRealmEntity() -> REALM_ENTITY { 59 | let mapper = PostRealmEntity() 60 | mapper.mapping(entity: self) 61 | return mapper 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Repository/TestPostDAO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestAllPostDAO.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | class TestPostDAO: PostInterface { 13 | fileprivate let data = BehaviorSubject<[Post]>(value: []) 14 | private var dataStore = [Int: Post]() 15 | 16 | init() { 17 | //add default value 18 | for i in 1..<5 { 19 | dataStore[i] = Post(id: i, title: "TestData Title\(i)", contents: "contents\(i)") 20 | } 21 | data.onNext(Array(dataStore.values)) 22 | } 23 | 24 | func getAllPost() -> BehaviorSubject<[Post]> { 25 | return data 26 | } 27 | 28 | func getPost(id: Int) throws -> Post { 29 | if let result = (try? data.value().first { $0.id == id }) as? Post { 30 | return result 31 | } 32 | 33 | throw NotFoundPost(message: "not found post: \(id)", code: 0) 34 | } 35 | 36 | func addPost(post: Post) { 37 | dataStore[post.id] = post 38 | data.onNext(Array(dataStore.values)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezero9/iOSCleanArchitecture/144ef062034b27fa43b86b8b255526d1e95d5f6e/iOSCleanArchitecture/Scenes/.DS_Store -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AddPost/AddPost.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AddPost/AddPostNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPostNavigator.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 17/01/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol AddPostNavigatorInterface: BaseNavigatorInterface { 13 | } 14 | 15 | class AddPostNavigator: BaseNavigator { 16 | } 17 | 18 | extension AddPostNavigator: AddPostNavigatorInterface { 19 | } 20 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AddPost/AddPostViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPostViewController.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 17/01/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class AddPostViewController: BaseViewController { 14 | @IBOutlet weak var cancelButton: UIBarButtonItem! 15 | @IBOutlet weak var saveButton: UIBarButtonItem! 16 | @IBOutlet weak var titleTextField: UITextField! 17 | @IBOutlet weak var contentsTextField: UITextView! 18 | 19 | override var baseViewModel: BaseViewModelInterface! { 20 | didSet { 21 | addPostViewModel = baseViewModel as? AddPostViewModel 22 | } 23 | } 24 | 25 | private var addPostViewModel: AddPostViewModel! 26 | private let disposeBag = DisposeBag() 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | drawContents() 31 | 32 | let ouput = addPostViewModel.bind(input: AddPostViewModel.Input(title: titleTextField.rx.text.orEmpty.asDriver(), 33 | contents: contentsTextField.rx.text.orEmpty.asDriver(), 34 | saveBtn: saveButton.rx.tap.asDriver(), 35 | cancelBtn: cancelButton.rx.tap.asDriver())) 36 | 37 | ouput.saveBtnEnable.drive(saveButton.rx.isEnabled).disposed(by: disposeBag) 38 | } 39 | 40 | private func drawContents() { 41 | self.contentsTextField.layer.borderColor = UIColor.gray.cgColor 42 | self.contentsTextField.layer.borderWidth = 2.3 43 | self.contentsTextField.layer.cornerRadius = 15 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AddPost/AddPostViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPostViewModel.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 17/01/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class AddPostViewModel: BaseViewModel { 14 | private let addPostModel: AddPostModelInterface 15 | private var navigator: AddPostNavigatorInterface? 16 | private let disposeBag = DisposeBag() 17 | 18 | override var baseNavigator: BaseNavigatorInterface! { 19 | didSet { 20 | navigator = baseNavigator as? AddPostNavigatorInterface 21 | } 22 | } 23 | 24 | init(addPostModel: AddPostModelInterface) { 25 | self.addPostModel = addPostModel 26 | } 27 | } 28 | 29 | extension AddPostViewModel: DataBinding { 30 | func bind(input: AddPostViewModel.Input) -> AddPostViewModel.Output { 31 | let values = Driver.combineLatest(input.title, input.contents) { ($0, $1) } 32 | let enableSave = Driver.combineLatest(input.title, input.contents) { !$0.isEmpty && !$1.isEmpty } 33 | let save = input.saveBtn.withLatestFrom(values).map { (title, contents) -> Post in 34 | return Post(id: 11, title: title, contents: contents) 35 | }.flatMapLatest { post -> SharedSequence in 36 | self.addPostModel.addPost(post: post) 37 | return Driver.just(()) 38 | } 39 | 40 | Driver 41 | .of(input.cancelBtn, save) 42 | .merge() 43 | .drive(onNext: { [weak self] _ in self?.navigator?.dismiss() }) 44 | .disposed(by: disposeBag) 45 | 46 | return Output(saveBtnEnable: enableSave) 47 | } 48 | 49 | struct Input { 50 | let title: Driver 51 | let contents: Driver 52 | let saveBtn: Driver 53 | let cancelBtn: Driver 54 | } 55 | 56 | struct Output { 57 | let saveBtnEnable: Driver 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AllPost/AllPost.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AllPost/AllPostNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AllPostNavigator.swift 3 | // ExampleTODO 4 | // 5 | // Created by YoungJu Lee on 16/01/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol AllPostNavigatorInterface: BaseNavigatorInterface { 13 | func toAddPost() 14 | func toEditPost() 15 | func toDetailPost() 16 | } 17 | 18 | class AllPostNavigator: BaseNavigator { 19 | } 20 | 21 | extension AllPostNavigator: AllPostNavigatorInterface { 22 | func toAddPost() { 23 | AddPostNavigator().presentViewController(from: topViewController) 24 | } 25 | 26 | func toEditPost() { 27 | //TODO EditPostNavigator().pushViewcontroller(from: topViewController) 28 | } 29 | 30 | func toDetailPost() { 31 | //TODO DetailPostNavigator().pushViewcontroller(from: topViewController) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AllPost/AllPostViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AllPostViewController.swift 3 | // ExampleTODO 4 | // 5 | // Created by YoungJu Lee on 16/01/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class AllPostViewController: BaseViewController { 14 | @IBOutlet weak var addPostButton: UIBarButtonItem! 15 | @IBOutlet weak var postTableView: UITableView! 16 | 17 | override var baseViewModel: BaseViewModelInterface! { 18 | didSet { 19 | allPostViewModel = baseViewModel as? AllPostViewModel 20 | } 21 | } 22 | 23 | private var allPostViewModel: AllPostViewModel! 24 | private let disposeBag = DisposeBag() 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | let output = allPostViewModel.bind(input: AllPostViewModel.Input(addPostTrigger: addPostButton.rx.tap.asDriver())) 30 | 31 | output.allPost.asObservable().bind(to: self.postTableView.rx.items(cellIdentifier: "cell", cellType: UITableViewCell.self)) { 32 | (index, data, cell) in 33 | cell.textLabel?.text = data.title 34 | }.disposed(by: disposeBag) 35 | 36 | output.addPost.drive().disposed(by: disposeBag) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/AllPost/AllPostViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AllPostViewModel.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 17/01/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class AllPostViewModel: BaseViewModel { 14 | private let allPostModel: AllPostModelInterface 15 | private var navigator: AllPostNavigator! 16 | override var baseNavigator: BaseNavigatorInterface! { 17 | didSet { 18 | navigator = baseNavigator as? AllPostNavigator 19 | } 20 | } 21 | 22 | init(allPostModel: AllPostModelInterface) { 23 | self.allPostModel = allPostModel 24 | } 25 | } 26 | 27 | extension AllPostViewModel: DataBinding { 28 | func bind(input: AllPostViewModel.Input) -> AllPostViewModel.Output { 29 | let addPost = input.addPostTrigger.do(onNext: { self.navigator?.toAddPost() }) 30 | let output = Output(allPost: allPostModel.getAllPost(), 31 | addPost: addPost) 32 | 33 | return output 34 | } 35 | 36 | struct Input { 37 | let addPostTrigger: Driver 38 | } 39 | 40 | struct Output{ 41 | let allPost: BehaviorSubject<[Post]> 42 | let addPost: Driver 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/DetailPost/DetailPost.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/DetailPost/DetailPostNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailPostNavigator.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 03/06/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol DetailNavigatorInterface: BaseNavigatorInterface { 12 | 13 | } 14 | 15 | class DetailNavigator: BaseNavigator { 16 | 17 | } 18 | 19 | 20 | extension DetailNavigator : DetailNavigatorInterface{ 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/DetailPost/DetailPostViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailPostViewController.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 03/06/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol DetailPostViewInterface: BaseViewInterface { 12 | 13 | } 14 | 15 | class DetailPostViewController: BaseViewController { 16 | 17 | } 18 | 19 | extension DetailPostViewController: DetailPostViewInterface { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/Scenes/DetailPost/DetailPostViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailPostViewModel.swift 3 | // ExampleTODO 4 | // 5 | // Created by LYJ on 03/06/2019. 6 | // Copyright © 2019 이영주. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol DetailViewInterface: BaseViewModelInterface { 12 | 13 | } 14 | 15 | class DetailViewModel: BaseViewModel{ 16 | 17 | } 18 | 19 | extension DetailViewModel: DetailViewInterface { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /iOSCleanArchitecture/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // iOSCleanArchitecture 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /iOSCleanArchitectureTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /iOSCleanArchitectureTests/iOSCleanArchitectureTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOSCleanArchitectureTests.swift 3 | // iOSCleanArchitectureTests 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import iOSCleanArchitecture 11 | 12 | class iOSCleanArchitectureTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /iOSCleanArchitectureUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /iOSCleanArchitectureUITests/iOSCleanArchitectureUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOSCleanArchitectureUITests.swift 3 | // iOSCleanArchitectureUITests 4 | // 5 | // Created by Lee YoungJu on 2019. 7. 13.. 6 | // Copyright © 2019년 Lee YoungJu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class iOSCleanArchitectureUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------