├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── README.md ├── RxSwiftCleanArchitecture.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── kidakiichiro.xcuserdatad │ └── xcschemes │ ├── RxSwiftCleanArchitecture.xcscheme │ └── xcschememanagement.plist ├── RxSwiftCleanArchitecture ├── App │ ├── Data │ │ ├── QiitaItem │ │ │ ├── QiitaItemDataStore.swift │ │ │ ├── QiitaItemEntity.swift │ │ │ ├── QiitaItemRepository.swift │ │ │ └── QiitaItemRequest.swift │ │ ├── QiitaItemComment │ │ │ ├── QiitaItemCommentDataStore.swift │ │ │ ├── QiitaItemCommentEntity.swift │ │ │ ├── QiitaItemCommentRepository.swift │ │ │ └── QiitaItemCommentRequest.swift │ │ ├── QiitaItemStocker │ │ │ ├── QiitaItemStockerDataStore.swift │ │ │ ├── QiitaItemStockerEntity.swift │ │ │ ├── QiitaItemStockerRepository.swift │ │ │ └── QiitaItemStockerRequest.swift │ │ └── Utility │ │ │ ├── ApiClient.swift │ │ │ └── ArrayTransform.swift │ ├── Environment │ │ ├── Const.swift │ │ ├── ErrorHandring.swift │ │ ├── NSDate+Util.swift │ │ ├── String+Util.swift │ │ └── UIStoryboard+Util.swift │ ├── Scenes │ │ ├── Detail │ │ │ ├── DetailConfigurator.swift │ │ │ ├── Domain │ │ │ │ ├── DetailModel.swift │ │ │ │ ├── DetailTranslater.swift │ │ │ │ └── DetailUseCase.swift │ │ │ └── Presentation │ │ │ │ ├── DetailPresenter.swift │ │ │ │ └── DetailViewController.swift │ │ ├── List │ │ │ ├── Domain │ │ │ │ ├── ListModel.swift │ │ │ │ ├── ListTranslater.swift │ │ │ │ └── ListUseCase.swift │ │ │ ├── ListConfigurator.swift │ │ │ └── Presentation │ │ │ │ ├── Cells │ │ │ │ └── ListTableViewCell.swift │ │ │ │ ├── ListPresenter.swift │ │ │ │ ├── ListRouter.swift │ │ │ │ └── ListViewController.swift │ │ └── Utility │ │ │ └── Dependencies.swift │ └── Storybords │ │ ├── Detail.storyboard │ │ └── List.Storyboard ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard └── Info.plist ├── RxSwiftCleanArchitectureTests ├── Info.plist ├── List │ ├── ListPresenterTests.swift │ └── ListUseCaseTests.swift └── RxSwiftCleanArchitectureTests.swift └── RxSwiftCleanArchitectureUITests ├── Info.plist └── RxSwiftCleanArchitectureUITests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode (from gitignore.io) 2 | build/ 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | *.xccheckout 13 | *.moved-aside 14 | DerivedData 15 | *.hmap 16 | *.ipa 17 | *.xcuserstate 18 | 19 | # CocoaPod 20 | Pods/* 21 | Podfile.lock 22 | 23 | # others 24 | *.swp 25 | !.gitkeep 26 | .DS_Store 27 | 28 | Carthage/* 29 | 30 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" "2.6.1" 2 | github "Alamofire/Alamofire" "3.5.0" 3 | github "Hearst-DD/ObjectMapper" "1.5.0" 4 | github "realm/realm-cocoa" "v2.0.0" 5 | github "rs/SDWebImage" 6 | github "SwiftyJSON/SwiftyJSON" "2.3.3" 7 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "3.5.0" 2 | github "Hearst-DD/ObjectMapper" "1.5.0" 3 | github "ReactiveX/RxSwift" "2.6.1" 4 | github "SwiftyJSON/SwiftyJSON" "2.3.3" 5 | github "realm/realm-cocoa" "v2.0.0" 6 | github "rs/SDWebImage" "4.0.0" 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxSwiftCleanArchitecture 2 | 3 | 詳しい内容はこちらに 4 | http://qiita.com/tamayuru/items/618ba3cf20be373f7455 5 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E0BB0E611D60E8FB0024F9EC /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E571D60E8FB0024F9EC /* SwiftyJSON.framework */; }; 11 | E0BB0E621D60E8FB0024F9EC /* WebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E581D60E8FB0024F9EC /* WebImage.framework */; }; 12 | E0BB0E631D60E8FB0024F9EC /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E591D60E8FB0024F9EC /* RxSwift.framework */; }; 13 | E0BB0E651D60E8FB0024F9EC /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E5B1D60E8FB0024F9EC /* RxBlocking.framework */; }; 14 | E0BB0E661D60E8FB0024F9EC /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E5C1D60E8FB0024F9EC /* RxCocoa.framework */; }; 15 | E0BB0E671D60E8FB0024F9EC /* ObjectMapper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E5D1D60E8FB0024F9EC /* ObjectMapper.framework */; }; 16 | E0BB0E681D60E8FB0024F9EC /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E5E1D60E8FB0024F9EC /* Alamofire.framework */; }; 17 | E0BB0E691D60E8FB0024F9EC /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E5F1D60E8FB0024F9EC /* Realm.framework */; }; 18 | E0BB0E6A1D60E8FB0024F9EC /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0BB0E601D60E8FB0024F9EC /* RealmSwift.framework */; }; 19 | E0BB0E6B1D60E9190024F9EC /* SwiftyJSON.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E571D60E8FB0024F9EC /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | E0BB0E6C1D60E9190024F9EC /* WebImage.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E581D60E8FB0024F9EC /* WebImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 21 | E0BB0E6D1D60E9190024F9EC /* RxSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E591D60E8FB0024F9EC /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | E0BB0E6E1D60E9190024F9EC /* RxTests.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E5A1D60E8FB0024F9EC /* RxTests.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 23 | E0BB0E6F1D60E9190024F9EC /* RxBlocking.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E5B1D60E8FB0024F9EC /* RxBlocking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | E0BB0E701D60E9190024F9EC /* RxCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E5C1D60E8FB0024F9EC /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 25 | E0BB0E711D60E9190024F9EC /* ObjectMapper.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E5D1D60E8FB0024F9EC /* ObjectMapper.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 26 | E0BB0E721D60E9190024F9EC /* Alamofire.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E5E1D60E8FB0024F9EC /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 27 | E0BB0E731D60E9190024F9EC /* Realm.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E5F1D60E8FB0024F9EC /* Realm.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 28 | E0BB0E741D60E9190024F9EC /* RealmSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E0BB0E601D60E8FB0024F9EC /* RealmSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 29 | E0D546691D609E9300E3E49C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546681D609E9300E3E49C /* AppDelegate.swift */; }; 30 | E0D546701D609E9300E3E49C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E0D5466F1D609E9300E3E49C /* Assets.xcassets */; }; 31 | E0D546731D609E9300E3E49C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E0D546711D609E9300E3E49C /* LaunchScreen.storyboard */; }; 32 | E0D5467E1D609E9300E3E49C /* RxSwiftCleanArchitectureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5467D1D609E9300E3E49C /* RxSwiftCleanArchitectureTests.swift */; }; 33 | E0D546891D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546881D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.swift */; }; 34 | E0D546CB1D609EFC00E3E49C /* QiitaItemDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546991D609EFC00E3E49C /* QiitaItemDataStore.swift */; }; 35 | E0D546CC1D609EFC00E3E49C /* QiitaItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5469A1D609EFC00E3E49C /* QiitaItemEntity.swift */; }; 36 | E0D546CD1D609EFC00E3E49C /* QiitaItemRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5469B1D609EFC00E3E49C /* QiitaItemRepository.swift */; }; 37 | E0D546CE1D609EFC00E3E49C /* QiitaItemRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5469C1D609EFC00E3E49C /* QiitaItemRequest.swift */; }; 38 | E0D546CF1D609EFC00E3E49C /* QiitaItemCommentDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5469E1D609EFC00E3E49C /* QiitaItemCommentDataStore.swift */; }; 39 | E0D546D01D609EFC00E3E49C /* QiitaItemCommentEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D5469F1D609EFC00E3E49C /* QiitaItemCommentEntity.swift */; }; 40 | E0D546D11D609EFC00E3E49C /* QiitaItemCommentRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A01D609EFC00E3E49C /* QiitaItemCommentRepository.swift */; }; 41 | E0D546D21D609EFC00E3E49C /* QiitaItemCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A11D609EFC00E3E49C /* QiitaItemCommentRequest.swift */; }; 42 | E0D546D31D609EFC00E3E49C /* QiitaItemStockerDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A31D609EFC00E3E49C /* QiitaItemStockerDataStore.swift */; }; 43 | E0D546D41D609EFC00E3E49C /* QiitaItemStockerEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A41D609EFC00E3E49C /* QiitaItemStockerEntity.swift */; }; 44 | E0D546D51D609EFC00E3E49C /* QiitaItemStockerRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A51D609EFC00E3E49C /* QiitaItemStockerRepository.swift */; }; 45 | E0D546D61D609EFC00E3E49C /* QiitaItemStockerRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A61D609EFC00E3E49C /* QiitaItemStockerRequest.swift */; }; 46 | E0D546D71D609EFC00E3E49C /* ApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A81D609EFC00E3E49C /* ApiClient.swift */; }; 47 | E0D546D81D609EFC00E3E49C /* ArrayTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546A91D609EFC00E3E49C /* ArrayTransform.swift */; }; 48 | E0D546D91D609EFC00E3E49C /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546AB1D609EFC00E3E49C /* Const.swift */; }; 49 | E0D546DA1D609EFC00E3E49C /* ErrorHandring.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546AC1D609EFC00E3E49C /* ErrorHandring.swift */; }; 50 | E0D546DB1D609EFC00E3E49C /* NSDate+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546AD1D609EFC00E3E49C /* NSDate+Util.swift */; }; 51 | E0D546DC1D609EFC00E3E49C /* String+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546AE1D609EFC00E3E49C /* String+Util.swift */; }; 52 | E0D546DD1D609EFC00E3E49C /* UIStoryboard+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546AF1D609EFC00E3E49C /* UIStoryboard+Util.swift */; }; 53 | E0D546DE1D609EFC00E3E49C /* DetailConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546B21D609EFC00E3E49C /* DetailConfigurator.swift */; }; 54 | E0D546DF1D609EFC00E3E49C /* DetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546B41D609EFC00E3E49C /* DetailModel.swift */; }; 55 | E0D546E01D609EFC00E3E49C /* DetailTranslater.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546B51D609EFC00E3E49C /* DetailTranslater.swift */; }; 56 | E0D546E11D609EFC00E3E49C /* DetailUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546B61D609EFC00E3E49C /* DetailUseCase.swift */; }; 57 | E0D546E21D609EFC00E3E49C /* DetailPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546B81D609EFC00E3E49C /* DetailPresenter.swift */; }; 58 | E0D546E31D609EFC00E3E49C /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546B91D609EFC00E3E49C /* DetailViewController.swift */; }; 59 | E0D546E41D609EFC00E3E49C /* ListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546BC1D609EFC00E3E49C /* ListModel.swift */; }; 60 | E0D546E51D609EFC00E3E49C /* ListTranslater.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546BD1D609EFC00E3E49C /* ListTranslater.swift */; }; 61 | E0D546E61D609EFC00E3E49C /* ListUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546BE1D609EFC00E3E49C /* ListUseCase.swift */; }; 62 | E0D546E71D609EFC00E3E49C /* ListConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546BF1D609EFC00E3E49C /* ListConfigurator.swift */; }; 63 | E0D546E81D609EFC00E3E49C /* ListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546C21D609EFC00E3E49C /* ListTableViewCell.swift */; }; 64 | E0D546E91D609EFC00E3E49C /* ListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546C31D609EFC00E3E49C /* ListPresenter.swift */; }; 65 | E0D546EA1D609EFC00E3E49C /* ListRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546C41D609EFC00E3E49C /* ListRouter.swift */; }; 66 | E0D546EB1D609EFC00E3E49C /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546C51D609EFC00E3E49C /* ListViewController.swift */; }; 67 | E0D546EC1D609EFC00E3E49C /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D546C71D609EFC00E3E49C /* Dependencies.swift */; }; 68 | E0D546ED1D609EFC00E3E49C /* Detail.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E0D546C91D609EFC00E3E49C /* Detail.storyboard */; }; 69 | E0D546EE1D609EFC00E3E49C /* List.Storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E0D546CA1D609EFC00E3E49C /* List.Storyboard */; }; 70 | E0D547041D60A25A00E3E49C /* ListPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D547031D60A25A00E3E49C /* ListPresenterTests.swift */; }; 71 | E0D547061D60A26500E3E49C /* ListUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D547051D60A26500E3E49C /* ListUseCaseTests.swift */; }; 72 | /* End PBXBuildFile section */ 73 | 74 | /* Begin PBXContainerItemProxy section */ 75 | E0D5467A1D609E9300E3E49C /* PBXContainerItemProxy */ = { 76 | isa = PBXContainerItemProxy; 77 | containerPortal = E0D5465D1D609E9300E3E49C /* Project object */; 78 | proxyType = 1; 79 | remoteGlobalIDString = E0D546641D609E9300E3E49C; 80 | remoteInfo = RxSwiftCleanArchitecture; 81 | }; 82 | E0D546851D609E9300E3E49C /* PBXContainerItemProxy */ = { 83 | isa = PBXContainerItemProxy; 84 | containerPortal = E0D5465D1D609E9300E3E49C /* Project object */; 85 | proxyType = 1; 86 | remoteGlobalIDString = E0D546641D609E9300E3E49C; 87 | remoteInfo = RxSwiftCleanArchitecture; 88 | }; 89 | /* End PBXContainerItemProxy section */ 90 | 91 | /* Begin PBXCopyFilesBuildPhase section */ 92 | E0BB070E1D60BAAC0024F9EC /* CopyFiles */ = { 93 | isa = PBXCopyFilesBuildPhase; 94 | buildActionMask = 2147483647; 95 | dstPath = ""; 96 | dstSubfolderSpec = 10; 97 | files = ( 98 | E0BB0E6B1D60E9190024F9EC /* SwiftyJSON.framework in CopyFiles */, 99 | E0BB0E6C1D60E9190024F9EC /* WebImage.framework in CopyFiles */, 100 | E0BB0E6D1D60E9190024F9EC /* RxSwift.framework in CopyFiles */, 101 | E0BB0E6E1D60E9190024F9EC /* RxTests.framework in CopyFiles */, 102 | E0BB0E6F1D60E9190024F9EC /* RxBlocking.framework in CopyFiles */, 103 | E0BB0E701D60E9190024F9EC /* RxCocoa.framework in CopyFiles */, 104 | E0BB0E711D60E9190024F9EC /* ObjectMapper.framework in CopyFiles */, 105 | E0BB0E721D60E9190024F9EC /* Alamofire.framework in CopyFiles */, 106 | E0BB0E731D60E9190024F9EC /* Realm.framework in CopyFiles */, 107 | E0BB0E741D60E9190024F9EC /* RealmSwift.framework in CopyFiles */, 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | /* End PBXCopyFilesBuildPhase section */ 112 | 113 | /* Begin PBXFileReference section */ 114 | E0BB0E571D60E8FB0024F9EC /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/iOS/SwiftyJSON.framework; sourceTree = ""; }; 115 | E0BB0E581D60E8FB0024F9EC /* WebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebImage.framework; path = Carthage/Build/iOS/WebImage.framework; sourceTree = ""; }; 116 | E0BB0E591D60E8FB0024F9EC /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; 117 | E0BB0E5A1D60E8FB0024F9EC /* RxTests.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTests.framework; path = Carthage/Build/iOS/RxTests.framework; sourceTree = ""; }; 118 | E0BB0E5B1D60E8FB0024F9EC /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = Carthage/Build/iOS/RxBlocking.framework; sourceTree = ""; }; 119 | E0BB0E5C1D60E8FB0024F9EC /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 120 | E0BB0E5D1D60E8FB0024F9EC /* ObjectMapper.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjectMapper.framework; path = Carthage/Build/iOS/ObjectMapper.framework; sourceTree = ""; }; 121 | E0BB0E5E1D60E8FB0024F9EC /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; 122 | E0BB0E5F1D60E8FB0024F9EC /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = ""; }; 123 | E0BB0E601D60E8FB0024F9EC /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = ""; }; 124 | E0D546651D609E9300E3E49C /* RxSwiftCleanArchitecture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxSwiftCleanArchitecture.app; sourceTree = BUILT_PRODUCTS_DIR; }; 125 | E0D546681D609E9300E3E49C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 126 | E0D5466F1D609E9300E3E49C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 127 | E0D546721D609E9300E3E49C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 128 | E0D546741D609E9300E3E49C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 129 | E0D546791D609E9300E3E49C /* RxSwiftCleanArchitectureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxSwiftCleanArchitectureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 130 | E0D5467D1D609E9300E3E49C /* RxSwiftCleanArchitectureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxSwiftCleanArchitectureTests.swift; sourceTree = ""; }; 131 | E0D5467F1D609E9300E3E49C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 132 | E0D546841D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxSwiftCleanArchitectureUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 133 | E0D546881D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxSwiftCleanArchitectureUITests.swift; sourceTree = ""; }; 134 | E0D5468A1D609E9300E3E49C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 135 | E0D546991D609EFC00E3E49C /* QiitaItemDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemDataStore.swift; sourceTree = ""; }; 136 | E0D5469A1D609EFC00E3E49C /* QiitaItemEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemEntity.swift; sourceTree = ""; }; 137 | E0D5469B1D609EFC00E3E49C /* QiitaItemRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemRepository.swift; sourceTree = ""; }; 138 | E0D5469C1D609EFC00E3E49C /* QiitaItemRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemRequest.swift; sourceTree = ""; }; 139 | E0D5469E1D609EFC00E3E49C /* QiitaItemCommentDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemCommentDataStore.swift; sourceTree = ""; }; 140 | E0D5469F1D609EFC00E3E49C /* QiitaItemCommentEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemCommentEntity.swift; sourceTree = ""; }; 141 | E0D546A01D609EFC00E3E49C /* QiitaItemCommentRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemCommentRepository.swift; sourceTree = ""; }; 142 | E0D546A11D609EFC00E3E49C /* QiitaItemCommentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemCommentRequest.swift; sourceTree = ""; }; 143 | E0D546A31D609EFC00E3E49C /* QiitaItemStockerDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemStockerDataStore.swift; sourceTree = ""; }; 144 | E0D546A41D609EFC00E3E49C /* QiitaItemStockerEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemStockerEntity.swift; sourceTree = ""; }; 145 | E0D546A51D609EFC00E3E49C /* QiitaItemStockerRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemStockerRepository.swift; sourceTree = ""; }; 146 | E0D546A61D609EFC00E3E49C /* QiitaItemStockerRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaItemStockerRequest.swift; sourceTree = ""; }; 147 | E0D546A81D609EFC00E3E49C /* ApiClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiClient.swift; sourceTree = ""; }; 148 | E0D546A91D609EFC00E3E49C /* ArrayTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayTransform.swift; sourceTree = ""; }; 149 | E0D546AB1D609EFC00E3E49C /* Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Const.swift; sourceTree = ""; }; 150 | E0D546AC1D609EFC00E3E49C /* ErrorHandring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorHandring.swift; sourceTree = ""; }; 151 | E0D546AD1D609EFC00E3E49C /* NSDate+Util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSDate+Util.swift"; sourceTree = ""; }; 152 | E0D546AE1D609EFC00E3E49C /* String+Util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Util.swift"; sourceTree = ""; }; 153 | E0D546AF1D609EFC00E3E49C /* UIStoryboard+Util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+Util.swift"; sourceTree = ""; }; 154 | E0D546B21D609EFC00E3E49C /* DetailConfigurator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailConfigurator.swift; sourceTree = ""; }; 155 | E0D546B41D609EFC00E3E49C /* DetailModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailModel.swift; sourceTree = ""; }; 156 | E0D546B51D609EFC00E3E49C /* DetailTranslater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailTranslater.swift; sourceTree = ""; }; 157 | E0D546B61D609EFC00E3E49C /* DetailUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailUseCase.swift; sourceTree = ""; }; 158 | E0D546B81D609EFC00E3E49C /* DetailPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailPresenter.swift; sourceTree = ""; }; 159 | E0D546B91D609EFC00E3E49C /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 160 | E0D546BC1D609EFC00E3E49C /* ListModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListModel.swift; sourceTree = ""; }; 161 | E0D546BD1D609EFC00E3E49C /* ListTranslater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTranslater.swift; sourceTree = ""; }; 162 | E0D546BE1D609EFC00E3E49C /* ListUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListUseCase.swift; sourceTree = ""; }; 163 | E0D546BF1D609EFC00E3E49C /* ListConfigurator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListConfigurator.swift; sourceTree = ""; }; 164 | E0D546C21D609EFC00E3E49C /* ListTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListTableViewCell.swift; sourceTree = ""; }; 165 | E0D546C31D609EFC00E3E49C /* ListPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListPresenter.swift; sourceTree = ""; }; 166 | E0D546C41D609EFC00E3E49C /* ListRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRouter.swift; sourceTree = ""; }; 167 | E0D546C51D609EFC00E3E49C /* ListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; 168 | E0D546C71D609EFC00E3E49C /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dependencies.swift; sourceTree = ""; }; 169 | E0D546C91D609EFC00E3E49C /* Detail.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Detail.storyboard; sourceTree = ""; }; 170 | E0D546CA1D609EFC00E3E49C /* List.Storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = List.Storyboard; sourceTree = ""; }; 171 | E0D547031D60A25A00E3E49C /* ListPresenterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListPresenterTests.swift; sourceTree = ""; }; 172 | E0D547051D60A26500E3E49C /* ListUseCaseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListUseCaseTests.swift; sourceTree = ""; }; 173 | /* End PBXFileReference section */ 174 | 175 | /* Begin PBXFrameworksBuildPhase section */ 176 | E0D546621D609E9300E3E49C /* Frameworks */ = { 177 | isa = PBXFrameworksBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | E0BB0E611D60E8FB0024F9EC /* SwiftyJSON.framework in Frameworks */, 181 | E0BB0E621D60E8FB0024F9EC /* WebImage.framework in Frameworks */, 182 | E0BB0E631D60E8FB0024F9EC /* RxSwift.framework in Frameworks */, 183 | E0BB0E651D60E8FB0024F9EC /* RxBlocking.framework in Frameworks */, 184 | E0BB0E661D60E8FB0024F9EC /* RxCocoa.framework in Frameworks */, 185 | E0BB0E671D60E8FB0024F9EC /* ObjectMapper.framework in Frameworks */, 186 | E0BB0E681D60E8FB0024F9EC /* Alamofire.framework in Frameworks */, 187 | E0BB0E691D60E8FB0024F9EC /* Realm.framework in Frameworks */, 188 | E0BB0E6A1D60E8FB0024F9EC /* RealmSwift.framework in Frameworks */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | E0D546761D609E9300E3E49C /* Frameworks */ = { 193 | isa = PBXFrameworksBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | E0D546811D609E9300E3E49C /* Frameworks */ = { 200 | isa = PBXFrameworksBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXFrameworksBuildPhase section */ 207 | 208 | /* Begin PBXGroup section */ 209 | E0BB0E561D60E8BE0024F9EC /* Frameworks */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | E0BB0E571D60E8FB0024F9EC /* SwiftyJSON.framework */, 213 | E0BB0E581D60E8FB0024F9EC /* WebImage.framework */, 214 | E0BB0E591D60E8FB0024F9EC /* RxSwift.framework */, 215 | E0BB0E5A1D60E8FB0024F9EC /* RxTests.framework */, 216 | E0BB0E5B1D60E8FB0024F9EC /* RxBlocking.framework */, 217 | E0BB0E5C1D60E8FB0024F9EC /* RxCocoa.framework */, 218 | E0BB0E5D1D60E8FB0024F9EC /* ObjectMapper.framework */, 219 | E0BB0E5E1D60E8FB0024F9EC /* Alamofire.framework */, 220 | E0BB0E5F1D60E8FB0024F9EC /* Realm.framework */, 221 | E0BB0E601D60E8FB0024F9EC /* RealmSwift.framework */, 222 | ); 223 | name = Frameworks; 224 | sourceTree = ""; 225 | }; 226 | E0D5465C1D609E9300E3E49C = { 227 | isa = PBXGroup; 228 | children = ( 229 | E0BB0E561D60E8BE0024F9EC /* Frameworks */, 230 | E0D546671D609E9300E3E49C /* RxSwiftCleanArchitecture */, 231 | E0D5467C1D609E9300E3E49C /* RxSwiftCleanArchitectureTests */, 232 | E0D546871D609E9300E3E49C /* RxSwiftCleanArchitectureUITests */, 233 | E0D546661D609E9300E3E49C /* Products */, 234 | ); 235 | sourceTree = ""; 236 | }; 237 | E0D546661D609E9300E3E49C /* Products */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | E0D546651D609E9300E3E49C /* RxSwiftCleanArchitecture.app */, 241 | E0D546791D609E9300E3E49C /* RxSwiftCleanArchitectureTests.xctest */, 242 | E0D546841D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.xctest */, 243 | ); 244 | name = Products; 245 | sourceTree = ""; 246 | }; 247 | E0D546671D609E9300E3E49C /* RxSwiftCleanArchitecture */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | E0D546961D609EFC00E3E49C /* App */, 251 | E0D546681D609E9300E3E49C /* AppDelegate.swift */, 252 | E0D5466F1D609E9300E3E49C /* Assets.xcassets */, 253 | E0D546711D609E9300E3E49C /* LaunchScreen.storyboard */, 254 | E0D546741D609E9300E3E49C /* Info.plist */, 255 | ); 256 | path = RxSwiftCleanArchitecture; 257 | sourceTree = ""; 258 | }; 259 | E0D5467C1D609E9300E3E49C /* RxSwiftCleanArchitectureTests */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | E0D547021D60A24D00E3E49C /* List */, 263 | E0D5467D1D609E9300E3E49C /* RxSwiftCleanArchitectureTests.swift */, 264 | E0D5467F1D609E9300E3E49C /* Info.plist */, 265 | ); 266 | path = RxSwiftCleanArchitectureTests; 267 | sourceTree = ""; 268 | }; 269 | E0D546871D609E9300E3E49C /* RxSwiftCleanArchitectureUITests */ = { 270 | isa = PBXGroup; 271 | children = ( 272 | E0D546881D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.swift */, 273 | E0D5468A1D609E9300E3E49C /* Info.plist */, 274 | ); 275 | path = RxSwiftCleanArchitectureUITests; 276 | sourceTree = ""; 277 | }; 278 | E0D546961D609EFC00E3E49C /* App */ = { 279 | isa = PBXGroup; 280 | children = ( 281 | E0D546971D609EFC00E3E49C /* Data */, 282 | E0D546AA1D609EFC00E3E49C /* Environment */, 283 | E0D546B01D609EFC00E3E49C /* Scenes */, 284 | E0D546C81D609EFC00E3E49C /* Storybords */, 285 | ); 286 | path = App; 287 | sourceTree = ""; 288 | }; 289 | E0D546971D609EFC00E3E49C /* Data */ = { 290 | isa = PBXGroup; 291 | children = ( 292 | E0D546981D609EFC00E3E49C /* QiitaItem */, 293 | E0D5469D1D609EFC00E3E49C /* QiitaItemComment */, 294 | E0D546A21D609EFC00E3E49C /* QiitaItemStocker */, 295 | E0D546A71D609EFC00E3E49C /* Utility */, 296 | ); 297 | path = Data; 298 | sourceTree = ""; 299 | }; 300 | E0D546981D609EFC00E3E49C /* QiitaItem */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | E0D546991D609EFC00E3E49C /* QiitaItemDataStore.swift */, 304 | E0D5469A1D609EFC00E3E49C /* QiitaItemEntity.swift */, 305 | E0D5469B1D609EFC00E3E49C /* QiitaItemRepository.swift */, 306 | E0D5469C1D609EFC00E3E49C /* QiitaItemRequest.swift */, 307 | ); 308 | path = QiitaItem; 309 | sourceTree = ""; 310 | }; 311 | E0D5469D1D609EFC00E3E49C /* QiitaItemComment */ = { 312 | isa = PBXGroup; 313 | children = ( 314 | E0D5469E1D609EFC00E3E49C /* QiitaItemCommentDataStore.swift */, 315 | E0D5469F1D609EFC00E3E49C /* QiitaItemCommentEntity.swift */, 316 | E0D546A01D609EFC00E3E49C /* QiitaItemCommentRepository.swift */, 317 | E0D546A11D609EFC00E3E49C /* QiitaItemCommentRequest.swift */, 318 | ); 319 | path = QiitaItemComment; 320 | sourceTree = ""; 321 | }; 322 | E0D546A21D609EFC00E3E49C /* QiitaItemStocker */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | E0D546A31D609EFC00E3E49C /* QiitaItemStockerDataStore.swift */, 326 | E0D546A41D609EFC00E3E49C /* QiitaItemStockerEntity.swift */, 327 | E0D546A51D609EFC00E3E49C /* QiitaItemStockerRepository.swift */, 328 | E0D546A61D609EFC00E3E49C /* QiitaItemStockerRequest.swift */, 329 | ); 330 | path = QiitaItemStocker; 331 | sourceTree = ""; 332 | }; 333 | E0D546A71D609EFC00E3E49C /* Utility */ = { 334 | isa = PBXGroup; 335 | children = ( 336 | E0D546A81D609EFC00E3E49C /* ApiClient.swift */, 337 | E0D546A91D609EFC00E3E49C /* ArrayTransform.swift */, 338 | ); 339 | path = Utility; 340 | sourceTree = ""; 341 | }; 342 | E0D546AA1D609EFC00E3E49C /* Environment */ = { 343 | isa = PBXGroup; 344 | children = ( 345 | E0D546AB1D609EFC00E3E49C /* Const.swift */, 346 | E0D546AC1D609EFC00E3E49C /* ErrorHandring.swift */, 347 | E0D546AD1D609EFC00E3E49C /* NSDate+Util.swift */, 348 | E0D546AE1D609EFC00E3E49C /* String+Util.swift */, 349 | E0D546AF1D609EFC00E3E49C /* UIStoryboard+Util.swift */, 350 | ); 351 | path = Environment; 352 | sourceTree = ""; 353 | }; 354 | E0D546B01D609EFC00E3E49C /* Scenes */ = { 355 | isa = PBXGroup; 356 | children = ( 357 | E0D546B11D609EFC00E3E49C /* Detail */, 358 | E0D546BA1D609EFC00E3E49C /* List */, 359 | E0D546C61D609EFC00E3E49C /* Utility */, 360 | ); 361 | path = Scenes; 362 | sourceTree = ""; 363 | }; 364 | E0D546B11D609EFC00E3E49C /* Detail */ = { 365 | isa = PBXGroup; 366 | children = ( 367 | E0D546B21D609EFC00E3E49C /* DetailConfigurator.swift */, 368 | E0D546B31D609EFC00E3E49C /* Domain */, 369 | E0D546B71D609EFC00E3E49C /* Presentation */, 370 | ); 371 | path = Detail; 372 | sourceTree = ""; 373 | }; 374 | E0D546B31D609EFC00E3E49C /* Domain */ = { 375 | isa = PBXGroup; 376 | children = ( 377 | E0D546B41D609EFC00E3E49C /* DetailModel.swift */, 378 | E0D546B51D609EFC00E3E49C /* DetailTranslater.swift */, 379 | E0D546B61D609EFC00E3E49C /* DetailUseCase.swift */, 380 | ); 381 | path = Domain; 382 | sourceTree = ""; 383 | }; 384 | E0D546B71D609EFC00E3E49C /* Presentation */ = { 385 | isa = PBXGroup; 386 | children = ( 387 | E0D546B81D609EFC00E3E49C /* DetailPresenter.swift */, 388 | E0D546B91D609EFC00E3E49C /* DetailViewController.swift */, 389 | ); 390 | path = Presentation; 391 | sourceTree = ""; 392 | }; 393 | E0D546BA1D609EFC00E3E49C /* List */ = { 394 | isa = PBXGroup; 395 | children = ( 396 | E0D546BB1D609EFC00E3E49C /* Domain */, 397 | E0D546BF1D609EFC00E3E49C /* ListConfigurator.swift */, 398 | E0D546C01D609EFC00E3E49C /* Presentation */, 399 | ); 400 | path = List; 401 | sourceTree = ""; 402 | }; 403 | E0D546BB1D609EFC00E3E49C /* Domain */ = { 404 | isa = PBXGroup; 405 | children = ( 406 | E0D546BC1D609EFC00E3E49C /* ListModel.swift */, 407 | E0D546BD1D609EFC00E3E49C /* ListTranslater.swift */, 408 | E0D546BE1D609EFC00E3E49C /* ListUseCase.swift */, 409 | ); 410 | path = Domain; 411 | sourceTree = ""; 412 | }; 413 | E0D546C01D609EFC00E3E49C /* Presentation */ = { 414 | isa = PBXGroup; 415 | children = ( 416 | E0D546C11D609EFC00E3E49C /* Cells */, 417 | E0D546C31D609EFC00E3E49C /* ListPresenter.swift */, 418 | E0D546C41D609EFC00E3E49C /* ListRouter.swift */, 419 | E0D546C51D609EFC00E3E49C /* ListViewController.swift */, 420 | ); 421 | path = Presentation; 422 | sourceTree = ""; 423 | }; 424 | E0D546C11D609EFC00E3E49C /* Cells */ = { 425 | isa = PBXGroup; 426 | children = ( 427 | E0D546C21D609EFC00E3E49C /* ListTableViewCell.swift */, 428 | ); 429 | path = Cells; 430 | sourceTree = ""; 431 | }; 432 | E0D546C61D609EFC00E3E49C /* Utility */ = { 433 | isa = PBXGroup; 434 | children = ( 435 | E0D546C71D609EFC00E3E49C /* Dependencies.swift */, 436 | ); 437 | path = Utility; 438 | sourceTree = ""; 439 | }; 440 | E0D546C81D609EFC00E3E49C /* Storybords */ = { 441 | isa = PBXGroup; 442 | children = ( 443 | E0D546C91D609EFC00E3E49C /* Detail.storyboard */, 444 | E0D546CA1D609EFC00E3E49C /* List.Storyboard */, 445 | ); 446 | path = Storybords; 447 | sourceTree = ""; 448 | }; 449 | E0D547021D60A24D00E3E49C /* List */ = { 450 | isa = PBXGroup; 451 | children = ( 452 | E0D547031D60A25A00E3E49C /* ListPresenterTests.swift */, 453 | E0D547051D60A26500E3E49C /* ListUseCaseTests.swift */, 454 | ); 455 | path = List; 456 | sourceTree = ""; 457 | }; 458 | /* End PBXGroup section */ 459 | 460 | /* Begin PBXNativeTarget section */ 461 | E0D546641D609E9300E3E49C /* RxSwiftCleanArchitecture */ = { 462 | isa = PBXNativeTarget; 463 | buildConfigurationList = E0D5468D1D609E9300E3E49C /* Build configuration list for PBXNativeTarget "RxSwiftCleanArchitecture" */; 464 | buildPhases = ( 465 | E0D546611D609E9300E3E49C /* Sources */, 466 | E0D546621D609E9300E3E49C /* Frameworks */, 467 | E0D546631D609E9300E3E49C /* Resources */, 468 | E0D546FF1D60A17900E3E49C /* ShellScript */, 469 | ); 470 | buildRules = ( 471 | ); 472 | dependencies = ( 473 | ); 474 | name = RxSwiftCleanArchitecture; 475 | productName = RxSwiftCleanArchitecture; 476 | productReference = E0D546651D609E9300E3E49C /* RxSwiftCleanArchitecture.app */; 477 | productType = "com.apple.product-type.application"; 478 | }; 479 | E0D546781D609E9300E3E49C /* RxSwiftCleanArchitectureTests */ = { 480 | isa = PBXNativeTarget; 481 | buildConfigurationList = E0D546901D609E9300E3E49C /* Build configuration list for PBXNativeTarget "RxSwiftCleanArchitectureTests" */; 482 | buildPhases = ( 483 | E0D546751D609E9300E3E49C /* Sources */, 484 | E0D546761D609E9300E3E49C /* Frameworks */, 485 | E0D546771D609E9300E3E49C /* Resources */, 486 | E0BB070E1D60BAAC0024F9EC /* CopyFiles */, 487 | ); 488 | buildRules = ( 489 | ); 490 | dependencies = ( 491 | E0D5467B1D609E9300E3E49C /* PBXTargetDependency */, 492 | ); 493 | name = RxSwiftCleanArchitectureTests; 494 | productName = RxSwiftCleanArchitectureTests; 495 | productReference = E0D546791D609E9300E3E49C /* RxSwiftCleanArchitectureTests.xctest */; 496 | productType = "com.apple.product-type.bundle.unit-test"; 497 | }; 498 | E0D546831D609E9300E3E49C /* RxSwiftCleanArchitectureUITests */ = { 499 | isa = PBXNativeTarget; 500 | buildConfigurationList = E0D546931D609E9300E3E49C /* Build configuration list for PBXNativeTarget "RxSwiftCleanArchitectureUITests" */; 501 | buildPhases = ( 502 | E0D546801D609E9300E3E49C /* Sources */, 503 | E0D546811D609E9300E3E49C /* Frameworks */, 504 | E0D546821D609E9300E3E49C /* Resources */, 505 | ); 506 | buildRules = ( 507 | ); 508 | dependencies = ( 509 | E0D546861D609E9300E3E49C /* PBXTargetDependency */, 510 | ); 511 | name = RxSwiftCleanArchitectureUITests; 512 | productName = RxSwiftCleanArchitectureUITests; 513 | productReference = E0D546841D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.xctest */; 514 | productType = "com.apple.product-type.bundle.ui-testing"; 515 | }; 516 | /* End PBXNativeTarget section */ 517 | 518 | /* Begin PBXProject section */ 519 | E0D5465D1D609E9300E3E49C /* Project object */ = { 520 | isa = PBXProject; 521 | attributes = { 522 | LastSwiftUpdateCheck = 0730; 523 | LastUpgradeCheck = 0730; 524 | ORGANIZATIONNAME = tamayuru; 525 | TargetAttributes = { 526 | E0D546641D609E9300E3E49C = { 527 | CreatedOnToolsVersion = 7.3.1; 528 | }; 529 | E0D546781D609E9300E3E49C = { 530 | CreatedOnToolsVersion = 7.3.1; 531 | TestTargetID = E0D546641D609E9300E3E49C; 532 | }; 533 | E0D546831D609E9300E3E49C = { 534 | CreatedOnToolsVersion = 7.3.1; 535 | TestTargetID = E0D546641D609E9300E3E49C; 536 | }; 537 | }; 538 | }; 539 | buildConfigurationList = E0D546601D609E9300E3E49C /* Build configuration list for PBXProject "RxSwiftCleanArchitecture" */; 540 | compatibilityVersion = "Xcode 3.2"; 541 | developmentRegion = English; 542 | hasScannedForEncodings = 0; 543 | knownRegions = ( 544 | en, 545 | Base, 546 | ); 547 | mainGroup = E0D5465C1D609E9300E3E49C; 548 | productRefGroup = E0D546661D609E9300E3E49C /* Products */; 549 | projectDirPath = ""; 550 | projectRoot = ""; 551 | targets = ( 552 | E0D546641D609E9300E3E49C /* RxSwiftCleanArchitecture */, 553 | E0D546781D609E9300E3E49C /* RxSwiftCleanArchitectureTests */, 554 | E0D546831D609E9300E3E49C /* RxSwiftCleanArchitectureUITests */, 555 | ); 556 | }; 557 | /* End PBXProject section */ 558 | 559 | /* Begin PBXResourcesBuildPhase section */ 560 | E0D546631D609E9300E3E49C /* Resources */ = { 561 | isa = PBXResourcesBuildPhase; 562 | buildActionMask = 2147483647; 563 | files = ( 564 | E0D546EE1D609EFC00E3E49C /* List.Storyboard in Resources */, 565 | E0D546731D609E9300E3E49C /* LaunchScreen.storyboard in Resources */, 566 | E0D546701D609E9300E3E49C /* Assets.xcassets in Resources */, 567 | E0D546ED1D609EFC00E3E49C /* Detail.storyboard in Resources */, 568 | ); 569 | runOnlyForDeploymentPostprocessing = 0; 570 | }; 571 | E0D546771D609E9300E3E49C /* Resources */ = { 572 | isa = PBXResourcesBuildPhase; 573 | buildActionMask = 2147483647; 574 | files = ( 575 | ); 576 | runOnlyForDeploymentPostprocessing = 0; 577 | }; 578 | E0D546821D609E9300E3E49C /* Resources */ = { 579 | isa = PBXResourcesBuildPhase; 580 | buildActionMask = 2147483647; 581 | files = ( 582 | ); 583 | runOnlyForDeploymentPostprocessing = 0; 584 | }; 585 | /* End PBXResourcesBuildPhase section */ 586 | 587 | /* Begin PBXShellScriptBuildPhase section */ 588 | E0D546FF1D60A17900E3E49C /* ShellScript */ = { 589 | isa = PBXShellScriptBuildPhase; 590 | buildActionMask = 2147483647; 591 | files = ( 592 | ); 593 | inputPaths = ( 594 | "$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework", 595 | "$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework", 596 | "$(SRCROOT)/Carthage/Build/iOS/RxSwift.framework", 597 | "$(SRCROOT)/Carthage/Build/iOS/ObjectMapper.framework", 598 | "$(SRCROOT)/Carthage/Build/iOS/WebImage.framework", 599 | "$(SRCROOT)/Carthage/Build/iOS/SwiftyJSON.framework", 600 | "$(SRCROOT)/Carthage/Build/iOS/RxBlocking.framework", 601 | "$(SRCROOT)/Carthage/Build/iOS/Realm.framework", 602 | "$(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework", 603 | ); 604 | outputPaths = ( 605 | ); 606 | runOnlyForDeploymentPostprocessing = 0; 607 | shellPath = /bin/sh; 608 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 609 | }; 610 | /* End PBXShellScriptBuildPhase section */ 611 | 612 | /* Begin PBXSourcesBuildPhase section */ 613 | E0D546611D609E9300E3E49C /* Sources */ = { 614 | isa = PBXSourcesBuildPhase; 615 | buildActionMask = 2147483647; 616 | files = ( 617 | E0D546D31D609EFC00E3E49C /* QiitaItemStockerDataStore.swift in Sources */, 618 | E0D546DD1D609EFC00E3E49C /* UIStoryboard+Util.swift in Sources */, 619 | E0D546CC1D609EFC00E3E49C /* QiitaItemEntity.swift in Sources */, 620 | E0D546D41D609EFC00E3E49C /* QiitaItemStockerEntity.swift in Sources */, 621 | E0D546DF1D609EFC00E3E49C /* DetailModel.swift in Sources */, 622 | E0D546CE1D609EFC00E3E49C /* QiitaItemRequest.swift in Sources */, 623 | E0D546D51D609EFC00E3E49C /* QiitaItemStockerRepository.swift in Sources */, 624 | E0D546E11D609EFC00E3E49C /* DetailUseCase.swift in Sources */, 625 | E0D546E31D609EFC00E3E49C /* DetailViewController.swift in Sources */, 626 | E0D546E51D609EFC00E3E49C /* ListTranslater.swift in Sources */, 627 | E0D546D91D609EFC00E3E49C /* Const.swift in Sources */, 628 | E0D546EC1D609EFC00E3E49C /* Dependencies.swift in Sources */, 629 | E0D546E41D609EFC00E3E49C /* ListModel.swift in Sources */, 630 | E0D546EB1D609EFC00E3E49C /* ListViewController.swift in Sources */, 631 | E0D546E61D609EFC00E3E49C /* ListUseCase.swift in Sources */, 632 | E0D546D21D609EFC00E3E49C /* QiitaItemCommentRequest.swift in Sources */, 633 | E0D546D61D609EFC00E3E49C /* QiitaItemStockerRequest.swift in Sources */, 634 | E0D546DA1D609EFC00E3E49C /* ErrorHandring.swift in Sources */, 635 | E0D546E81D609EFC00E3E49C /* ListTableViewCell.swift in Sources */, 636 | E0D546EA1D609EFC00E3E49C /* ListRouter.swift in Sources */, 637 | E0D546E01D609EFC00E3E49C /* DetailTranslater.swift in Sources */, 638 | E0D546E91D609EFC00E3E49C /* ListPresenter.swift in Sources */, 639 | E0D546CB1D609EFC00E3E49C /* QiitaItemDataStore.swift in Sources */, 640 | E0D546CF1D609EFC00E3E49C /* QiitaItemCommentDataStore.swift in Sources */, 641 | E0D546DE1D609EFC00E3E49C /* DetailConfigurator.swift in Sources */, 642 | E0D546DC1D609EFC00E3E49C /* String+Util.swift in Sources */, 643 | E0D546DB1D609EFC00E3E49C /* NSDate+Util.swift in Sources */, 644 | E0D546D81D609EFC00E3E49C /* ArrayTransform.swift in Sources */, 645 | E0D546D11D609EFC00E3E49C /* QiitaItemCommentRepository.swift in Sources */, 646 | E0D546CD1D609EFC00E3E49C /* QiitaItemRepository.swift in Sources */, 647 | E0D546D01D609EFC00E3E49C /* QiitaItemCommentEntity.swift in Sources */, 648 | E0D546E21D609EFC00E3E49C /* DetailPresenter.swift in Sources */, 649 | E0D546E71D609EFC00E3E49C /* ListConfigurator.swift in Sources */, 650 | E0D546691D609E9300E3E49C /* AppDelegate.swift in Sources */, 651 | E0D546D71D609EFC00E3E49C /* ApiClient.swift in Sources */, 652 | ); 653 | runOnlyForDeploymentPostprocessing = 0; 654 | }; 655 | E0D546751D609E9300E3E49C /* Sources */ = { 656 | isa = PBXSourcesBuildPhase; 657 | buildActionMask = 2147483647; 658 | files = ( 659 | E0D547041D60A25A00E3E49C /* ListPresenterTests.swift in Sources */, 660 | E0D5467E1D609E9300E3E49C /* RxSwiftCleanArchitectureTests.swift in Sources */, 661 | E0D547061D60A26500E3E49C /* ListUseCaseTests.swift in Sources */, 662 | ); 663 | runOnlyForDeploymentPostprocessing = 0; 664 | }; 665 | E0D546801D609E9300E3E49C /* Sources */ = { 666 | isa = PBXSourcesBuildPhase; 667 | buildActionMask = 2147483647; 668 | files = ( 669 | E0D546891D609E9300E3E49C /* RxSwiftCleanArchitectureUITests.swift in Sources */, 670 | ); 671 | runOnlyForDeploymentPostprocessing = 0; 672 | }; 673 | /* End PBXSourcesBuildPhase section */ 674 | 675 | /* Begin PBXTargetDependency section */ 676 | E0D5467B1D609E9300E3E49C /* PBXTargetDependency */ = { 677 | isa = PBXTargetDependency; 678 | target = E0D546641D609E9300E3E49C /* RxSwiftCleanArchitecture */; 679 | targetProxy = E0D5467A1D609E9300E3E49C /* PBXContainerItemProxy */; 680 | }; 681 | E0D546861D609E9300E3E49C /* PBXTargetDependency */ = { 682 | isa = PBXTargetDependency; 683 | target = E0D546641D609E9300E3E49C /* RxSwiftCleanArchitecture */; 684 | targetProxy = E0D546851D609E9300E3E49C /* PBXContainerItemProxy */; 685 | }; 686 | /* End PBXTargetDependency section */ 687 | 688 | /* Begin PBXVariantGroup section */ 689 | E0D546711D609E9300E3E49C /* LaunchScreen.storyboard */ = { 690 | isa = PBXVariantGroup; 691 | children = ( 692 | E0D546721D609E9300E3E49C /* Base */, 693 | ); 694 | name = LaunchScreen.storyboard; 695 | sourceTree = ""; 696 | }; 697 | /* End PBXVariantGroup section */ 698 | 699 | /* Begin XCBuildConfiguration section */ 700 | E0D5468B1D609E9300E3E49C /* Debug */ = { 701 | isa = XCBuildConfiguration; 702 | buildSettings = { 703 | ALWAYS_SEARCH_USER_PATHS = NO; 704 | CLANG_ANALYZER_NONNULL = YES; 705 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 706 | CLANG_CXX_LIBRARY = "libc++"; 707 | CLANG_ENABLE_MODULES = YES; 708 | CLANG_ENABLE_OBJC_ARC = YES; 709 | CLANG_WARN_BOOL_CONVERSION = YES; 710 | CLANG_WARN_CONSTANT_CONVERSION = YES; 711 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 712 | CLANG_WARN_EMPTY_BODY = YES; 713 | CLANG_WARN_ENUM_CONVERSION = YES; 714 | CLANG_WARN_INT_CONVERSION = YES; 715 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 716 | CLANG_WARN_UNREACHABLE_CODE = YES; 717 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 718 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 719 | COPY_PHASE_STRIP = NO; 720 | DEBUG_INFORMATION_FORMAT = dwarf; 721 | ENABLE_STRICT_OBJC_MSGSEND = YES; 722 | ENABLE_TESTABILITY = YES; 723 | GCC_C_LANGUAGE_STANDARD = gnu99; 724 | GCC_DYNAMIC_NO_PIC = NO; 725 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 726 | GCC_NO_COMMON_BLOCKS = YES; 727 | GCC_OPTIMIZATION_LEVEL = 0; 728 | GCC_PREPROCESSOR_DEFINITIONS = ( 729 | "DEBUG=1", 730 | "$(inherited)", 731 | ); 732 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 733 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 734 | GCC_WARN_UNDECLARED_SELECTOR = YES; 735 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 736 | GCC_WARN_UNUSED_FUNCTION = YES; 737 | GCC_WARN_UNUSED_VARIABLE = YES; 738 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 739 | MTL_ENABLE_DEBUG_INFO = YES; 740 | ONLY_ACTIVE_ARCH = YES; 741 | SDKROOT = iphoneos; 742 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 743 | SWIFT_VERSION = 2.3; 744 | }; 745 | name = Debug; 746 | }; 747 | E0D5468C1D609E9300E3E49C /* Release */ = { 748 | isa = XCBuildConfiguration; 749 | buildSettings = { 750 | ALWAYS_SEARCH_USER_PATHS = NO; 751 | CLANG_ANALYZER_NONNULL = YES; 752 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 753 | CLANG_CXX_LIBRARY = "libc++"; 754 | CLANG_ENABLE_MODULES = YES; 755 | CLANG_ENABLE_OBJC_ARC = YES; 756 | CLANG_WARN_BOOL_CONVERSION = YES; 757 | CLANG_WARN_CONSTANT_CONVERSION = YES; 758 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 759 | CLANG_WARN_EMPTY_BODY = YES; 760 | CLANG_WARN_ENUM_CONVERSION = YES; 761 | CLANG_WARN_INT_CONVERSION = YES; 762 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 763 | CLANG_WARN_UNREACHABLE_CODE = YES; 764 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 765 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 766 | COPY_PHASE_STRIP = NO; 767 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 768 | ENABLE_NS_ASSERTIONS = NO; 769 | ENABLE_STRICT_OBJC_MSGSEND = YES; 770 | GCC_C_LANGUAGE_STANDARD = gnu99; 771 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 772 | GCC_NO_COMMON_BLOCKS = YES; 773 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 774 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 775 | GCC_WARN_UNDECLARED_SELECTOR = YES; 776 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 777 | GCC_WARN_UNUSED_FUNCTION = YES; 778 | GCC_WARN_UNUSED_VARIABLE = YES; 779 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 780 | MTL_ENABLE_DEBUG_INFO = NO; 781 | SDKROOT = iphoneos; 782 | SWIFT_VERSION = 2.3; 783 | VALIDATE_PRODUCT = YES; 784 | }; 785 | name = Release; 786 | }; 787 | E0D5468E1D609E9300E3E49C /* Debug */ = { 788 | isa = XCBuildConfiguration; 789 | buildSettings = { 790 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 791 | FRAMEWORK_SEARCH_PATHS = ( 792 | "$(inherited)", 793 | "$(PROJECT_DIR)/Carthage/Build/iOS", 794 | "$(SRCROOT)/Carthage/Build/iOS", 795 | ); 796 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 797 | INFOPLIST_FILE = RxSwiftCleanArchitecture/Info.plist; 798 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 799 | PRODUCT_BUNDLE_IDENTIFIER = tamayuru.RxSwiftCleanArchitecture; 800 | PRODUCT_NAME = "$(TARGET_NAME)"; 801 | SWIFT_VERSION = 2.3; 802 | VALID_ARCHS = "arm64 armv7 armv7s"; 803 | }; 804 | name = Debug; 805 | }; 806 | E0D5468F1D609E9300E3E49C /* Release */ = { 807 | isa = XCBuildConfiguration; 808 | buildSettings = { 809 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 810 | FRAMEWORK_SEARCH_PATHS = ( 811 | "$(inherited)", 812 | "$(PROJECT_DIR)/Carthage/Build/iOS", 813 | "$(SRCROOT)/Carthage/Build/iOS", 814 | ); 815 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 816 | INFOPLIST_FILE = RxSwiftCleanArchitecture/Info.plist; 817 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 818 | PRODUCT_BUNDLE_IDENTIFIER = tamayuru.RxSwiftCleanArchitecture; 819 | PRODUCT_NAME = "$(TARGET_NAME)"; 820 | SWIFT_VERSION = 2.3; 821 | VALID_ARCHS = "arm64 armv7 armv7s"; 822 | }; 823 | name = Release; 824 | }; 825 | E0D546911D609E9300E3E49C /* Debug */ = { 826 | isa = XCBuildConfiguration; 827 | buildSettings = { 828 | BUNDLE_LOADER = "$(TEST_HOST)"; 829 | FRAMEWORK_SEARCH_PATHS = ( 830 | "$(inherited)", 831 | "$(PROJECT_DIR)/Carthage/Build/iOS", 832 | ); 833 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 834 | INFOPLIST_FILE = RxSwiftCleanArchitectureTests/Info.plist; 835 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 836 | PRODUCT_BUNDLE_IDENTIFIER = tamayuru.RxSwiftCleanArchitectureTests; 837 | PRODUCT_NAME = "$(TARGET_NAME)"; 838 | SWIFT_VERSION = 2.3; 839 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RxSwiftCleanArchitecture.app/RxSwiftCleanArchitecture"; 840 | VALID_ARCHS = "arm64 armv7 armv7s"; 841 | }; 842 | name = Debug; 843 | }; 844 | E0D546921D609E9300E3E49C /* Release */ = { 845 | isa = XCBuildConfiguration; 846 | buildSettings = { 847 | BUNDLE_LOADER = "$(TEST_HOST)"; 848 | FRAMEWORK_SEARCH_PATHS = ( 849 | "$(inherited)", 850 | "$(PROJECT_DIR)/Carthage/Build/iOS", 851 | ); 852 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 853 | INFOPLIST_FILE = RxSwiftCleanArchitectureTests/Info.plist; 854 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 855 | PRODUCT_BUNDLE_IDENTIFIER = tamayuru.RxSwiftCleanArchitectureTests; 856 | PRODUCT_NAME = "$(TARGET_NAME)"; 857 | SWIFT_VERSION = 2.3; 858 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RxSwiftCleanArchitecture.app/RxSwiftCleanArchitecture"; 859 | VALID_ARCHS = "arm64 armv7 armv7s"; 860 | }; 861 | name = Release; 862 | }; 863 | E0D546941D609E9300E3E49C /* Debug */ = { 864 | isa = XCBuildConfiguration; 865 | buildSettings = { 866 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 867 | INFOPLIST_FILE = RxSwiftCleanArchitectureUITests/Info.plist; 868 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 869 | PRODUCT_BUNDLE_IDENTIFIER = tamayuru.RxSwiftCleanArchitectureUITests; 870 | PRODUCT_NAME = "$(TARGET_NAME)"; 871 | SWIFT_VERSION = 2.3; 872 | TEST_TARGET_NAME = RxSwiftCleanArchitecture; 873 | }; 874 | name = Debug; 875 | }; 876 | E0D546951D609E9300E3E49C /* Release */ = { 877 | isa = XCBuildConfiguration; 878 | buildSettings = { 879 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 880 | INFOPLIST_FILE = RxSwiftCleanArchitectureUITests/Info.plist; 881 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 882 | PRODUCT_BUNDLE_IDENTIFIER = tamayuru.RxSwiftCleanArchitectureUITests; 883 | PRODUCT_NAME = "$(TARGET_NAME)"; 884 | SWIFT_VERSION = 2.3; 885 | TEST_TARGET_NAME = RxSwiftCleanArchitecture; 886 | }; 887 | name = Release; 888 | }; 889 | /* End XCBuildConfiguration section */ 890 | 891 | /* Begin XCConfigurationList section */ 892 | E0D546601D609E9300E3E49C /* Build configuration list for PBXProject "RxSwiftCleanArchitecture" */ = { 893 | isa = XCConfigurationList; 894 | buildConfigurations = ( 895 | E0D5468B1D609E9300E3E49C /* Debug */, 896 | E0D5468C1D609E9300E3E49C /* Release */, 897 | ); 898 | defaultConfigurationIsVisible = 0; 899 | defaultConfigurationName = Release; 900 | }; 901 | E0D5468D1D609E9300E3E49C /* Build configuration list for PBXNativeTarget "RxSwiftCleanArchitecture" */ = { 902 | isa = XCConfigurationList; 903 | buildConfigurations = ( 904 | E0D5468E1D609E9300E3E49C /* Debug */, 905 | E0D5468F1D609E9300E3E49C /* Release */, 906 | ); 907 | defaultConfigurationIsVisible = 0; 908 | defaultConfigurationName = Release; 909 | }; 910 | E0D546901D609E9300E3E49C /* Build configuration list for PBXNativeTarget "RxSwiftCleanArchitectureTests" */ = { 911 | isa = XCConfigurationList; 912 | buildConfigurations = ( 913 | E0D546911D609E9300E3E49C /* Debug */, 914 | E0D546921D609E9300E3E49C /* Release */, 915 | ); 916 | defaultConfigurationIsVisible = 0; 917 | defaultConfigurationName = Release; 918 | }; 919 | E0D546931D609E9300E3E49C /* Build configuration list for PBXNativeTarget "RxSwiftCleanArchitectureUITests" */ = { 920 | isa = XCConfigurationList; 921 | buildConfigurations = ( 922 | E0D546941D609E9300E3E49C /* Debug */, 923 | E0D546951D609E9300E3E49C /* Release */, 924 | ); 925 | defaultConfigurationIsVisible = 0; 926 | defaultConfigurationName = Release; 927 | }; 928 | /* End XCConfigurationList section */ 929 | }; 930 | rootObject = E0D5465D1D609E9300E3E49C /* Project object */; 931 | } 932 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture.xcodeproj/xcuserdata/kidakiichiro.xcuserdatad/xcschemes/RxSwiftCleanArchitecture.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture.xcodeproj/xcuserdata/kidakiichiro.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RxSwiftCleanArchitecture.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | E0D546641D609E9300E3E49C 16 | 17 | primary 18 | 19 | 20 | E0D546781D609E9300E3E49C 21 | 22 | primary 23 | 24 | 25 | E0D546831D609E9300E3E49C 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItem/QiitaItemDataStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QiitaItemDataStore.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import Foundation 11 | 12 | class QiitaItemDataStore : NSObject { 13 | 14 | let request = QiitaItemRequest() 15 | 16 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable { 17 | return request.fetch(params: params, retryCount: retryCount) 18 | } 19 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItem/QiitaItemEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WikipediaEntity.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * 11 | * APIなどのデータのEntityクラス 12 | * このEntityをPresentation層で利用することはありません。 13 | * Domain層のTranslaterが同じくDomain層にあるModelの型に変換をします。 14 | */ 15 | 16 | import ObjectMapper 17 | import RealmSwift 18 | 19 | /** 20 | * QiitaEntityのリスト 21 | */ 22 | class QiitaItemEntities { 23 | var contentsList : [QiitaItemEntity] = [] 24 | } 25 | 26 | class QiitaItemEntity : Object, Mappable { 27 | 28 | // contents 29 | dynamic var id : String = "" 30 | dynamic var title : String = "" 31 | dynamic var url : String = "" 32 | dynamic var renderedBody : String = "" 33 | dynamic var body : String = "" 34 | dynamic var createdAt : String = "" 35 | dynamic var updatedAt : String = "" 36 | var tags : List = List() 37 | 38 | 39 | // user 40 | dynamic var itemsCount : Int = 0 41 | dynamic var profileImageUrl : String = "" 42 | dynamic var userName : String = "" 43 | dynamic var userId : String = "" 44 | dynamic var githubLoginName : String = "" 45 | 46 | required convenience init? (_ map : Map) { 47 | self.init() 48 | } 49 | 50 | func mapping(map: Map) { 51 | 52 | id <- map["id"] 53 | title <- map["title"] 54 | url <- map["url"] 55 | renderedBody <- map["rendered_body"] 56 | body <- map["body"] 57 | createdAt <- map["created_at"] 58 | updatedAt <- map["updated_at"] 59 | tags <- (map["tags"], ArrayTransform()) 60 | githubLoginName <- map["user.github_login_name"] 61 | itemsCount <- map["user.itemsCount"] 62 | userName <- map["user.name"] 63 | userId <- map["user.id"] 64 | profileImageUrl <- map["user.profile_image_url"] 65 | } 66 | } 67 | 68 | class QiitaItemTagEntity : Object, Mappable { 69 | 70 | dynamic var name : String = "" 71 | 72 | required convenience init? (_ map : Map) { 73 | self.init() 74 | mapping(map) 75 | } 76 | 77 | func mapping(map: Map) { 78 | name <- map["name"] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItem/QiitaItemRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QiitaItemRepository.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | 11 | protocol QiitaItemRepositoryProtocol { 12 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable 13 | } 14 | 15 | class QiitaItemRepository : QiitaItemRepositoryProtocol { 16 | 17 | lazy var dataStore = QiitaItemDataStore() 18 | 19 | func fetch(params params: [String : AnyObject], retryCount: Int) -> Observable { 20 | 21 | return dataStore.fetch(params : params, retryCount : retryCount) 22 | .catchError { error in 23 | 24 | // API Requestでthrowされた時のcatch先 25 | // ここでは空のオブジェクトを入れるようにする 26 | return Observable.just(QiitaItemEntities()) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItem/QiitaItemRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WikipediaRequest.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import ObjectMapper 11 | import SwiftyJSON 12 | 13 | class QiitaItemRequest { 14 | 15 | let url = "https://qiita.com/api/v2/items" 16 | let apiClient = ApiClient.sharedInstance 17 | 18 | init () { 19 | } 20 | 21 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable { 22 | 23 | return apiClient.request(method: .GET, url: url, params: [:]) 24 | .retry(retryCount) 25 | .debug("hgoeghoegheo") 26 | .map { [weak self] (data, response) in 27 | guard let qiitaItemEntities = self?.createResponse(data: data) else { 28 | throw ErrorHandring.Request.ParseError 29 | } 30 | return qiitaItemEntities 31 | } 32 | } 33 | } 34 | 35 | private extension QiitaItemRequest { 36 | 37 | func createResponse(data data : NSData) -> QiitaItemEntities? { 38 | 39 | let jsonData = JSON(data: data) 40 | 41 | guard let data = jsonData.arrayObject else { return nil } 42 | guard let itemData = Mapper().mapArray(data) else { return nil } 43 | 44 | let qiitaItemEntities = QiitaItemEntities() 45 | qiitaItemEntities.contentsList = itemData 46 | 47 | return qiitaItemEntities 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemComment/QiitaItemCommentDataStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QiitaItemCommentDataStore.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import Foundation 11 | 12 | class QiitaItemCommentDataStore : NSObject { 13 | 14 | let request = QiitaItemCommentRequest() 15 | 16 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable { 17 | return request.fetch(params: params, retryCount: retryCount) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemComment/QiitaItemCommentEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WikipediaEntity.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * 11 | * APIなどのデータのEntityクラス 12 | * このEntityをPresentation層で利用することはありません。 13 | * Domain層のTranslaterが同じくDomain層にあるModelの型に変換をします。 14 | */ 15 | 16 | import ObjectMapper 17 | 18 | /** 19 | * QiitaEntityのリスト 20 | */ 21 | class QiitaItemCommentEntities { 22 | var contentsList : [QiitaItemCommentEntity] = [] 23 | } 24 | 25 | class QiitaItemCommentEntity : Mappable { 26 | 27 | // contents 28 | dynamic var body : String = "" 29 | dynamic var createdAt : String = "" 30 | dynamic var id : String = "" 31 | dynamic var renderedBody : String = "" 32 | dynamic var updatedAt : String = "" 33 | dynamic var userDescription : String = "" 34 | dynamic var userFacebookId : String = "" 35 | dynamic var userFolloweesCount : Int = 0 36 | dynamic var userFollowersCount : Int = 0 37 | dynamic var userGithubLoginName : String = "" 38 | dynamic var userId : String = "" 39 | dynamic var userItemCount : Int = 0 40 | dynamic var userLinkedinId : String = "" 41 | dynamic var userLocation : String = "" 42 | dynamic var userName : String = "" 43 | dynamic var userOrganization : String = "" 44 | dynamic var userPermanentId : Int = 0 45 | dynamic var userProfileImageUrl : String = "" 46 | dynamic var userTwitterScreenName : String = "" 47 | dynamic var userwebSiteUrl : String = "" 48 | 49 | required convenience init? (_ map : Map) { 50 | self.init() 51 | } 52 | 53 | func mapping(map: Map) { 54 | body <- map["body"] 55 | createdAt <- map["created_at"] 56 | id <- map["id"] 57 | renderedBody <- map["rerendered_body"] 58 | updatedAt <- map["updated_at"] 59 | userDescription <- map["user.description"] 60 | userFacebookId <- map["user.facebook_id"] 61 | userFolloweesCount <- map["user.followees_count"] 62 | userFollowersCount <- map["user.followers_count"] 63 | userGithubLoginName <- map["user.github_login_name"] 64 | userId <- map["user.id"] 65 | userItemCount <- map["user.items_count"] 66 | userLinkedinId <- map["user.linkedin_id"] 67 | userLocation <- map["user.location"] 68 | userName <- map["user.name"] 69 | userOrganization <- map["user.organization"] 70 | userPermanentId <- map["user.permanent_id"] 71 | userProfileImageUrl <- map["user.profile_image_url"] 72 | userTwitterScreenName <- map["user.twitter_screen_name"] 73 | userwebSiteUrl <- map["user.website_url"] 74 | } 75 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemComment/QiitaItemCommentRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QiitaItemCommentRepository.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | 11 | protocol QiitaItemCommentRepositoryProtocol { 12 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable 13 | } 14 | 15 | class QiitaItemCommentRepository : QiitaItemCommentRepositoryProtocol { 16 | 17 | lazy var dataStore = QiitaItemCommentDataStore() 18 | 19 | func fetch(params params: [String : AnyObject], retryCount: Int) -> Observable { 20 | 21 | return dataStore.fetch(params : params, retryCount : retryCount) 22 | .catchError { error in 23 | // API Requestでthrowされた時のcatch先 24 | // ここでは空のオブジェクトを入れるようにする 25 | return Observable.just(QiitaItemCommentEntities()) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemComment/QiitaItemCommentRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WikipediaRequest.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import ObjectMapper 11 | import SwiftyJSON 12 | 13 | class QiitaItemCommentRequest { 14 | 15 | let prefixUrl = "https://qiita.com/api/v2/items" 16 | let apiClient = ApiClient.sharedInstance 17 | 18 | init () { 19 | } 20 | 21 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable { 22 | 23 | guard let itemId = params["itemId"] as? String else { 24 | return Observable.just(QiitaItemCommentEntities()) 25 | } 26 | 27 | let url = prefixUrl + itemId + "/comments" 28 | 29 | return apiClient.request(method: .GET, url: url, params: [:]) 30 | .retry(retryCount) 31 | .map { [weak self] (data, response) in 32 | guard let qiitaItemEntities = self?.createResponse(data: data) else { 33 | throw ErrorHandring.Request.ParseError 34 | } 35 | return qiitaItemEntities 36 | } 37 | } 38 | } 39 | 40 | private extension QiitaItemCommentRequest { 41 | 42 | func createResponse(data data : NSData) -> QiitaItemCommentEntities? { 43 | 44 | let jsonData = JSON(data: data) 45 | 46 | guard let data = jsonData.arrayObject else { return nil } 47 | guard let itemData = Mapper().mapArray(data) else { return nil } 48 | 49 | let qiitaItemEntities = QiitaItemCommentEntities() 50 | qiitaItemEntities.contentsList = itemData 51 | 52 | return qiitaItemEntities 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemStocker/QiitaItemStockerDataStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QiitaItemStockerDataStore.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import Foundation 11 | 12 | class QiitaItemStockerDataStore : NSObject { 13 | 14 | let request = QiitaItemStockerRequest() 15 | 16 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable { 17 | return request.fetch(params: params, retryCount: retryCount) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemStocker/QiitaItemStockerEntity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WikipediaEntity.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * 11 | * APIなどのデータのEntityクラス 12 | * このEntityをPresentation層で利用することはありません。 13 | * Domain層のTranslaterが同じくDomain層にあるModelの型に変換をします。 14 | */ 15 | 16 | import ObjectMapper 17 | 18 | /** 19 | * QiitaEntityのリスト 20 | */ 21 | class QiitaItemStockerEntities { 22 | var contentsList : [QiitaItemStockerEntity] = [] 23 | } 24 | 25 | class QiitaItemStockerEntity : Mappable { 26 | 27 | // contents 28 | dynamic var stockerDescription: String = "" 29 | dynamic var facebookId : String = "" 30 | dynamic var followeesCount : Int = 0 31 | dynamic var gitHubLoginName : String = "" 32 | dynamic var id : String = "" 33 | dynamic var itemsCount : Int = 0 34 | dynamic var linkedinId : String = "" 35 | dynamic var location : String = "" 36 | dynamic var name : String = "" 37 | dynamic var organization : String = "" 38 | dynamic var permanentId : Int = 0 39 | dynamic var profileImageUrl : String = "" 40 | dynamic var twitterScreenName : String = "" 41 | dynamic var webSiteUrl : String = "" 42 | 43 | required convenience init? (_ map : Map) { 44 | self.init() 45 | } 46 | 47 | func mapping(map: Map) { 48 | stockerDescription <- map["description"] 49 | facebookId <- map["facebook_id"] 50 | followeesCount <- map["followees_count"] 51 | gitHubLoginName <- map["github_login_name"] 52 | id <- map["id"] 53 | itemsCount <- map["items_count"] 54 | linkedinId <- map["linkedin_id"] 55 | location <- map["location"] 56 | name <- map["name"] 57 | organization <- map["organization"] 58 | permanentId <- map["permanent_id"] 59 | profileImageUrl <- map["profile_image_url"] 60 | twitterScreenName <- map["twitter_screen_name"] 61 | webSiteUrl <- map["website_url"] 62 | } 63 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemStocker/QiitaItemStockerRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QiitaItemStockerRepository.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | 11 | protocol QiitaItemStockerRepositoryProtocol { 12 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable 13 | } 14 | 15 | class QiitaItemStockerRepository : QiitaItemStockerRepositoryProtocol { 16 | 17 | lazy var dataStore = QiitaItemStockerDataStore() 18 | 19 | func fetch(params params: [String : AnyObject], retryCount: Int) -> Observable { 20 | 21 | return dataStore.fetch(params : params, retryCount : retryCount) 22 | .catchError { error in 23 | // API Requestでthrowされた時のcatch先 24 | // ここでは空のオブジェクトを入れるようにする 25 | return Observable.just(QiitaItemStockerEntities()) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/QiitaItemStocker/QiitaItemStockerRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WikipediaRequest.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import ObjectMapper 11 | import SwiftyJSON 12 | 13 | class QiitaItemStockerRequest { 14 | 15 | let prefixUrl = "https://qiita.com/api/v2/items" 16 | let apiClient = ApiClient.sharedInstance 17 | 18 | init () { 19 | } 20 | 21 | func fetch(params params : [String : AnyObject], retryCount : Int) -> Observable { 22 | 23 | guard let itemId = params["itemId"] as? String else { 24 | return Observable.just(QiitaItemStockerEntities()) 25 | } 26 | 27 | let url = prefixUrl + itemId + "/stockers" 28 | 29 | //let realm = try! Realm() 30 | return apiClient.request(method: .GET, url: url, params: [:]) 31 | .retry(retryCount) 32 | .map { [weak self] (data, response) in 33 | guard let qiitaItemEntities = self?.createResponse(data: data) else { 34 | throw ErrorHandring.Request.ParseError 35 | } 36 | return qiitaItemEntities 37 | } 38 | } 39 | } 40 | 41 | private extension QiitaItemStockerRequest { 42 | 43 | func createResponse(data data : NSData) -> QiitaItemStockerEntities? { 44 | 45 | let jsonData = JSON(data: data) 46 | 47 | guard let data = jsonData.arrayObject else { return nil } 48 | guard let itemData = Mapper().mapArray(data) else { return nil } 49 | 50 | let qiitaItemEntities = QiitaItemStockerEntities() 51 | qiitaItemEntities.contentsList = itemData 52 | 53 | return qiitaItemEntities 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/Utility/ApiClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiClient.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/07. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Alamofire 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class ApiClient { 14 | 15 | private let manager = Alamofire.Manager.sharedInstance 16 | 17 | static let sharedInstance = ApiClient() 18 | 19 | private init() { } 20 | 21 | func request(method method : Alamofire.Method, url : String, params : [String : AnyObject]) -> Observable<(NSData, NSHTTPURLResponse)> { 22 | 23 | var mutableRequest = NSMutableURLRequest(URL : NSURL(string : url)!) 24 | mutableRequest.cachePolicy = .ReloadIgnoringLocalCacheData 25 | mutableRequest.timeoutInterval = 10 26 | mutableRequest.HTTPMethod = method.rawValue 27 | mutableRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type") 28 | mutableRequest = Alamofire.ParameterEncoding.URL.encode(mutableRequest, parameters: params).0 29 | 30 | return manager.session.rx_response(mutableRequest) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Data/Utility/ArrayTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrayTransform.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RealmSwift 12 | 13 | class ArrayTransform : TransformType { 14 | 15 | typealias Object = List 16 | typealias JSON = Array 17 | 18 | func transformFromJSON(value: AnyObject?) -> List? { 19 | let result = List() 20 | if let tempArr = value as! Array? { 21 | for entry in tempArr { 22 | let mapper = Mapper() 23 | let model : T = mapper.map(entry)! 24 | result.append(model) 25 | } 26 | } 27 | return result 28 | } 29 | 30 | func transformToJSON(value: List?) -> Array? { 31 | if (value?.count > 0) 32 | { 33 | var result = Array() 34 | for entry in value! { 35 | result.append(entry) 36 | } 37 | return result 38 | } 39 | return nil 40 | } 41 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Environment/Const.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Const.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/13. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum StoryboardName : String { 12 | case ListPage = "List" 13 | case DetailPage = "Detail" 14 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Environment/ErrorHandring.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandring.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/08. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ErrorHandring { 12 | 13 | enum Request : ErrorType { 14 | case InvalidParameter 15 | case ParseError 16 | case EmptyData 17 | 18 | func createMessageNSError(title title : String, message : String, code : String) -> ErrorType { 19 | return ErrorHandring.createNSError(title: title, message: message, code: code) 20 | } 21 | } 22 | 23 | static func createNSError(title title : String, message : String, code : String) -> ErrorType { 24 | let userInfo = createUserInfo(title : title, message: message, code: code) 25 | return NSError(domain: code, code : Int(code)!, userInfo: userInfo) 26 | } 27 | 28 | static func createUserInfo(title title : String, message : String, code : String) -> [String : String] { 29 | return [ 30 | NSLocalizedDescriptionKey : title, 31 | NSLocalizedFailureReasonErrorKey : message, 32 | NSLocalizedRecoverySuggestionErrorKey : code 33 | ] 34 | } 35 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Environment/NSDate+Util.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+Util.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSDate { 12 | 13 | func transformToString(format : String = "") -> String { 14 | 15 | let df : NSDateFormatter = NSDateFormatter() 16 | df.locale = NSLocale(localeIdentifier : "ja_JP") 17 | df.timeZone = NSTimeZone(name : "JST") 18 | 19 | if format == "" { 20 | df.dateFormat = "yyyy-MM-dd'T'HH:mm:sszzz" 21 | } else { 22 | df.dateFormat = format 23 | } 24 | return df.stringFromDate(self) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Environment/String+Util.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Util.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | 13 | func transformNSDateGmt() -> NSDate? { 14 | let df = NSDateFormatter() 15 | df.locale = NSLocale(localeIdentifier : "ja_JP") 16 | df.timeZone = NSTimeZone(name : "JST") 17 | df.dateFormat = "yyyy-MM-dd'T'HH:mm:sszzz" 18 | return df.dateFromString(self) 19 | } 20 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Environment/UIStoryboard+Util.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIStoryboard+Util.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/13. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIStoryboard { 13 | 14 | static func instantiateViewController(storyboardName storyboard : StoryboardName, viewControllerId : String!, type : T.Type) -> T { 15 | 16 | let storyboard = UIStoryboard(name : storyboard.rawValue, bundle : nil) 17 | return storyboard.instantiateViewControllerWithIdentifier(viewControllerId) as! T 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Detail/DetailConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailConfigurator.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | class DetailConfigurator { 10 | 11 | static var sharedInstance = DetailConfigurator() 12 | 13 | func configure(viewController : DetailViewController) { 14 | 15 | let useCase : DetailUseCase = DetailUseCase( 16 | qiitaItemStockerRepository : QiitaItemStockerRepository(), 17 | qiitaItemCommentRepository : QiitaItemCommentRepository() 18 | ) 19 | 20 | let presenter : DetailPresenter = DetailPresenter(useCase : useCase) 21 | 22 | viewController.presenter = presenter 23 | } 24 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Detail/Domain/DetailModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailModel.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/11. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxCocoa 11 | 12 | class DetailModelsList { 13 | var contentsList : [String : DetailModels] = [:] 14 | 15 | func getCommentData() -> DetailModels? { 16 | return contentsList["comment"] 17 | } 18 | 19 | func getStockerData() -> DetailModels? { 20 | return contentsList["stocker"] 21 | } 22 | } 23 | 24 | class DetailModels { 25 | var contentsList : [DetailModel] = [] 26 | 27 | func selectedData(index index : Int) -> DetailModel { 28 | return contentsList[index] 29 | } 30 | } 31 | 32 | class DetailModel { 33 | 34 | // QiitaItemEntityにあるデータの中から表示に必要なものだけ記載する 35 | var commentId : String = "" 36 | var commentBody : String = "" 37 | var stockerUserId : String = "" 38 | 39 | // View用 (必要性を感じなければ、上にあるPropertyをそのまま利用でもいいです) 40 | var viewIdWithTitle : Driver = Driver.just("") 41 | var viewUpdatedAt : Driver = Driver.just("") 42 | var viewTags : Driver = Driver.just("") 43 | var viewThumbnail : Driver = Driver.just(nil) 44 | var viewBody : Driver = Driver.just("") 45 | var viewUserId : Driver = Driver.just("") 46 | 47 | init() { 48 | } 49 | 50 | init(data : QiitaItemCommentEntity) { 51 | self.commentId = data.id 52 | self.commentBody = data.body 53 | } 54 | 55 | init(data : QiitaItemStockerEntity) { 56 | self.stockerUserId = data.id 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Detail/Domain/DetailTranslater.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailTranslater.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/11. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class DetailTranslater { 12 | 13 | static func generate(qiitaCommentItems qiitaCommentItems : QiitaItemCommentEntities, qiitaStockerItems : QiitaItemStockerEntities) -> DetailModelsList { 14 | 15 | let modelsList = DetailModelsList() 16 | modelsList.contentsList["comment"] = DetailTranslater.generate(qiitaItems: qiitaCommentItems) 17 | modelsList.contentsList["stocker"] = DetailTranslater.generate(qiitaItems: qiitaStockerItems) 18 | 19 | return modelsList 20 | } 21 | 22 | static func generate(qiitaItems qiitaItems : QiitaItemCommentEntities) -> DetailModels { 23 | 24 | let model = DetailModels() 25 | qiitaItems.contentsList.forEach { entity -> () in 26 | let data = DetailModel(data : entity) 27 | model.contentsList.append(data) 28 | } 29 | 30 | return model 31 | } 32 | 33 | static func generate(qiitaItems qiitaItems : QiitaItemStockerEntities) -> DetailModels { 34 | 35 | let model = DetailModels() 36 | qiitaItems.contentsList.forEach { entity -> () in 37 | let data = DetailModel(data : entity) 38 | model.contentsList.append(data) 39 | } 40 | 41 | return model 42 | } 43 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Detail/Domain/DetailUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailUseCase.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/11. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RxCocoa 12 | 13 | protocol DetailUseCaseProtocol : class { 14 | //func loadData(itemId itemdId : String) -> Observable 15 | } 16 | 17 | class DetailUseCase : DetailUseCaseProtocol { 18 | 19 | var qiitaItemStockerRepository : QiitaItemStockerRepositoryProtocol! 20 | var qiitaItemCommentRepository : QiitaItemCommentRepositoryProtocol! 21 | 22 | init (qiitaItemStockerRepository : QiitaItemStockerRepositoryProtocol, 23 | qiitaItemCommentRepository : QiitaItemCommentRepositoryProtocol) { 24 | self.qiitaItemStockerRepository = qiitaItemStockerRepository 25 | self.qiitaItemCommentRepository = qiitaItemCommentRepository 26 | } 27 | 28 | deinit { 29 | } 30 | } 31 | 32 | extension DetailUseCase { 33 | 34 | /** 35 | * Observable.zipやcombineLatestを利用すると並列処理っぽくできてハッピーです 36 | */ 37 | /* 38 | func loadData(itemId itemId : String) -> Observable { 39 | 40 | return Observable.zip( 41 | qiitaItemStockerRepository.fetch(params: ["itemId" : itemId], retryCount: 3), 42 | qiitaItemCommentRepository.fetch(params: ["itemId" : itemId], retryCount: 3) 43 | ) { stocker, comment in 44 | return (stocker, comment) 45 | } 46 | .subscribeOn(Dependencies.sharedInstance.backgroundScheduler) 47 | .flatMap { stocker, comment -> Observable in 48 | return Observable.just(DetailTranslater.generate( 49 | qiitaCommentItems : comment, 50 | qiitaStockerItems : stocker 51 | ) 52 | ) 53 | } 54 | .observeOn(Dependencies.sharedInstance.mainScheduler) 55 | .shareReplay(1) 56 | } 57 | */ 58 | } 59 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Detail/Presentation/DetailPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailPresenter.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RxCocoa 12 | 13 | protocol DetailPresenterProtocol : class { 14 | var viewModels : DetailModelsList {get} 15 | func load(itemId itemId : String) 16 | } 17 | 18 | class DetailPresenter : DetailPresenterProtocol { 19 | 20 | var useCase : DetailUseCaseProtocol! 21 | 22 | // 公開 23 | private(set) var viewModels : DetailModelsList = DetailModelsList() 24 | 25 | // 非公開 26 | private let disposeBag = DisposeBag() 27 | 28 | init (useCase : DetailUseCaseProtocol) { 29 | self.useCase = useCase 30 | } 31 | 32 | deinit { 33 | } 34 | } 35 | 36 | extension DetailPresenter { 37 | 38 | func load(itemId itemId : String) { 39 | 40 | /* 41 | Observable.just(true) 42 | .flatMap { [unowned self] _ in 43 | return self.useCase.loadData(itemId : itemId) 44 | } 45 | .debug() 46 | .subscribe( 47 | onNext : { data in 48 | print(data) 49 | }, 50 | onError : { error in 51 | }, 52 | onCompleted : { _ in 53 | } 54 | ).addDisposableTo(disposeBag) 55 | */ 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Detail/Presentation/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class DetailViewController: UIViewController { 14 | 15 | var presenter : DetailPresenterProtocol! 16 | 17 | @IBOutlet weak var viewItemTitle: UILabel! 18 | @IBOutlet weak var viewItemId: UILabel! 19 | 20 | let disposeBag = DisposeBag() 21 | var itemId : String = "" 22 | var itemTitle : String = "" 23 | 24 | override func awakeFromNib() { 25 | super.awakeFromNib() 26 | DetailConfigurator.sharedInstance.configure(self) 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | // Do any additional setup after loading the view. 32 | 33 | viewItemTitle.text = itemTitle 34 | viewItemId.text = itemId 35 | 36 | setSubscribe() 37 | presenter.load(itemId : itemId) 38 | } 39 | 40 | override func didReceiveMemoryWarning() { 41 | super.didReceiveMemoryWarning() 42 | // Dispose of any resources that can be recreated. 43 | } 44 | } 45 | 46 | private extension DetailViewController { 47 | 48 | func setSubscribe() { 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Domain/ListModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListModel.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/11. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * 11 | * Presentaion層(表示用のProperty定義も含む)で利用するModelを定義します 12 | * (ViewModelという認識でいいと思っています) 13 | * そうすることで、DateStoreから取得するデータに変更(型クラスの変更)があっても 14 | * Domain層の変更だけで、Presentation層に手を加えることがなくなります。 15 | * 16 | * また、DataStoreから取得したデータをここでModelに変換して、PresenterのところでViewModelを用意することもできますが 17 | * Sceneの表示に関するデータを集約したモデルとしてここに定義しておくと、責任箇所がはっきりして良いと思っています。 18 | */ 19 | 20 | import Foundation 21 | 22 | /** 23 | * TableViewのように、ListModelが複数ある場合、[ListModel]としてもいいですが 24 | * ○件取得などのattributeがある場合、[ListModel]の他にattribute用の型が必要になっていきます。 25 | * そのため、ここでさらにそれらをまとめる用のListModelsも記載しておきます。 26 | * この話はEntity側でも同様です。 27 | */ 28 | class ListModels { 29 | var contentsList : [ListModel] = [] 30 | 31 | func selectedData(index index : Int) -> ListModel { 32 | return self.contentsList[index] 33 | } 34 | } 35 | 36 | class ListModel { 37 | 38 | var valueObject : ListValueObject = ListValueObject() 39 | 40 | var viewModel : ListViewModel = ListViewModel() 41 | 42 | init() { 43 | } 44 | } 45 | 46 | class ListValueObject { 47 | 48 | // QiitaItemEntityにあるデータの中から表示に必要なものだけ記載する 49 | var id : String = "" 50 | var title : String = "" 51 | var tags : [String] = [] 52 | var updatedAt : String = "" 53 | var body : String = "" 54 | var userId : String = "" 55 | var userImage : String = "" 56 | 57 | init () { 58 | 59 | } 60 | 61 | init(data : QiitaItemEntity) { 62 | self.id = data.id 63 | self.title = data.title 64 | self.updatedAt = data.updatedAt 65 | self.userImage = data.profileImageUrl 66 | self.body = data.body 67 | self.userId = data.userId 68 | 69 | data.tags.forEach { element in 70 | self.tags.append(element.name) 71 | } 72 | } 73 | } 74 | 75 | class ListViewModel { 76 | 77 | // View用 (必要性を感じなければ、上にあるPropertyをそのまま利用でもいいです) 78 | var viewIdWithTitle : String = "" 79 | var viewUpdatedAt : String = "" 80 | var viewTags : String = "" 81 | var viewThumbnail : NSURL? = nil 82 | var viewBody : String = "" 83 | var viewUserId : String = "" 84 | 85 | init () { 86 | 87 | } 88 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Domain/ListTranslater.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListTranslater.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/11. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * Entity -> Modelを生成する処理 11 | * 12 | * 複数APIなどでリクエストした場合、DataStoreから流れるEntityの型クラスが複数出てくると思うので 13 | * その数分だけgenerateメソッドを用意する 14 | * (genericsとかでイイカンジにできるのであれば素敵だと思います。 15 | * ただEntityがprotocolで定義できるようなものでないと難しいかなと思っています。いいアイデア募集中) 16 | * 17 | */ 18 | 19 | import Foundation 20 | 21 | class ListTranslater { 22 | 23 | static func generate(qiitaItems qiitaItems : QiitaItemEntities) -> ListModels { 24 | 25 | let models = ListModels() 26 | 27 | qiitaItems.contentsList.forEach { data -> () in 28 | let model = ListModel() 29 | model.valueObject = ListValueObject(data : data) 30 | models.contentsList.append(model) 31 | } 32 | 33 | return models 34 | } 35 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Domain/ListUseCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListUseCase.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/11. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * 11 | * 各Scenesで利用するビジネスロジックをここに集約 & EntityなどをPresentaion層用の型を変換する。 12 | * 基本的にreturnはObservable型ので、宣言が集約した場所です 13 | * Observableにしておくと、Presenterのところで加工や合成、並列で取得など操作がスマートです 14 | * (disposeBagに入れるところは極力まとまっていた方がいいです) 15 | * 16 | * Repository経由でデータを取得し、Presentationで利用する型を生成する 17 | * また、依存度注入し疎結合にしておくと素敵です。 18 | * 疎結合になることによって、Protocolを利用しているテストクラスを用いてテストがしやすくなります。 19 | * 20 | */ 21 | 22 | import Foundation 23 | import RxSwift 24 | 25 | protocol ListUseCaseProtocol : class { 26 | func loadData() -> Observable 27 | } 28 | 29 | class ListUseCase : ListUseCaseProtocol { 30 | 31 | private let qiitaItemRepository : QiitaItemRepositoryProtocol! 32 | 33 | init (qiitaItemRepository : QiitaItemRepositoryProtocol) { 34 | self.qiitaItemRepository = qiitaItemRepository 35 | } 36 | 37 | deinit { 38 | } 39 | } 40 | 41 | extension ListUseCase { 42 | 43 | func loadData() -> Observable { 44 | 45 | return qiitaItemRepository.fetch(params : [:], retryCount: 3) 46 | .map { ListTranslater.generate(qiitaItems : $0) } 47 | .map { [unowned self] in self.updateContentsModel(models : $0) } 48 | } 49 | } 50 | 51 | private extension ListUseCase { 52 | 53 | func updateContentsModel(models models : ListModels) -> ListModels { 54 | 55 | models.contentsList.forEach { [unowned self] model -> () in 56 | self.createViewModel(model : model) 57 | } 58 | return models 59 | } 60 | 61 | func createViewModel(model model : ListModel) -> ListModel { 62 | 63 | let thumbnailUrl = NSURL(string : model.valueObject.userImage) 64 | let title = model.valueObject.title 65 | let tags = "タグ : " + model.valueObject.tags.joinWithSeparator(",") 66 | let body = model.valueObject.body 67 | let userId = model.valueObject.userId 68 | 69 | if let date = model.valueObject.updatedAt.transformNSDateGmt() { 70 | let dateString = date.transformToString("yyyy年MM月dd日") + " 更新" 71 | model.viewModel.viewUpdatedAt = dateString 72 | } 73 | 74 | model.viewModel.viewThumbnail = thumbnailUrl 75 | model.viewModel.viewIdWithTitle = title 76 | model.viewModel.viewTags = tags 77 | model.viewModel.viewBody = body 78 | model.viewModel.viewUserId = userId 79 | 80 | return model 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/ListConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListConfigurator.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * ここで、依存度注入することによって 11 | * ViewController / Presenter / UseCase / Repositoryを疎結合にする 12 | */ 13 | 14 | class ListConfigurator { 15 | 16 | static var sharedInstance = ListConfigurator() 17 | 18 | func configure(viewController : ListViewController) { 19 | 20 | let useCase : ListUseCase = ListUseCase( 21 | qiitaItemRepository : QiitaItemRepository() 22 | ) 23 | 24 | let presenter : ListPresenter = ListPresenter(useCase : useCase) 25 | 26 | let router = ListRouter(viewController: viewController) 27 | 28 | viewController.presenter = presenter 29 | viewController.router = router 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Presentation/Cells/ListTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListTableViewCell.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import WebImage 13 | 14 | class ListTableViewCell: UITableViewCell { 15 | 16 | @IBOutlet weak var title: UILabel! 17 | @IBOutlet weak var tags: UILabel! 18 | @IBOutlet weak var updatedAt: UILabel! 19 | @IBOutlet weak var thumbnail: UIImageView! 20 | @IBOutlet weak var body: UILabel! 21 | @IBOutlet weak var userId: UILabel! 22 | 23 | let disposeBag = DisposeBag() 24 | let boldFont = UIFont.boldSystemFontOfSize(17.0) 25 | 26 | 27 | override func awakeFromNib() { 28 | super.awakeFromNib() 29 | // Initialization code 30 | } 31 | 32 | override func setSelected(selected: Bool, animated: Bool) { 33 | super.setSelected(selected, animated: animated) 34 | 35 | // Configure the view for the selected state 36 | } 37 | 38 | func bindData(model model : ListModel) { 39 | 40 | Driver.just(model.viewModel.viewIdWithTitle).drive(title.rx_text).addDisposableTo(disposeBag) 41 | Driver.just(model.viewModel.viewTags).drive(tags.rx_text).addDisposableTo(disposeBag) 42 | Driver.just(model.viewModel.viewUpdatedAt).drive(updatedAt.rx_text).addDisposableTo(disposeBag) 43 | Driver.just(model.viewModel.viewThumbnail).driveNext { [unowned self] thumbnail in 44 | self.thumbnail.sd_setImageWithURL(thumbnail) 45 | }.addDisposableTo(disposeBag) 46 | Driver.just(model.viewModel.viewBody).drive(body.rx_text).addDisposableTo(disposeBag) 47 | Driver.just(model.viewModel.viewUserId).drive(userId.rx_text).addDisposableTo(disposeBag) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Presentation/ListPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListPresenter.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * ViewController側のアクションを受け取り、必要があればUseCaseにアクセスする。 11 | * UseCaseから取得したデータを加工し、ViewController/Viewの変更を通知する 12 | * (プレゼンテーションロジック) 13 | * 14 | * また、基本的に利用できる型はModelで定義したListModels or ListModelのみ 15 | * (そうすることで、取得するデータ先が変更になっても、PresenterやViewControllerを修正することがなくなっていきます) 16 | */ 17 | 18 | 19 | import Foundation 20 | import RxSwift 21 | import RxCocoa 22 | 23 | protocol ListPresenterProtocol : class { 24 | 25 | // viewModel部分は作るものによってはObservableやSubjectにする場合もあると思いますが 26 | // 今回はそのままです 27 | var viewReloadData : PublishSubject {get} 28 | var viewErrorPage : PublishSubject {get} 29 | var isStartActivityIndicator : PublishSubject {get} 30 | 31 | func load() 32 | } 33 | 34 | class ListPresenter : ListPresenterProtocol { 35 | 36 | private let useCase : ListUseCaseProtocol! 37 | 38 | // 公開 39 | private(set) var viewReloadData = PublishSubject() 40 | private(set) var viewErrorPage = PublishSubject() 41 | private(set) var isStartActivityIndicator = PublishSubject() 42 | 43 | // 非公開 44 | private let disposeBag = DisposeBag() 45 | 46 | init (useCase : ListUseCaseProtocol) { 47 | self.useCase = useCase 48 | } 49 | 50 | deinit { 51 | } 52 | } 53 | 54 | extension ListPresenter { 55 | 56 | func load() { 57 | 58 | Observable.just() 59 | .doOn { [unowned self] in self.isStartActivityIndicator.onNext(true) } 60 | .observeOn(Dependencies.sharedInstance.backgroundScheduler) 61 | .flatMap { [unowned self] in self.useCase.loadData() } 62 | .observeOn(Dependencies.sharedInstance.mainScheduler) 63 | .shareReplay(1) 64 | .doOn { [unowned self] in self.isStartActivityIndicator.onNext(false) } 65 | .subscribe( 66 | // ストリームから流れたデータをViewModelsにセット 67 | onNext : { [weak self] models in 68 | 69 | guard let weakSelf = self else { return } 70 | weakSelf.viewReloadData.onNext(models) 71 | weakSelf.isStartActivityIndicator.onNext(false) 72 | }, 73 | // エラーが流れたらエラーページを表示 74 | onError : { [weak self] error in 75 | guard let weakSelf = self else { return } 76 | weakSelf.viewErrorPage.onNext(true) 77 | weakSelf.isStartActivityIndicator.onNext(false) 78 | }, 79 | // 完了時にはインジケータを隠す 80 | // (ちなみにonErrorに入るとonCompletedには流れません) 81 | onCompleted : { _ in } 82 | ).addDisposableTo(disposeBag) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Presentation/ListRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListRouter.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/13. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | /** 10 | * ページ遷移先 11 | * 正直なところ、ViewControllerにおいたままでも良い気がしています。 12 | * weakとはいえ、viewControllerとpresenterを渡しているのがなんとも… 13 | */ 14 | 15 | import Foundation 16 | import UIKit 17 | 18 | protocol ListRouterProtocol { 19 | func navigateToDetailScenes(index index : Int) 20 | } 21 | 22 | class ListRouter : ListRouterProtocol { 23 | 24 | weak private var viewController : ListViewController! 25 | 26 | init (viewController : ListViewController) { 27 | self.viewController = viewController 28 | } 29 | 30 | func navigateToDetailScenes(index index : Int) { 31 | 32 | let viewController = UIStoryboard.instantiateViewController( 33 | storyboardName: StoryboardName.DetailPage, 34 | viewControllerId: StoryboardName.DetailPage.rawValue, 35 | type : DetailViewController.self) 36 | 37 | let model = self.viewController.models.selectedData(index: index) 38 | viewController.itemId = model.valueObject.id 39 | viewController.itemTitle = model.valueObject.title 40 | 41 | self.viewController.navigationController?.pushViewController(viewController, animated: true) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/List/Presentation/ListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListViewController.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class ListViewController: UIViewController { 14 | 15 | @IBOutlet weak var errorView: UIView! 16 | @IBOutlet weak var tableView: UITableView! 17 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 18 | 19 | var presenter : ListPresenterProtocol! 20 | var router : ListRouterProtocol! 21 | 22 | var models: ListModels = ListModels() 23 | let disposeBag = DisposeBag() 24 | let tableViewRefreshControl = UIRefreshControl() 25 | 26 | override func awakeFromNib() { 27 | super.awakeFromNib() 28 | ListConfigurator.sharedInstance.configure(self) 29 | } 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | // Do any additional setup after loading the view. 34 | 35 | // 基本的にはStoryboard側でdelegateとdatasourceをひも付けちゃう方がいいですが 36 | // 今回はコードの方に記載しました 37 | tableView.delegate = self 38 | tableView.dataSource = self 39 | 40 | tableView.addSubview(tableViewRefreshControl) 41 | tableView.rowHeight = UITableViewAutomaticDimension 42 | tableView.estimatedRowHeight = 140 43 | 44 | setSubscribe() 45 | presenter.load() 46 | } 47 | 48 | override func didReceiveMemoryWarning() { 49 | super.didReceiveMemoryWarning() 50 | // Dispose of any resources that can be recreated. 51 | } 52 | } 53 | 54 | /** 55 | * tableViewあたりはRxCocoaとか標準のものかお好みで 56 | * 今回は、タップ時のページ遷移はrx_itemSelectedを用いて、残りは標準のものを利用しています 57 | */ 58 | extension ListViewController : UITableViewDataSource, UITableViewDelegate { 59 | 60 | func numberOfSectionsInTableView(tableView: UITableView) -> Int { 61 | return 1 62 | } 63 | 64 | func tableView(tableView : UITableView, numberOfRowsInSection section : Int) -> Int { 65 | return models.contentsList.count ?? 1 66 | } 67 | 68 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 69 | 70 | let cell = tableView.dequeueReusableCellWithIdentifier("ListTableViewCell", forIndexPath: indexPath) as! ListTableViewCell 71 | 72 | guard indexPath.section == 0 && models.contentsList.count > indexPath.item else { 73 | return cell 74 | } 75 | 76 | cell.bindData(model: models.contentsList[indexPath.row]) 77 | return cell 78 | } 79 | } 80 | 81 | private extension ListViewController { 82 | 83 | func setSubscribe() { 84 | 85 | presenter.viewReloadData.subscribeNext { [unowned self] models in 86 | self.models = models 87 | self.errorView.hidden = true 88 | self.tableView.hidden = false 89 | self.tableView.reloadData() 90 | }.addDisposableTo(disposeBag) 91 | 92 | presenter.viewErrorPage.filter {$0 == true}.subscribeNext { [unowned self] _ in 93 | self.errorView.hidden = false 94 | self.tableView.hidden = true 95 | }.addDisposableTo(disposeBag) 96 | 97 | presenter.isStartActivityIndicator.subscribeNext { [unowned self] status in 98 | if status == true { 99 | self.activityIndicator.startAnimating() 100 | self.activityIndicator.hidden = false 101 | } else { 102 | self.activityIndicator.stopAnimating() 103 | self.activityIndicator.hidden = true 104 | } 105 | }.addDisposableTo(disposeBag) 106 | 107 | tableViewRefreshControl.rx_controlEvent(.ValueChanged).subscribeNext { [unowned self] _ in 108 | self.presenter.load() 109 | self.tableViewRefreshControl.endRefreshing() 110 | }.addDisposableTo(disposeBag) 111 | 112 | tableView.rx_itemSelected.subscribeNext { [unowned self] indexPath in 113 | self.tableView.deselectRowAtIndexPath(indexPath, animated: true) 114 | self.router.navigateToDetailScenes(index : indexPath.row) 115 | }.addDisposableTo(disposeBag) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Scenes/Utility/Dependencies.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dependencies.swift 3 | // RxSwiftCleneArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/12. 6 | // Copyright © 2016年 kirou_16. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class Dependencies { 14 | 15 | let mainScheduler = MainScheduler.instance 16 | let backgroundScheduler : ImmediateSchedulerType = { 17 | return ConcurrentDispatchQueueScheduler(globalConcurrentQueueQOS : .UserInteractive) 18 | }() 19 | 20 | static let sharedInstance = Dependencies() 21 | 22 | private init() {} 23 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Storybords/Detail.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 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 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/App/Storybords/List.Storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 86 | 92 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxSwiftCleanArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/14. 6 | // Copyright © 2016年 tamayuru. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/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 | 27 | 28 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitecture/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | List 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitectureTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitectureTests/List/ListPresenterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListPresenterTests.swift 3 | // RxSwiftCleanArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/14. 6 | // Copyright © 2016年 tamayuru. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import RxSwiftCleanArchitecture 11 | 12 | import RxSwift 13 | import RxTest 14 | 15 | class ListPresenterCaseTests : XCTestCase { 16 | 17 | var disposeBag : DisposeBag! 18 | var scheduler : TestScheduler! 19 | 20 | override func setUp() { 21 | super.setUp() 22 | disposeBag = DisposeBag() 23 | scheduler = TestScheduler(initialClock: 0) 24 | } 25 | 26 | override func tearDown() { 27 | super.tearDown() 28 | } 29 | 30 | func testUseCaseNormal() { 31 | 32 | let loading = scheduler.createObserver(Bool) 33 | 34 | let presenter = ListPresenter(useCase : QiitaItemNormalTestUseCase()) 35 | 36 | // subscribeNext時に利用するSubjectから通知が来るか 37 | presenter.viewReloadData 38 | .asObserver() 39 | .bindTo(loading) 40 | .addDisposableTo(disposeBag) 41 | 42 | // データが何件流れてくるか 43 | let elementsCount = scheduler.createObserver(Int) 44 | presenter.viewReloadData 45 | .asObserver() 46 | .map { _ -> Int in return presenter.viewModels.contentsList.count } 47 | .bindTo(elementsCount) 48 | .addDisposableTo(disposeBag) 49 | 50 | scheduler.scheduleAt(100) { presenter.viewReloadData.onNext(true) } 51 | scheduler.scheduleAt(200) { presenter.viewReloadData.onNext(false) } 52 | scheduler.scheduleAt(500) { presenter.load() } 53 | scheduler.start() 54 | 55 | XCTAssertEqual(loading.events, [ 56 | next(100, true), 57 | next(200, false), 58 | next(500, true), 59 | ]) 60 | 61 | XCTAssertEqual(elementsCount.events, [ 62 | next(100, 0), 63 | next(200, 0), 64 | next(500, 10), 65 | ]) 66 | } 67 | 68 | func testUseCaseError() { 69 | 70 | let presenter = ListPresenter(useCase : QiitaItemNormalTestUseCase()) 71 | 72 | // subscribeError時に利用するSubjectから通知が来るか 73 | presenter.viewErrorPage.asObserver().subscribeNext { 74 | XCTAssertTrue($0) 75 | }.addDisposableTo(disposeBag) 76 | 77 | presenter.load() 78 | } 79 | } 80 | 81 | class QiitaItemNormalTestUseCase : ListUseCaseProtocol { 82 | func loadData() -> Observable { 83 | let listModels = ListModels() 84 | // 10件つくる 85 | for _ in 0..<10 { 86 | listModels.contentsList.append(ListModel()) 87 | } 88 | return Observable.just(listModels) 89 | } 90 | } 91 | 92 | class QiitaItemErrorTestUseCase : ListUseCaseProtocol { 93 | func loadData() -> Observable { 94 | return Observable.just().map { 95 | throw ErrorHandring.Request.EmptyData 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitectureTests/List/ListUseCaseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListUseCaseTests.swift 3 | // RxSwiftCleanArchitecture 4 | // 5 | // Created by kirou_16 on 2016/08/14. 6 | // Copyright © 2016年 tamayuru. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import RxSwiftCleanArchitecture 12 | import RxSwift 13 | import RxTests 14 | 15 | class ListUseCaseTests : XCTestCase { 16 | 17 | var disposeBag : DisposeBag! 18 | var scheduler : TestScheduler! 19 | 20 | override func setUp() { 21 | disposeBag = DisposeBag() 22 | scheduler = TestScheduler(initialClock: 0) 23 | } 24 | 25 | func testUseCaseNormal() { 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /RxSwiftCleanArchitectureTests/RxSwiftCleanArchitectureTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxSwiftCleanArchitectureTests.swift 3 | // RxSwiftCleanArchitectureTests 4 | // 5 | // Created by kirou_16 on 2016/08/14. 6 | // Copyright © 2016年 tamayuru. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import RxSwiftCleanArchitecture 11 | 12 | class RxSwiftCleanArchitectureTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitectureUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /RxSwiftCleanArchitectureUITests/RxSwiftCleanArchitectureUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxSwiftCleanArchitectureUITests.swift 3 | // RxSwiftCleanArchitectureUITests 4 | // 5 | // Created by kirou_16 on 2016/08/14. 6 | // Copyright © 2016年 tamayuru. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class RxSwiftCleanArchitectureUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // 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. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------