├── .gitignore
├── LICENSE
├── Podfile
├── Podfile.lock
├── README.md
├── ToDo.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── ToDo.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── ToDo
├── App
│ ├── AppComponent.swift
│ └── AppDelegate.swift
├── Models
│ ├── Config.swift
│ ├── Item.swift
│ └── ItemViewModel.swift
├── RIBs
│ ├── ActionableItems
│ │ ├── DrawerActionableItem.swift
│ │ ├── ListActionableItem.swift
│ │ ├── LoggedInActionableItem.swift
│ │ └── RootActionableItem.swift
│ ├── Common
│ │ ├── LoadingPresentable.swift
│ │ └── UINavigationController.swift
│ ├── Drawer
│ │ ├── DrawerBuilder.swift
│ │ ├── DrawerComponent+List.swift
│ │ ├── DrawerComponent+Menu.swift
│ │ ├── DrawerInteractor.swift
│ │ ├── DrawerRouter.swift
│ │ └── DrawerViewController.swift
│ ├── Item
│ │ ├── ItemBuilder.swift
│ │ ├── ItemInteractor.swift
│ │ ├── ItemRouter.swift
│ │ ├── ItemViewController.swift
│ │ └── ItemViewController.xib
│ ├── List
│ │ ├── ListBuilder.swift
│ │ ├── ListComponent+Item.swift
│ │ ├── ListInteractor.swift
│ │ ├── ListRouter.swift
│ │ ├── ListViewController.swift
│ │ └── ListViewController.xib
│ ├── LoggedIn
│ │ ├── LoggedInBuilder.swift
│ │ ├── LoggedInComponent+Drawer.swift
│ │ ├── LoggedInInteractor.swift
│ │ └── LoggedInRouter.swift
│ ├── LoggedOut
│ │ ├── LoggedOutBuilder.swift
│ │ ├── LoggedOutInteractor.swift
│ │ ├── LoggedOutRouter.swift
│ │ ├── LoggedOutViewController.swift
│ │ └── LoggedOutViewController.xib
│ ├── Menu
│ │ ├── MenuBuilder.swift
│ │ ├── MenuInteractor.swift
│ │ ├── MenuRouter.swift
│ │ ├── MenuViewController.swift
│ │ └── MenuViewController.xib
│ ├── Root
│ │ ├── RootBuilder.swift
│ │ ├── RootComponent+LoggedIn.swift
│ │ ├── RootComponent+LoggedOut.swift
│ │ ├── RootInteractor.swift
│ │ ├── RootRouter.swift
│ │ └── RootViewController.swift
│ └── Workflows
│ │ ├── DeeplinkWorkflow
│ │ └── DeeplinkWorkflow.swift
│ │ └── RootWorkflow
│ │ └── RootWorkflow.swift
├── Resources
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Services
│ └── ToDoService.swift
├── Utils
│ └── Date.swift
└── Views
│ ├── ItemCell.swift
│ └── ItemCell.xib
├── ToDoTests
├── Info.plist
└── ToDoTests.swift
└── ToDoUITests
├── Info.plist
└── ToDoUITests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | # Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | # Carthage/Checkouts
54 |
55 | Carthage/Build
56 |
57 | # fastlane
58 | #
59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60 | # screenshots whenever they are needed.
61 | # For more information about the recommended setup visit:
62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
63 |
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Dev4Jam
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '11.4'
3 |
4 | target 'ToDo' do
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | # Pods for ToDo
7 | use_frameworks!
8 |
9 | # Pods for ToDo
10 | pod 'RIBs'
11 | pod 'SnapKit'
12 | pod 'RxCocoa'
13 | pod 'IGListKit'
14 | pod 'TransitionButton'#, :git => "https://github.com/AladinWay/TransitionButton.git"
15 | pod 'SVProgressHUD'
16 | pod 'DynamicButton'
17 | pod 'Realm'
18 | pod 'RealmSwift'
19 | pod 'REFrostedViewController'
20 | pod 'Eureka'
21 |
22 | target 'ToDoTests' do
23 | inherit! :search_paths
24 | # Pods for testing
25 | end
26 |
27 | target 'ToDoUITests' do
28 | inherit! :search_paths
29 | # Pods for testing
30 | end
31 |
32 | end
33 |
34 | post_install do |installer|
35 | installer.pods_project.targets.each do |target|
36 | target.build_configurations.each do |config|
37 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.4'
38 | config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'
39 | #config.build_settings['ENABLE_BITCODE'] = 'NO'
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - DynamicButton (6.2.1)
3 | - Eureka (5.3.3)
4 | - IGListDiffKit (4.0.0)
5 | - IGListKit (4.0.0):
6 | - IGListDiffKit (= 4.0.0)
7 | - Realm (10.7.2):
8 | - Realm/Headers (= 10.7.2)
9 | - Realm/Headers (10.7.2)
10 | - RealmSwift (10.7.2):
11 | - Realm (= 10.7.2)
12 | - REFrostedViewController (2.4.8)
13 | - RIBs (0.9.1):
14 | - RxSwift (~> 4.0)
15 | - RxCocoa (4.5.0):
16 | - RxSwift (>= 4.4.2, ~> 4.4)
17 | - RxSwift (4.5.0)
18 | - SnapKit (5.0.1)
19 | - SVProgressHUD (2.2.5)
20 | - TransitionButton (0.5.3)
21 |
22 | DEPENDENCIES:
23 | - DynamicButton
24 | - Eureka
25 | - IGListKit
26 | - Realm
27 | - RealmSwift
28 | - REFrostedViewController
29 | - RIBs
30 | - RxCocoa
31 | - SnapKit
32 | - SVProgressHUD
33 | - TransitionButton
34 |
35 | SPEC REPOS:
36 | trunk:
37 | - DynamicButton
38 | - Eureka
39 | - IGListDiffKit
40 | - IGListKit
41 | - Realm
42 | - RealmSwift
43 | - REFrostedViewController
44 | - RIBs
45 | - RxCocoa
46 | - RxSwift
47 | - SnapKit
48 | - SVProgressHUD
49 | - TransitionButton
50 |
51 | SPEC CHECKSUMS:
52 | DynamicButton: 95f30460793bd3510a5c213ac1b810a5e3d54b6a
53 | Eureka: e6d720290c56bb23333ad1a0e020b2a98a0e9a43
54 | IGListDiffKit: 665d6cf43ce726e676013db9c7d6c4294259b6b2
55 | IGListKit: fd5a5d21935298f5849fa49d426843cff97b77c7
56 | Realm: e523da9ade306c5ae87e85dc09fdef148d3e1cc1
57 | RealmSwift: 4f6758c3adbdcc87f7b7777107226532a077f61c
58 | REFrostedViewController: a893b4979ba1f200c547aef9ff4c3fd5a18f5923
59 | RIBs: 879184fbca567425c488d6b3d14a9c5abfbf9d47
60 | RxCocoa: cbf70265dc65a981d4ac982e513c10cf23df24a0
61 | RxSwift: f172070dfd1a93d70a9ab97a5a01166206e1c575
62 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb
63 | SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
64 | TransitionButton: 36283346e17c64775a1b3bc3a7a72940a367de1f
65 |
66 | PODFILE CHECKSUM: 82353c97b5e1940f4a62bd7f098391a76d858f4b
67 |
68 | COCOAPODS: 1.10.1
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | # ToDo
9 |
10 | ToDo iOS app is a demo of RIBs architecture https://github.com/uber/RIBs/wiki
11 |
12 | # You can find examples of:
13 |
14 | 0. RIBs architecture in real application
15 | 1. Presenting / Dismissing modal view controller
16 | 2. Pushing / Poping a view controller
17 | 3. Handling back gesture and back button tap in UINavigationController
18 | 4. Working with system view controllers (UIAlertController and others)
19 | 5. Side/drawer menu
20 | 6. Handling deep links
21 | 7. Building workflows
22 | 8. Scopes
23 |
24 |
--------------------------------------------------------------------------------
/ToDo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 054BB18E6B3628A601DCE50B /* Pods_ToDoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4E4874F7ED8C2FBCB7BB86E /* Pods_ToDoTests.framework */; };
11 | 0A0CF40820231EF20010892B /* LoggedInActionableItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40020231EF20010892B /* LoggedInActionableItem.swift */; };
12 | 0A0CF40920231EF20010892B /* DrawerActionableItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40120231EF20010892B /* DrawerActionableItem.swift */; };
13 | 0A0CF40A20231EF20010892B /* RootActionableItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40220231EF20010892B /* RootActionableItem.swift */; };
14 | 0A0CF40B20231EF20010892B /* RootWorkflow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40520231EF20010892B /* RootWorkflow.swift */; };
15 | 0A0CF40C20231EF20010892B /* DeeplinkWorkflow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40720231EF20010892B /* DeeplinkWorkflow.swift */; };
16 | 0A0CF40E20231FA60010892B /* UINavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40D20231FA60010892B /* UINavigationController.swift */; };
17 | 0A0CF410202321530010892B /* LoggedInComponent+Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF40F202321530010892B /* LoggedInComponent+Drawer.swift */; };
18 | 0A0CF412202324460010892B /* DrawerComponent+List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF411202324460010892B /* DrawerComponent+List.swift */; };
19 | 0A0CF4192023D0940010892B /* MenuRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF4142023D0940010892B /* MenuRouter.swift */; };
20 | 0A0CF41A2023D0940010892B /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF4152023D0940010892B /* MenuViewController.swift */; };
21 | 0A0CF41B2023D0940010892B /* MenuBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF4162023D0940010892B /* MenuBuilder.swift */; };
22 | 0A0CF41C2023D0940010892B /* MenuInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF4172023D0940010892B /* MenuInteractor.swift */; };
23 | 0A0CF41D2023D0940010892B /* MenuViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A0CF4182023D0940010892B /* MenuViewController.xib */; };
24 | 0A0CF41F2023D0B50010892B /* DrawerComponent+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF41E2023D0B50010892B /* DrawerComponent+Menu.swift */; };
25 | 0A0CF4212023D45B0010892B /* ListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A0CF4202023D45B0010892B /* ListViewController.xib */; };
26 | 0A0CF4232023DDCD0010892B /* ListActionableItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0CF4222023DDCD0010892B /* ListActionableItem.swift */; };
27 | 0A398A3D20229EBE00741F6D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A3C20229EBE00741F6D /* AppDelegate.swift */; };
28 | 0A398A4420229EBE00741F6D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0A398A4320229EBE00741F6D /* Assets.xcassets */; };
29 | 0A398A4720229EBE00741F6D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0A398A4520229EBE00741F6D /* LaunchScreen.storyboard */; };
30 | 0A398A5220229EBF00741F6D /* ToDoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A5120229EBF00741F6D /* ToDoTests.swift */; };
31 | 0A398A5D20229EBF00741F6D /* ToDoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A5C20229EBF00741F6D /* ToDoUITests.swift */; };
32 | 0A398A6D2022A15F00741F6D /* AppComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A6C2022A15F00741F6D /* AppComponent.swift */; };
33 | 0A398A742022A1B700741F6D /* ListRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A702022A1B700741F6D /* ListRouter.swift */; };
34 | 0A398A752022A1B700741F6D /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A712022A1B700741F6D /* ListViewController.swift */; };
35 | 0A398A762022A1B700741F6D /* ListBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A722022A1B700741F6D /* ListBuilder.swift */; };
36 | 0A398A772022A1B700741F6D /* ListInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A732022A1B700741F6D /* ListInteractor.swift */; };
37 | 0A398A7A2022A39D00741F6D /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A792022A39D00741F6D /* Item.swift */; };
38 | 0A398A7D2022A45400741F6D /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A7C2022A45400741F6D /* Date.swift */; };
39 | 0A398A802022A4CD00741F6D /* ToDoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A7F2022A4CD00741F6D /* ToDoService.swift */; };
40 | 0A398A842022A75400741F6D /* LoadingPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A832022A75400741F6D /* LoadingPresentable.swift */; };
41 | 0A398A862022A88A00741F6D /* ItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A852022A88A00741F6D /* ItemViewModel.swift */; };
42 | 0A398A8C2022AD2D00741F6D /* ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A8A2022AD2D00741F6D /* ItemCell.swift */; };
43 | 0A398A8D2022AD2D00741F6D /* ItemCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A398A8B2022AD2D00741F6D /* ItemCell.xib */; };
44 | 0A398A972022C87A00741F6D /* ItemRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A922022C87A00741F6D /* ItemRouter.swift */; };
45 | 0A398A982022C87A00741F6D /* ItemViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A932022C87A00741F6D /* ItemViewController.swift */; };
46 | 0A398A992022C87A00741F6D /* ItemBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A942022C87A00741F6D /* ItemBuilder.swift */; };
47 | 0A398A9A2022C87A00741F6D /* ItemInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A952022C87A00741F6D /* ItemInteractor.swift */; };
48 | 0A398A9B2022C87A00741F6D /* ItemViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A398A962022C87A00741F6D /* ItemViewController.xib */; };
49 | 0A398A9D2022D7DD00741F6D /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A9C2022D7DD00741F6D /* Config.swift */; };
50 | 0A398A9F2022D9A200741F6D /* ListComponent+Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398A9E2022D9A200741F6D /* ListComponent+Item.swift */; };
51 | 0A398AA52023139200741F6D /* RootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AA12023139200741F6D /* RootRouter.swift */; };
52 | 0A398AA62023139200741F6D /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AA22023139200741F6D /* RootViewController.swift */; };
53 | 0A398AA72023139200741F6D /* RootBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AA32023139200741F6D /* RootBuilder.swift */; };
54 | 0A398AA82023139200741F6D /* RootInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AA42023139200741F6D /* RootInteractor.swift */; };
55 | 0A398AAB2023182800741F6D /* RootComponent+LoggedOut.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AA92023182800741F6D /* RootComponent+LoggedOut.swift */; };
56 | 0A398AAC2023182800741F6D /* RootComponent+LoggedIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AAA2023182800741F6D /* RootComponent+LoggedIn.swift */; };
57 | 0A398ABB2023188200741F6D /* LoggedOutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AAE2023188200741F6D /* LoggedOutViewController.swift */; };
58 | 0A398ABC2023188200741F6D /* LoggedOutViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A398AAF2023188200741F6D /* LoggedOutViewController.xib */; };
59 | 0A398ABD2023188200741F6D /* LoggedOutInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AB02023188200741F6D /* LoggedOutInteractor.swift */; };
60 | 0A398ABE2023188200741F6D /* LoggedOutBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AB12023188200741F6D /* LoggedOutBuilder.swift */; };
61 | 0A398ABF2023188200741F6D /* LoggedOutRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AB22023188200741F6D /* LoggedOutRouter.swift */; };
62 | 0A398AC22023188200741F6D /* LoggedInBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AB62023188200741F6D /* LoggedInBuilder.swift */; };
63 | 0A398AC32023188200741F6D /* LoggedInRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AB72023188200741F6D /* LoggedInRouter.swift */; };
64 | 0A398AC42023188200741F6D /* LoggedInInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AB82023188200741F6D /* LoggedInInteractor.swift */; };
65 | 0A398ACC20231A6900741F6D /* DrawerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AC820231A6900741F6D /* DrawerViewController.swift */; };
66 | 0A398ACD20231A6900741F6D /* DrawerInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398AC920231A6900741F6D /* DrawerInteractor.swift */; };
67 | 0A398ACE20231A6900741F6D /* DrawerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398ACA20231A6900741F6D /* DrawerRouter.swift */; };
68 | 0A398ACF20231A6900741F6D /* DrawerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A398ACB20231A6900741F6D /* DrawerBuilder.swift */; };
69 | 0A4DA10C6375B6A71BAA5334 /* Pods_ToDoUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C62C235366A3AA92388A477 /* Pods_ToDoUITests.framework */; };
70 | 53F42407639FAB86D89ECAD5 /* Pods_ToDo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 916A48A5C900CAD5E6DBE4EA /* Pods_ToDo.framework */; };
71 | /* End PBXBuildFile section */
72 |
73 | /* Begin PBXContainerItemProxy section */
74 | 0A398A4E20229EBF00741F6D /* PBXContainerItemProxy */ = {
75 | isa = PBXContainerItemProxy;
76 | containerPortal = 0A398A3120229EBE00741F6D /* Project object */;
77 | proxyType = 1;
78 | remoteGlobalIDString = 0A398A3820229EBE00741F6D;
79 | remoteInfo = ToDo;
80 | };
81 | 0A398A5920229EBF00741F6D /* PBXContainerItemProxy */ = {
82 | isa = PBXContainerItemProxy;
83 | containerPortal = 0A398A3120229EBE00741F6D /* Project object */;
84 | proxyType = 1;
85 | remoteGlobalIDString = 0A398A3820229EBE00741F6D;
86 | remoteInfo = ToDo;
87 | };
88 | /* End PBXContainerItemProxy section */
89 |
90 | /* Begin PBXFileReference section */
91 | 03605E0BDA10357A4A3F9F4D /* Pods-ToDo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ToDo.release.xcconfig"; path = "Pods/Target Support Files/Pods-ToDo/Pods-ToDo.release.xcconfig"; sourceTree = ""; };
92 | 0A0CF40020231EF20010892B /* LoggedInActionableItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInActionableItem.swift; sourceTree = ""; };
93 | 0A0CF40120231EF20010892B /* DrawerActionableItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerActionableItem.swift; sourceTree = ""; };
94 | 0A0CF40220231EF20010892B /* RootActionableItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootActionableItem.swift; sourceTree = ""; };
95 | 0A0CF40520231EF20010892B /* RootWorkflow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootWorkflow.swift; sourceTree = ""; };
96 | 0A0CF40720231EF20010892B /* DeeplinkWorkflow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkWorkflow.swift; sourceTree = ""; };
97 | 0A0CF40D20231FA60010892B /* UINavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UINavigationController.swift; sourceTree = ""; };
98 | 0A0CF40F202321530010892B /* LoggedInComponent+Drawer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LoggedInComponent+Drawer.swift"; sourceTree = ""; };
99 | 0A0CF411202324460010892B /* DrawerComponent+List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DrawerComponent+List.swift"; sourceTree = ""; };
100 | 0A0CF4142023D0940010892B /* MenuRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuRouter.swift; sourceTree = ""; };
101 | 0A0CF4152023D0940010892B /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; };
102 | 0A0CF4162023D0940010892B /* MenuBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBuilder.swift; sourceTree = ""; };
103 | 0A0CF4172023D0940010892B /* MenuInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuInteractor.swift; sourceTree = ""; };
104 | 0A0CF4182023D0940010892B /* MenuViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuViewController.xib; sourceTree = ""; };
105 | 0A0CF41E2023D0B50010892B /* DrawerComponent+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DrawerComponent+Menu.swift"; sourceTree = ""; };
106 | 0A0CF4202023D45B0010892B /* ListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ListViewController.xib; sourceTree = ""; };
107 | 0A0CF4222023DDCD0010892B /* ListActionableItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListActionableItem.swift; sourceTree = ""; };
108 | 0A398A3920229EBE00741F6D /* ToDo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ToDo.app; sourceTree = BUILT_PRODUCTS_DIR; };
109 | 0A398A3C20229EBE00741F6D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
110 | 0A398A4320229EBE00741F6D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
111 | 0A398A4620229EBE00741F6D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
112 | 0A398A4820229EBF00741F6D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
113 | 0A398A4D20229EBF00741F6D /* ToDoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ToDoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
114 | 0A398A5120229EBF00741F6D /* ToDoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoTests.swift; sourceTree = ""; };
115 | 0A398A5320229EBF00741F6D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
116 | 0A398A5820229EBF00741F6D /* ToDoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ToDoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
117 | 0A398A5C20229EBF00741F6D /* ToDoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoUITests.swift; sourceTree = ""; };
118 | 0A398A5E20229EBF00741F6D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
119 | 0A398A6C2022A15F00741F6D /* AppComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppComponent.swift; sourceTree = ""; };
120 | 0A398A702022A1B700741F6D /* ListRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRouter.swift; sourceTree = ""; };
121 | 0A398A712022A1B700741F6D /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; };
122 | 0A398A722022A1B700741F6D /* ListBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBuilder.swift; sourceTree = ""; };
123 | 0A398A732022A1B700741F6D /* ListInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListInteractor.swift; sourceTree = ""; };
124 | 0A398A792022A39D00741F6D /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; };
125 | 0A398A7C2022A45400741F6D /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; };
126 | 0A398A7F2022A4CD00741F6D /* ToDoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoService.swift; sourceTree = ""; };
127 | 0A398A832022A75400741F6D /* LoadingPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingPresentable.swift; sourceTree = ""; };
128 | 0A398A852022A88A00741F6D /* ItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewModel.swift; sourceTree = ""; };
129 | 0A398A8A2022AD2D00741F6D /* ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = ""; };
130 | 0A398A8B2022AD2D00741F6D /* ItemCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemCell.xib; sourceTree = ""; };
131 | 0A398A922022C87A00741F6D /* ItemRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemRouter.swift; sourceTree = ""; };
132 | 0A398A932022C87A00741F6D /* ItemViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewController.swift; sourceTree = ""; };
133 | 0A398A942022C87A00741F6D /* ItemBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemBuilder.swift; sourceTree = ""; };
134 | 0A398A952022C87A00741F6D /* ItemInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemInteractor.swift; sourceTree = ""; };
135 | 0A398A962022C87A00741F6D /* ItemViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemViewController.xib; sourceTree = ""; };
136 | 0A398A9C2022D7DD00741F6D /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; };
137 | 0A398A9E2022D9A200741F6D /* ListComponent+Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListComponent+Item.swift"; sourceTree = ""; };
138 | 0A398AA12023139200741F6D /* RootRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootRouter.swift; sourceTree = ""; };
139 | 0A398AA22023139200741F6D /* RootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; };
140 | 0A398AA32023139200741F6D /* RootBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootBuilder.swift; sourceTree = ""; };
141 | 0A398AA42023139200741F6D /* RootInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootInteractor.swift; sourceTree = ""; };
142 | 0A398AA92023182800741F6D /* RootComponent+LoggedOut.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RootComponent+LoggedOut.swift"; sourceTree = ""; };
143 | 0A398AAA2023182800741F6D /* RootComponent+LoggedIn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RootComponent+LoggedIn.swift"; sourceTree = ""; };
144 | 0A398AAE2023188200741F6D /* LoggedOutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutViewController.swift; sourceTree = ""; };
145 | 0A398AAF2023188200741F6D /* LoggedOutViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoggedOutViewController.xib; sourceTree = ""; };
146 | 0A398AB02023188200741F6D /* LoggedOutInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutInteractor.swift; sourceTree = ""; };
147 | 0A398AB12023188200741F6D /* LoggedOutBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutBuilder.swift; sourceTree = ""; };
148 | 0A398AB22023188200741F6D /* LoggedOutRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutRouter.swift; sourceTree = ""; };
149 | 0A398AB62023188200741F6D /* LoggedInBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInBuilder.swift; sourceTree = ""; };
150 | 0A398AB72023188200741F6D /* LoggedInRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInRouter.swift; sourceTree = ""; };
151 | 0A398AB82023188200741F6D /* LoggedInInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInInteractor.swift; sourceTree = ""; };
152 | 0A398AC820231A6900741F6D /* DrawerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerViewController.swift; sourceTree = ""; };
153 | 0A398AC920231A6900741F6D /* DrawerInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerInteractor.swift; sourceTree = ""; };
154 | 0A398ACA20231A6900741F6D /* DrawerRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerRouter.swift; sourceTree = ""; };
155 | 0A398ACB20231A6900741F6D /* DrawerBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerBuilder.swift; sourceTree = ""; };
156 | 100052882F75295A287FAA47 /* Pods-ToDoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ToDoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ToDoTests/Pods-ToDoTests.debug.xcconfig"; sourceTree = ""; };
157 | 33B0C1192983C4F2C0FD30CF /* Pods-ToDoUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ToDoUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ToDoUITests/Pods-ToDoUITests.debug.xcconfig"; sourceTree = ""; };
158 | 4671FC4DBBB94037D17F2066 /* Pods-ToDoUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ToDoUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ToDoUITests/Pods-ToDoUITests.release.xcconfig"; sourceTree = ""; };
159 | 6C62C235366A3AA92388A477 /* Pods_ToDoUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ToDoUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
160 | 916A48A5C900CAD5E6DBE4EA /* Pods_ToDo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ToDo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
161 | C4E4874F7ED8C2FBCB7BB86E /* Pods_ToDoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ToDoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
162 | CE013D3E86FEFF3D06480E67 /* Pods-ToDo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ToDo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ToDo/Pods-ToDo.debug.xcconfig"; sourceTree = ""; };
163 | E9FA3507A60CD3E496C7878A /* Pods-ToDoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ToDoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ToDoTests/Pods-ToDoTests.release.xcconfig"; sourceTree = ""; };
164 | /* End PBXFileReference section */
165 |
166 | /* Begin PBXFrameworksBuildPhase section */
167 | 0A398A3620229EBE00741F6D /* Frameworks */ = {
168 | isa = PBXFrameworksBuildPhase;
169 | buildActionMask = 2147483647;
170 | files = (
171 | 53F42407639FAB86D89ECAD5 /* Pods_ToDo.framework in Frameworks */,
172 | );
173 | runOnlyForDeploymentPostprocessing = 0;
174 | };
175 | 0A398A4A20229EBF00741F6D /* Frameworks */ = {
176 | isa = PBXFrameworksBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | 054BB18E6B3628A601DCE50B /* Pods_ToDoTests.framework in Frameworks */,
180 | );
181 | runOnlyForDeploymentPostprocessing = 0;
182 | };
183 | 0A398A5520229EBF00741F6D /* Frameworks */ = {
184 | isa = PBXFrameworksBuildPhase;
185 | buildActionMask = 2147483647;
186 | files = (
187 | 0A4DA10C6375B6A71BAA5334 /* Pods_ToDoUITests.framework in Frameworks */,
188 | );
189 | runOnlyForDeploymentPostprocessing = 0;
190 | };
191 | /* End PBXFrameworksBuildPhase section */
192 |
193 | /* Begin PBXGroup section */
194 | 0A0CF3FF20231EF20010892B /* ActionableItems */ = {
195 | isa = PBXGroup;
196 | children = (
197 | 0A0CF40020231EF20010892B /* LoggedInActionableItem.swift */,
198 | 0A0CF40120231EF20010892B /* DrawerActionableItem.swift */,
199 | 0A0CF4222023DDCD0010892B /* ListActionableItem.swift */,
200 | 0A0CF40220231EF20010892B /* RootActionableItem.swift */,
201 | );
202 | path = ActionableItems;
203 | sourceTree = "";
204 | };
205 | 0A0CF40320231EF20010892B /* Workflows */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 0A0CF40420231EF20010892B /* RootWorkflow */,
209 | 0A0CF40620231EF20010892B /* DeeplinkWorkflow */,
210 | );
211 | path = Workflows;
212 | sourceTree = "";
213 | };
214 | 0A0CF40420231EF20010892B /* RootWorkflow */ = {
215 | isa = PBXGroup;
216 | children = (
217 | 0A0CF40520231EF20010892B /* RootWorkflow.swift */,
218 | );
219 | path = RootWorkflow;
220 | sourceTree = "";
221 | };
222 | 0A0CF40620231EF20010892B /* DeeplinkWorkflow */ = {
223 | isa = PBXGroup;
224 | children = (
225 | 0A0CF40720231EF20010892B /* DeeplinkWorkflow.swift */,
226 | );
227 | path = DeeplinkWorkflow;
228 | sourceTree = "";
229 | };
230 | 0A0CF4132023D0860010892B /* Menu */ = {
231 | isa = PBXGroup;
232 | children = (
233 | 0A0CF4162023D0940010892B /* MenuBuilder.swift */,
234 | 0A0CF4142023D0940010892B /* MenuRouter.swift */,
235 | 0A0CF4172023D0940010892B /* MenuInteractor.swift */,
236 | 0A0CF4152023D0940010892B /* MenuViewController.swift */,
237 | 0A0CF4182023D0940010892B /* MenuViewController.xib */,
238 | );
239 | path = Menu;
240 | sourceTree = "";
241 | };
242 | 0A398A3020229EBE00741F6D = {
243 | isa = PBXGroup;
244 | children = (
245 | 0A398A3B20229EBE00741F6D /* ToDo */,
246 | 0A398A5020229EBF00741F6D /* ToDoTests */,
247 | 0A398A5B20229EBF00741F6D /* ToDoUITests */,
248 | 0A398A3A20229EBE00741F6D /* Products */,
249 | 18A4E32003849CFD79BE49E0 /* Pods */,
250 | B5046D93DB3F818116951289 /* Frameworks */,
251 | );
252 | sourceTree = "";
253 | };
254 | 0A398A3A20229EBE00741F6D /* Products */ = {
255 | isa = PBXGroup;
256 | children = (
257 | 0A398A3920229EBE00741F6D /* ToDo.app */,
258 | 0A398A4D20229EBF00741F6D /* ToDoTests.xctest */,
259 | 0A398A5820229EBF00741F6D /* ToDoUITests.xctest */,
260 | );
261 | name = Products;
262 | sourceTree = "";
263 | };
264 | 0A398A3B20229EBE00741F6D /* ToDo */ = {
265 | isa = PBXGroup;
266 | children = (
267 | 0A398A6B20229EDD00741F6D /* App */,
268 | 0A398A6E2022A18100741F6D /* RIBs */,
269 | 0A398A892022AD0E00741F6D /* Views */,
270 | 0A398A782022A38B00741F6D /* Models */,
271 | 0A398A7E2022A4B100741F6D /* Services */,
272 | 0A398A7B2022A44500741F6D /* Utils */,
273 | 0A398A6A20229EC800741F6D /* Resources */,
274 | );
275 | path = ToDo;
276 | sourceTree = "";
277 | };
278 | 0A398A5020229EBF00741F6D /* ToDoTests */ = {
279 | isa = PBXGroup;
280 | children = (
281 | 0A398A5120229EBF00741F6D /* ToDoTests.swift */,
282 | 0A398A5320229EBF00741F6D /* Info.plist */,
283 | );
284 | path = ToDoTests;
285 | sourceTree = "";
286 | };
287 | 0A398A5B20229EBF00741F6D /* ToDoUITests */ = {
288 | isa = PBXGroup;
289 | children = (
290 | 0A398A5C20229EBF00741F6D /* ToDoUITests.swift */,
291 | 0A398A5E20229EBF00741F6D /* Info.plist */,
292 | );
293 | path = ToDoUITests;
294 | sourceTree = "";
295 | };
296 | 0A398A6A20229EC800741F6D /* Resources */ = {
297 | isa = PBXGroup;
298 | children = (
299 | 0A398A4320229EBE00741F6D /* Assets.xcassets */,
300 | 0A398A4520229EBE00741F6D /* LaunchScreen.storyboard */,
301 | 0A398A4820229EBF00741F6D /* Info.plist */,
302 | );
303 | path = Resources;
304 | sourceTree = "";
305 | };
306 | 0A398A6B20229EDD00741F6D /* App */ = {
307 | isa = PBXGroup;
308 | children = (
309 | 0A398A3C20229EBE00741F6D /* AppDelegate.swift */,
310 | 0A398A6C2022A15F00741F6D /* AppComponent.swift */,
311 | );
312 | path = App;
313 | sourceTree = "";
314 | };
315 | 0A398A6E2022A18100741F6D /* RIBs */ = {
316 | isa = PBXGroup;
317 | children = (
318 | 0A398A822022A74300741F6D /* Common */,
319 | 0A398AA02023137100741F6D /* Root */,
320 | 0A398AB32023188200741F6D /* LoggedIn */,
321 | 0A398AAD2023188200741F6D /* LoggedOut */,
322 | 0A398AC720231A6900741F6D /* Drawer */,
323 | 0A0CF4132023D0860010892B /* Menu */,
324 | 0A398A6F2022A18900741F6D /* List */,
325 | 0A398A912022C86600741F6D /* Item */,
326 | 0A0CF3FF20231EF20010892B /* ActionableItems */,
327 | 0A0CF40320231EF20010892B /* Workflows */,
328 | );
329 | path = RIBs;
330 | sourceTree = "";
331 | };
332 | 0A398A6F2022A18900741F6D /* List */ = {
333 | isa = PBXGroup;
334 | children = (
335 | 0A398A722022A1B700741F6D /* ListBuilder.swift */,
336 | 0A398A702022A1B700741F6D /* ListRouter.swift */,
337 | 0A398A732022A1B700741F6D /* ListInteractor.swift */,
338 | 0A398A712022A1B700741F6D /* ListViewController.swift */,
339 | 0A0CF4202023D45B0010892B /* ListViewController.xib */,
340 | 0A398A9E2022D9A200741F6D /* ListComponent+Item.swift */,
341 | );
342 | path = List;
343 | sourceTree = "";
344 | };
345 | 0A398A782022A38B00741F6D /* Models */ = {
346 | isa = PBXGroup;
347 | children = (
348 | 0A398A792022A39D00741F6D /* Item.swift */,
349 | 0A398A852022A88A00741F6D /* ItemViewModel.swift */,
350 | 0A398A9C2022D7DD00741F6D /* Config.swift */,
351 | );
352 | path = Models;
353 | sourceTree = "";
354 | };
355 | 0A398A7B2022A44500741F6D /* Utils */ = {
356 | isa = PBXGroup;
357 | children = (
358 | 0A398A7C2022A45400741F6D /* Date.swift */,
359 | );
360 | path = Utils;
361 | sourceTree = "";
362 | };
363 | 0A398A7E2022A4B100741F6D /* Services */ = {
364 | isa = PBXGroup;
365 | children = (
366 | 0A398A7F2022A4CD00741F6D /* ToDoService.swift */,
367 | );
368 | path = Services;
369 | sourceTree = "";
370 | };
371 | 0A398A822022A74300741F6D /* Common */ = {
372 | isa = PBXGroup;
373 | children = (
374 | 0A398A832022A75400741F6D /* LoadingPresentable.swift */,
375 | 0A0CF40D20231FA60010892B /* UINavigationController.swift */,
376 | );
377 | path = Common;
378 | sourceTree = "";
379 | };
380 | 0A398A892022AD0E00741F6D /* Views */ = {
381 | isa = PBXGroup;
382 | children = (
383 | 0A398A8A2022AD2D00741F6D /* ItemCell.swift */,
384 | 0A398A8B2022AD2D00741F6D /* ItemCell.xib */,
385 | );
386 | path = Views;
387 | sourceTree = "";
388 | };
389 | 0A398A912022C86600741F6D /* Item */ = {
390 | isa = PBXGroup;
391 | children = (
392 | 0A398A942022C87A00741F6D /* ItemBuilder.swift */,
393 | 0A398A922022C87A00741F6D /* ItemRouter.swift */,
394 | 0A398A952022C87A00741F6D /* ItemInteractor.swift */,
395 | 0A398A932022C87A00741F6D /* ItemViewController.swift */,
396 | 0A398A962022C87A00741F6D /* ItemViewController.xib */,
397 | );
398 | path = Item;
399 | sourceTree = "";
400 | };
401 | 0A398AA02023137100741F6D /* Root */ = {
402 | isa = PBXGroup;
403 | children = (
404 | 0A398AA32023139200741F6D /* RootBuilder.swift */,
405 | 0A398AA12023139200741F6D /* RootRouter.swift */,
406 | 0A398AA42023139200741F6D /* RootInteractor.swift */,
407 | 0A398AA22023139200741F6D /* RootViewController.swift */,
408 | 0A398AAA2023182800741F6D /* RootComponent+LoggedIn.swift */,
409 | 0A398AA92023182800741F6D /* RootComponent+LoggedOut.swift */,
410 | );
411 | path = Root;
412 | sourceTree = "";
413 | };
414 | 0A398AAD2023188200741F6D /* LoggedOut */ = {
415 | isa = PBXGroup;
416 | children = (
417 | 0A398AB12023188200741F6D /* LoggedOutBuilder.swift */,
418 | 0A398AB22023188200741F6D /* LoggedOutRouter.swift */,
419 | 0A398AB02023188200741F6D /* LoggedOutInteractor.swift */,
420 | 0A398AAE2023188200741F6D /* LoggedOutViewController.swift */,
421 | 0A398AAF2023188200741F6D /* LoggedOutViewController.xib */,
422 | );
423 | path = LoggedOut;
424 | sourceTree = "";
425 | };
426 | 0A398AB32023188200741F6D /* LoggedIn */ = {
427 | isa = PBXGroup;
428 | children = (
429 | 0A398AB62023188200741F6D /* LoggedInBuilder.swift */,
430 | 0A398AB72023188200741F6D /* LoggedInRouter.swift */,
431 | 0A398AB82023188200741F6D /* LoggedInInteractor.swift */,
432 | 0A0CF40F202321530010892B /* LoggedInComponent+Drawer.swift */,
433 | );
434 | path = LoggedIn;
435 | sourceTree = "";
436 | };
437 | 0A398AC720231A6900741F6D /* Drawer */ = {
438 | isa = PBXGroup;
439 | children = (
440 | 0A398ACB20231A6900741F6D /* DrawerBuilder.swift */,
441 | 0A398ACA20231A6900741F6D /* DrawerRouter.swift */,
442 | 0A398AC920231A6900741F6D /* DrawerInteractor.swift */,
443 | 0A398AC820231A6900741F6D /* DrawerViewController.swift */,
444 | 0A0CF411202324460010892B /* DrawerComponent+List.swift */,
445 | 0A0CF41E2023D0B50010892B /* DrawerComponent+Menu.swift */,
446 | );
447 | path = Drawer;
448 | sourceTree = "";
449 | };
450 | 18A4E32003849CFD79BE49E0 /* Pods */ = {
451 | isa = PBXGroup;
452 | children = (
453 | CE013D3E86FEFF3D06480E67 /* Pods-ToDo.debug.xcconfig */,
454 | 03605E0BDA10357A4A3F9F4D /* Pods-ToDo.release.xcconfig */,
455 | 100052882F75295A287FAA47 /* Pods-ToDoTests.debug.xcconfig */,
456 | E9FA3507A60CD3E496C7878A /* Pods-ToDoTests.release.xcconfig */,
457 | 33B0C1192983C4F2C0FD30CF /* Pods-ToDoUITests.debug.xcconfig */,
458 | 4671FC4DBBB94037D17F2066 /* Pods-ToDoUITests.release.xcconfig */,
459 | );
460 | name = Pods;
461 | sourceTree = "";
462 | };
463 | B5046D93DB3F818116951289 /* Frameworks */ = {
464 | isa = PBXGroup;
465 | children = (
466 | 916A48A5C900CAD5E6DBE4EA /* Pods_ToDo.framework */,
467 | C4E4874F7ED8C2FBCB7BB86E /* Pods_ToDoTests.framework */,
468 | 6C62C235366A3AA92388A477 /* Pods_ToDoUITests.framework */,
469 | );
470 | name = Frameworks;
471 | sourceTree = "";
472 | };
473 | /* End PBXGroup section */
474 |
475 | /* Begin PBXNativeTarget section */
476 | 0A398A3820229EBE00741F6D /* ToDo */ = {
477 | isa = PBXNativeTarget;
478 | buildConfigurationList = 0A398A6120229EBF00741F6D /* Build configuration list for PBXNativeTarget "ToDo" */;
479 | buildPhases = (
480 | 757A4E7E56EA4FB0AA9CC45A /* [CP] Check Pods Manifest.lock */,
481 | 0A398A3520229EBE00741F6D /* Sources */,
482 | 0A398A3620229EBE00741F6D /* Frameworks */,
483 | 0A398A3720229EBE00741F6D /* Resources */,
484 | 44F2F90FCE6F87CC29EA0CC4 /* [CP] Embed Pods Frameworks */,
485 | );
486 | buildRules = (
487 | );
488 | dependencies = (
489 | );
490 | name = ToDo;
491 | productName = ToDo;
492 | productReference = 0A398A3920229EBE00741F6D /* ToDo.app */;
493 | productType = "com.apple.product-type.application";
494 | };
495 | 0A398A4C20229EBF00741F6D /* ToDoTests */ = {
496 | isa = PBXNativeTarget;
497 | buildConfigurationList = 0A398A6420229EBF00741F6D /* Build configuration list for PBXNativeTarget "ToDoTests" */;
498 | buildPhases = (
499 | E1602153A3D8468ED61169AB /* [CP] Check Pods Manifest.lock */,
500 | 0A398A4920229EBF00741F6D /* Sources */,
501 | 0A398A4A20229EBF00741F6D /* Frameworks */,
502 | 0A398A4B20229EBF00741F6D /* Resources */,
503 | );
504 | buildRules = (
505 | );
506 | dependencies = (
507 | 0A398A4F20229EBF00741F6D /* PBXTargetDependency */,
508 | );
509 | name = ToDoTests;
510 | productName = ToDoTests;
511 | productReference = 0A398A4D20229EBF00741F6D /* ToDoTests.xctest */;
512 | productType = "com.apple.product-type.bundle.unit-test";
513 | };
514 | 0A398A5720229EBF00741F6D /* ToDoUITests */ = {
515 | isa = PBXNativeTarget;
516 | buildConfigurationList = 0A398A6720229EBF00741F6D /* Build configuration list for PBXNativeTarget "ToDoUITests" */;
517 | buildPhases = (
518 | 39261D78BCE753E58A5015E2 /* [CP] Check Pods Manifest.lock */,
519 | 0A398A5420229EBF00741F6D /* Sources */,
520 | 0A398A5520229EBF00741F6D /* Frameworks */,
521 | 0A398A5620229EBF00741F6D /* Resources */,
522 | );
523 | buildRules = (
524 | );
525 | dependencies = (
526 | 0A398A5A20229EBF00741F6D /* PBXTargetDependency */,
527 | );
528 | name = ToDoUITests;
529 | productName = ToDoUITests;
530 | productReference = 0A398A5820229EBF00741F6D /* ToDoUITests.xctest */;
531 | productType = "com.apple.product-type.bundle.ui-testing";
532 | };
533 | /* End PBXNativeTarget section */
534 |
535 | /* Begin PBXProject section */
536 | 0A398A3120229EBE00741F6D /* Project object */ = {
537 | isa = PBXProject;
538 | attributes = {
539 | LastSwiftUpdateCheck = 0920;
540 | LastUpgradeCheck = 1010;
541 | ORGANIZATIONNAME = Dev4Jam;
542 | TargetAttributes = {
543 | 0A398A3820229EBE00741F6D = {
544 | CreatedOnToolsVersion = 9.2;
545 | ProvisioningStyle = Automatic;
546 | };
547 | 0A398A4C20229EBF00741F6D = {
548 | CreatedOnToolsVersion = 9.2;
549 | LastSwiftMigration = 1010;
550 | ProvisioningStyle = Automatic;
551 | TestTargetID = 0A398A3820229EBE00741F6D;
552 | };
553 | 0A398A5720229EBF00741F6D = {
554 | CreatedOnToolsVersion = 9.2;
555 | LastSwiftMigration = 1010;
556 | ProvisioningStyle = Automatic;
557 | TestTargetID = 0A398A3820229EBE00741F6D;
558 | };
559 | };
560 | };
561 | buildConfigurationList = 0A398A3420229EBE00741F6D /* Build configuration list for PBXProject "ToDo" */;
562 | compatibilityVersion = "Xcode 8.0";
563 | developmentRegion = en;
564 | hasScannedForEncodings = 0;
565 | knownRegions = (
566 | en,
567 | Base,
568 | );
569 | mainGroup = 0A398A3020229EBE00741F6D;
570 | productRefGroup = 0A398A3A20229EBE00741F6D /* Products */;
571 | projectDirPath = "";
572 | projectRoot = "";
573 | targets = (
574 | 0A398A3820229EBE00741F6D /* ToDo */,
575 | 0A398A4C20229EBF00741F6D /* ToDoTests */,
576 | 0A398A5720229EBF00741F6D /* ToDoUITests */,
577 | );
578 | };
579 | /* End PBXProject section */
580 |
581 | /* Begin PBXResourcesBuildPhase section */
582 | 0A398A3720229EBE00741F6D /* Resources */ = {
583 | isa = PBXResourcesBuildPhase;
584 | buildActionMask = 2147483647;
585 | files = (
586 | 0A398ABC2023188200741F6D /* LoggedOutViewController.xib in Resources */,
587 | 0A0CF41D2023D0940010892B /* MenuViewController.xib in Resources */,
588 | 0A0CF4212023D45B0010892B /* ListViewController.xib in Resources */,
589 | 0A398A4720229EBE00741F6D /* LaunchScreen.storyboard in Resources */,
590 | 0A398A9B2022C87A00741F6D /* ItemViewController.xib in Resources */,
591 | 0A398A4420229EBE00741F6D /* Assets.xcassets in Resources */,
592 | 0A398A8D2022AD2D00741F6D /* ItemCell.xib in Resources */,
593 | );
594 | runOnlyForDeploymentPostprocessing = 0;
595 | };
596 | 0A398A4B20229EBF00741F6D /* Resources */ = {
597 | isa = PBXResourcesBuildPhase;
598 | buildActionMask = 2147483647;
599 | files = (
600 | );
601 | runOnlyForDeploymentPostprocessing = 0;
602 | };
603 | 0A398A5620229EBF00741F6D /* Resources */ = {
604 | isa = PBXResourcesBuildPhase;
605 | buildActionMask = 2147483647;
606 | files = (
607 | );
608 | runOnlyForDeploymentPostprocessing = 0;
609 | };
610 | /* End PBXResourcesBuildPhase section */
611 |
612 | /* Begin PBXShellScriptBuildPhase section */
613 | 39261D78BCE753E58A5015E2 /* [CP] Check Pods Manifest.lock */ = {
614 | isa = PBXShellScriptBuildPhase;
615 | buildActionMask = 2147483647;
616 | files = (
617 | );
618 | inputPaths = (
619 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
620 | "${PODS_ROOT}/Manifest.lock",
621 | );
622 | name = "[CP] Check Pods Manifest.lock";
623 | outputPaths = (
624 | "$(DERIVED_FILE_DIR)/Pods-ToDoUITests-checkManifestLockResult.txt",
625 | );
626 | runOnlyForDeploymentPostprocessing = 0;
627 | shellPath = /bin/sh;
628 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
629 | showEnvVarsInLog = 0;
630 | };
631 | 44F2F90FCE6F87CC29EA0CC4 /* [CP] Embed Pods Frameworks */ = {
632 | isa = PBXShellScriptBuildPhase;
633 | buildActionMask = 2147483647;
634 | files = (
635 | );
636 | inputPaths = (
637 | "${PODS_ROOT}/Target Support Files/Pods-ToDo/Pods-ToDo-frameworks.sh",
638 | "${BUILT_PRODUCTS_DIR}/DynamicButton/DynamicButton.framework",
639 | "${BUILT_PRODUCTS_DIR}/Eureka/Eureka.framework",
640 | "${BUILT_PRODUCTS_DIR}/IGListDiffKit/IGListDiffKit.framework",
641 | "${BUILT_PRODUCTS_DIR}/IGListKit/IGListKit.framework",
642 | "${BUILT_PRODUCTS_DIR}/REFrostedViewController/REFrostedViewController.framework",
643 | "${BUILT_PRODUCTS_DIR}/RIBs/RIBs.framework",
644 | "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework",
645 | "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework",
646 | "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
647 | "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
648 | "${BUILT_PRODUCTS_DIR}/SVProgressHUD/SVProgressHUD.framework",
649 | "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
650 | "${BUILT_PRODUCTS_DIR}/TransitionButton/TransitionButton.framework",
651 | );
652 | name = "[CP] Embed Pods Frameworks";
653 | outputPaths = (
654 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DynamicButton.framework",
655 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Eureka.framework",
656 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IGListDiffKit.framework",
657 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IGListKit.framework",
658 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/REFrostedViewController.framework",
659 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RIBs.framework",
660 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework",
661 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework",
662 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
663 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
664 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SVProgressHUD.framework",
665 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
666 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TransitionButton.framework",
667 | );
668 | runOnlyForDeploymentPostprocessing = 0;
669 | shellPath = /bin/sh;
670 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ToDo/Pods-ToDo-frameworks.sh\"\n";
671 | showEnvVarsInLog = 0;
672 | };
673 | 757A4E7E56EA4FB0AA9CC45A /* [CP] Check Pods Manifest.lock */ = {
674 | isa = PBXShellScriptBuildPhase;
675 | buildActionMask = 2147483647;
676 | files = (
677 | );
678 | inputPaths = (
679 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
680 | "${PODS_ROOT}/Manifest.lock",
681 | );
682 | name = "[CP] Check Pods Manifest.lock";
683 | outputPaths = (
684 | "$(DERIVED_FILE_DIR)/Pods-ToDo-checkManifestLockResult.txt",
685 | );
686 | runOnlyForDeploymentPostprocessing = 0;
687 | shellPath = /bin/sh;
688 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
689 | showEnvVarsInLog = 0;
690 | };
691 | E1602153A3D8468ED61169AB /* [CP] Check Pods Manifest.lock */ = {
692 | isa = PBXShellScriptBuildPhase;
693 | buildActionMask = 2147483647;
694 | files = (
695 | );
696 | inputPaths = (
697 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
698 | "${PODS_ROOT}/Manifest.lock",
699 | );
700 | name = "[CP] Check Pods Manifest.lock";
701 | outputPaths = (
702 | "$(DERIVED_FILE_DIR)/Pods-ToDoTests-checkManifestLockResult.txt",
703 | );
704 | runOnlyForDeploymentPostprocessing = 0;
705 | shellPath = /bin/sh;
706 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
707 | showEnvVarsInLog = 0;
708 | };
709 | /* End PBXShellScriptBuildPhase section */
710 |
711 | /* Begin PBXSourcesBuildPhase section */
712 | 0A398A3520229EBE00741F6D /* Sources */ = {
713 | isa = PBXSourcesBuildPhase;
714 | buildActionMask = 2147483647;
715 | files = (
716 | 0A398A9F2022D9A200741F6D /* ListComponent+Item.swift in Sources */,
717 | 0A398ABD2023188200741F6D /* LoggedOutInteractor.swift in Sources */,
718 | 0A398ACF20231A6900741F6D /* DrawerBuilder.swift in Sources */,
719 | 0A0CF410202321530010892B /* LoggedInComponent+Drawer.swift in Sources */,
720 | 0A398A802022A4CD00741F6D /* ToDoService.swift in Sources */,
721 | 0A398AA82023139200741F6D /* RootInteractor.swift in Sources */,
722 | 0A398AA52023139200741F6D /* RootRouter.swift in Sources */,
723 | 0A398AC22023188200741F6D /* LoggedInBuilder.swift in Sources */,
724 | 0A0CF4232023DDCD0010892B /* ListActionableItem.swift in Sources */,
725 | 0A398A772022A1B700741F6D /* ListInteractor.swift in Sources */,
726 | 0A0CF40C20231EF20010892B /* DeeplinkWorkflow.swift in Sources */,
727 | 0A398A742022A1B700741F6D /* ListRouter.swift in Sources */,
728 | 0A0CF40820231EF20010892B /* LoggedInActionableItem.swift in Sources */,
729 | 0A398A862022A88A00741F6D /* ItemViewModel.swift in Sources */,
730 | 0A0CF41F2023D0B50010892B /* DrawerComponent+Menu.swift in Sources */,
731 | 0A398A9D2022D7DD00741F6D /* Config.swift in Sources */,
732 | 0A398A762022A1B700741F6D /* ListBuilder.swift in Sources */,
733 | 0A398ABB2023188200741F6D /* LoggedOutViewController.swift in Sources */,
734 | 0A398ABF2023188200741F6D /* LoggedOutRouter.swift in Sources */,
735 | 0A398AA72023139200741F6D /* RootBuilder.swift in Sources */,
736 | 0A0CF41C2023D0940010892B /* MenuInteractor.swift in Sources */,
737 | 0A398AAC2023182800741F6D /* RootComponent+LoggedIn.swift in Sources */,
738 | 0A398A842022A75400741F6D /* LoadingPresentable.swift in Sources */,
739 | 0A398A7D2022A45400741F6D /* Date.swift in Sources */,
740 | 0A398AA62023139200741F6D /* RootViewController.swift in Sources */,
741 | 0A398A982022C87A00741F6D /* ItemViewController.swift in Sources */,
742 | 0A398ABE2023188200741F6D /* LoggedOutBuilder.swift in Sources */,
743 | 0A398AAB2023182800741F6D /* RootComponent+LoggedOut.swift in Sources */,
744 | 0A398ACE20231A6900741F6D /* DrawerRouter.swift in Sources */,
745 | 0A398A3D20229EBE00741F6D /* AppDelegate.swift in Sources */,
746 | 0A0CF40920231EF20010892B /* DrawerActionableItem.swift in Sources */,
747 | 0A398A9A2022C87A00741F6D /* ItemInteractor.swift in Sources */,
748 | 0A398AC42023188200741F6D /* LoggedInInteractor.swift in Sources */,
749 | 0A0CF40A20231EF20010892B /* RootActionableItem.swift in Sources */,
750 | 0A0CF41A2023D0940010892B /* MenuViewController.swift in Sources */,
751 | 0A0CF40E20231FA60010892B /* UINavigationController.swift in Sources */,
752 | 0A398AC32023188200741F6D /* LoggedInRouter.swift in Sources */,
753 | 0A398ACC20231A6900741F6D /* DrawerViewController.swift in Sources */,
754 | 0A398A8C2022AD2D00741F6D /* ItemCell.swift in Sources */,
755 | 0A0CF41B2023D0940010892B /* MenuBuilder.swift in Sources */,
756 | 0A398A7A2022A39D00741F6D /* Item.swift in Sources */,
757 | 0A398A752022A1B700741F6D /* ListViewController.swift in Sources */,
758 | 0A398A992022C87A00741F6D /* ItemBuilder.swift in Sources */,
759 | 0A0CF40B20231EF20010892B /* RootWorkflow.swift in Sources */,
760 | 0A0CF412202324460010892B /* DrawerComponent+List.swift in Sources */,
761 | 0A398A6D2022A15F00741F6D /* AppComponent.swift in Sources */,
762 | 0A0CF4192023D0940010892B /* MenuRouter.swift in Sources */,
763 | 0A398ACD20231A6900741F6D /* DrawerInteractor.swift in Sources */,
764 | 0A398A972022C87A00741F6D /* ItemRouter.swift in Sources */,
765 | );
766 | runOnlyForDeploymentPostprocessing = 0;
767 | };
768 | 0A398A4920229EBF00741F6D /* Sources */ = {
769 | isa = PBXSourcesBuildPhase;
770 | buildActionMask = 2147483647;
771 | files = (
772 | 0A398A5220229EBF00741F6D /* ToDoTests.swift in Sources */,
773 | );
774 | runOnlyForDeploymentPostprocessing = 0;
775 | };
776 | 0A398A5420229EBF00741F6D /* Sources */ = {
777 | isa = PBXSourcesBuildPhase;
778 | buildActionMask = 2147483647;
779 | files = (
780 | 0A398A5D20229EBF00741F6D /* ToDoUITests.swift in Sources */,
781 | );
782 | runOnlyForDeploymentPostprocessing = 0;
783 | };
784 | /* End PBXSourcesBuildPhase section */
785 |
786 | /* Begin PBXTargetDependency section */
787 | 0A398A4F20229EBF00741F6D /* PBXTargetDependency */ = {
788 | isa = PBXTargetDependency;
789 | target = 0A398A3820229EBE00741F6D /* ToDo */;
790 | targetProxy = 0A398A4E20229EBF00741F6D /* PBXContainerItemProxy */;
791 | };
792 | 0A398A5A20229EBF00741F6D /* PBXTargetDependency */ = {
793 | isa = PBXTargetDependency;
794 | target = 0A398A3820229EBE00741F6D /* ToDo */;
795 | targetProxy = 0A398A5920229EBF00741F6D /* PBXContainerItemProxy */;
796 | };
797 | /* End PBXTargetDependency section */
798 |
799 | /* Begin PBXVariantGroup section */
800 | 0A398A4520229EBE00741F6D /* LaunchScreen.storyboard */ = {
801 | isa = PBXVariantGroup;
802 | children = (
803 | 0A398A4620229EBE00741F6D /* Base */,
804 | );
805 | name = LaunchScreen.storyboard;
806 | sourceTree = "";
807 | };
808 | /* End PBXVariantGroup section */
809 |
810 | /* Begin XCBuildConfiguration section */
811 | 0A398A5F20229EBF00741F6D /* Debug */ = {
812 | isa = XCBuildConfiguration;
813 | buildSettings = {
814 | ALWAYS_SEARCH_USER_PATHS = NO;
815 | CLANG_ANALYZER_NONNULL = YES;
816 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
817 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
818 | CLANG_CXX_LIBRARY = "libc++";
819 | CLANG_ENABLE_MODULES = YES;
820 | CLANG_ENABLE_OBJC_ARC = YES;
821 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
822 | CLANG_WARN_BOOL_CONVERSION = YES;
823 | CLANG_WARN_COMMA = YES;
824 | CLANG_WARN_CONSTANT_CONVERSION = YES;
825 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
826 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
827 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
828 | CLANG_WARN_EMPTY_BODY = YES;
829 | CLANG_WARN_ENUM_CONVERSION = YES;
830 | CLANG_WARN_INFINITE_RECURSION = YES;
831 | CLANG_WARN_INT_CONVERSION = YES;
832 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
833 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
834 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
835 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
836 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
837 | CLANG_WARN_STRICT_PROTOTYPES = YES;
838 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
839 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
840 | CLANG_WARN_UNREACHABLE_CODE = YES;
841 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
842 | CODE_SIGN_IDENTITY = "iPhone Developer";
843 | COPY_PHASE_STRIP = NO;
844 | DEBUG_INFORMATION_FORMAT = dwarf;
845 | ENABLE_STRICT_OBJC_MSGSEND = YES;
846 | ENABLE_TESTABILITY = YES;
847 | GCC_C_LANGUAGE_STANDARD = gnu11;
848 | GCC_DYNAMIC_NO_PIC = NO;
849 | GCC_NO_COMMON_BLOCKS = YES;
850 | GCC_OPTIMIZATION_LEVEL = 0;
851 | GCC_PREPROCESSOR_DEFINITIONS = (
852 | "DEBUG=1",
853 | "$(inherited)",
854 | );
855 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
856 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
857 | GCC_WARN_UNDECLARED_SELECTOR = YES;
858 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
859 | GCC_WARN_UNUSED_FUNCTION = YES;
860 | GCC_WARN_UNUSED_VARIABLE = YES;
861 | IPHONEOS_DEPLOYMENT_TARGET = 11.4;
862 | MTL_ENABLE_DEBUG_INFO = YES;
863 | ONLY_ACTIVE_ARCH = YES;
864 | SDKROOT = iphoneos;
865 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
866 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
867 | SWIFT_VERSION = 4.2;
868 | };
869 | name = Debug;
870 | };
871 | 0A398A6020229EBF00741F6D /* Release */ = {
872 | isa = XCBuildConfiguration;
873 | buildSettings = {
874 | ALWAYS_SEARCH_USER_PATHS = NO;
875 | CLANG_ANALYZER_NONNULL = YES;
876 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
877 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
878 | CLANG_CXX_LIBRARY = "libc++";
879 | CLANG_ENABLE_MODULES = YES;
880 | CLANG_ENABLE_OBJC_ARC = YES;
881 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
882 | CLANG_WARN_BOOL_CONVERSION = YES;
883 | CLANG_WARN_COMMA = YES;
884 | CLANG_WARN_CONSTANT_CONVERSION = YES;
885 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
886 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
887 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
888 | CLANG_WARN_EMPTY_BODY = YES;
889 | CLANG_WARN_ENUM_CONVERSION = YES;
890 | CLANG_WARN_INFINITE_RECURSION = YES;
891 | CLANG_WARN_INT_CONVERSION = YES;
892 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
893 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
894 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
895 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
896 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
897 | CLANG_WARN_STRICT_PROTOTYPES = YES;
898 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
899 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
900 | CLANG_WARN_UNREACHABLE_CODE = YES;
901 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
902 | CODE_SIGN_IDENTITY = "iPhone Developer";
903 | COPY_PHASE_STRIP = NO;
904 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
905 | ENABLE_NS_ASSERTIONS = NO;
906 | ENABLE_STRICT_OBJC_MSGSEND = YES;
907 | GCC_C_LANGUAGE_STANDARD = gnu11;
908 | GCC_NO_COMMON_BLOCKS = YES;
909 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
910 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
911 | GCC_WARN_UNDECLARED_SELECTOR = YES;
912 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
913 | GCC_WARN_UNUSED_FUNCTION = YES;
914 | GCC_WARN_UNUSED_VARIABLE = YES;
915 | IPHONEOS_DEPLOYMENT_TARGET = 11.4;
916 | MTL_ENABLE_DEBUG_INFO = NO;
917 | SDKROOT = iphoneos;
918 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
919 | SWIFT_VERSION = 4.2;
920 | VALIDATE_PRODUCT = YES;
921 | };
922 | name = Release;
923 | };
924 | 0A398A6220229EBF00741F6D /* Debug */ = {
925 | isa = XCBuildConfiguration;
926 | baseConfigurationReference = CE013D3E86FEFF3D06480E67 /* Pods-ToDo.debug.xcconfig */;
927 | buildSettings = {
928 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
929 | CODE_SIGN_STYLE = Automatic;
930 | INFOPLIST_FILE = "$(SRCROOT)/ToDo/Resources/Info.plist";
931 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
932 | PRODUCT_BUNDLE_IDENTIFIER = "au.com.dev4jam-todo-ios";
933 | PRODUCT_NAME = "$(TARGET_NAME)";
934 | SWIFT_VERSION = 5.0;
935 | TARGETED_DEVICE_FAMILY = "1,2";
936 | };
937 | name = Debug;
938 | };
939 | 0A398A6320229EBF00741F6D /* Release */ = {
940 | isa = XCBuildConfiguration;
941 | baseConfigurationReference = 03605E0BDA10357A4A3F9F4D /* Pods-ToDo.release.xcconfig */;
942 | buildSettings = {
943 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
944 | CODE_SIGN_STYLE = Automatic;
945 | INFOPLIST_FILE = "$(SRCROOT)/ToDo/Resources/Info.plist";
946 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
947 | PRODUCT_BUNDLE_IDENTIFIER = "au.com.dev4jam-todo-ios";
948 | PRODUCT_NAME = "$(TARGET_NAME)";
949 | SWIFT_VERSION = 5.0;
950 | TARGETED_DEVICE_FAMILY = "1,2";
951 | };
952 | name = Release;
953 | };
954 | 0A398A6520229EBF00741F6D /* Debug */ = {
955 | isa = XCBuildConfiguration;
956 | baseConfigurationReference = 100052882F75295A287FAA47 /* Pods-ToDoTests.debug.xcconfig */;
957 | buildSettings = {
958 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
959 | BUNDLE_LOADER = "$(TEST_HOST)";
960 | CODE_SIGN_STYLE = Automatic;
961 | INFOPLIST_FILE = ToDoTests/Info.plist;
962 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
963 | PRODUCT_BUNDLE_IDENTIFIER = "au.com.westpac.todo-ios.ToDoTests";
964 | PRODUCT_NAME = "$(TARGET_NAME)";
965 | SWIFT_VERSION = 4.2;
966 | TARGETED_DEVICE_FAMILY = "1,2";
967 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ToDo.app/ToDo";
968 | };
969 | name = Debug;
970 | };
971 | 0A398A6620229EBF00741F6D /* Release */ = {
972 | isa = XCBuildConfiguration;
973 | baseConfigurationReference = E9FA3507A60CD3E496C7878A /* Pods-ToDoTests.release.xcconfig */;
974 | buildSettings = {
975 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
976 | BUNDLE_LOADER = "$(TEST_HOST)";
977 | CODE_SIGN_STYLE = Automatic;
978 | INFOPLIST_FILE = ToDoTests/Info.plist;
979 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
980 | PRODUCT_BUNDLE_IDENTIFIER = "au.com.westpac.todo-ios.ToDoTests";
981 | PRODUCT_NAME = "$(TARGET_NAME)";
982 | SWIFT_VERSION = 4.2;
983 | TARGETED_DEVICE_FAMILY = "1,2";
984 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ToDo.app/ToDo";
985 | };
986 | name = Release;
987 | };
988 | 0A398A6820229EBF00741F6D /* Debug */ = {
989 | isa = XCBuildConfiguration;
990 | baseConfigurationReference = 33B0C1192983C4F2C0FD30CF /* Pods-ToDoUITests.debug.xcconfig */;
991 | buildSettings = {
992 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
993 | CODE_SIGN_STYLE = Automatic;
994 | INFOPLIST_FILE = ToDoUITests/Info.plist;
995 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
996 | PRODUCT_BUNDLE_IDENTIFIER = "au.com.westpac.todo-ios.ToDoUITests";
997 | PRODUCT_NAME = "$(TARGET_NAME)";
998 | SWIFT_VERSION = 4.2;
999 | TARGETED_DEVICE_FAMILY = "1,2";
1000 | TEST_TARGET_NAME = ToDo;
1001 | };
1002 | name = Debug;
1003 | };
1004 | 0A398A6920229EBF00741F6D /* Release */ = {
1005 | isa = XCBuildConfiguration;
1006 | baseConfigurationReference = 4671FC4DBBB94037D17F2066 /* Pods-ToDoUITests.release.xcconfig */;
1007 | buildSettings = {
1008 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1009 | CODE_SIGN_STYLE = Automatic;
1010 | INFOPLIST_FILE = ToDoUITests/Info.plist;
1011 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1012 | PRODUCT_BUNDLE_IDENTIFIER = "au.com.westpac.todo-ios.ToDoUITests";
1013 | PRODUCT_NAME = "$(TARGET_NAME)";
1014 | SWIFT_VERSION = 4.2;
1015 | TARGETED_DEVICE_FAMILY = "1,2";
1016 | TEST_TARGET_NAME = ToDo;
1017 | };
1018 | name = Release;
1019 | };
1020 | /* End XCBuildConfiguration section */
1021 |
1022 | /* Begin XCConfigurationList section */
1023 | 0A398A3420229EBE00741F6D /* Build configuration list for PBXProject "ToDo" */ = {
1024 | isa = XCConfigurationList;
1025 | buildConfigurations = (
1026 | 0A398A5F20229EBF00741F6D /* Debug */,
1027 | 0A398A6020229EBF00741F6D /* Release */,
1028 | );
1029 | defaultConfigurationIsVisible = 0;
1030 | defaultConfigurationName = Release;
1031 | };
1032 | 0A398A6120229EBF00741F6D /* Build configuration list for PBXNativeTarget "ToDo" */ = {
1033 | isa = XCConfigurationList;
1034 | buildConfigurations = (
1035 | 0A398A6220229EBF00741F6D /* Debug */,
1036 | 0A398A6320229EBF00741F6D /* Release */,
1037 | );
1038 | defaultConfigurationIsVisible = 0;
1039 | defaultConfigurationName = Release;
1040 | };
1041 | 0A398A6420229EBF00741F6D /* Build configuration list for PBXNativeTarget "ToDoTests" */ = {
1042 | isa = XCConfigurationList;
1043 | buildConfigurations = (
1044 | 0A398A6520229EBF00741F6D /* Debug */,
1045 | 0A398A6620229EBF00741F6D /* Release */,
1046 | );
1047 | defaultConfigurationIsVisible = 0;
1048 | defaultConfigurationName = Release;
1049 | };
1050 | 0A398A6720229EBF00741F6D /* Build configuration list for PBXNativeTarget "ToDoUITests" */ = {
1051 | isa = XCConfigurationList;
1052 | buildConfigurations = (
1053 | 0A398A6820229EBF00741F6D /* Debug */,
1054 | 0A398A6920229EBF00741F6D /* Release */,
1055 | );
1056 | defaultConfigurationIsVisible = 0;
1057 | defaultConfigurationName = Release;
1058 | };
1059 | /* End XCConfigurationList section */
1060 | };
1061 | rootObject = 0A398A3120229EBE00741F6D /* Project object */;
1062 | }
1063 |
--------------------------------------------------------------------------------
/ToDo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ToDo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ToDo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ToDo/App/AppComponent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppComponent.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RIBs
11 | import RxSwift
12 |
13 | final class AppComponent: Component, RootDependency {
14 | let application: UIApplication
15 | let launchOptions: [UIApplication.LaunchOptionsKey : Any]?
16 | let service: ToDoServiceProtocol
17 | let config: Variable
18 |
19 |
20 | init(application: UIApplication, launchOptions: [UIApplication.LaunchOptionsKey : Any]?) {
21 | let configSnapshot = Config(maxIncompleteItems: 10)
22 |
23 | self.application = application
24 | self.launchOptions = launchOptions
25 | self.service = ToDoService()
26 | self.config = Variable(configSnapshot)
27 |
28 | super.init(dependency: EmptyComponent())
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ToDo/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RIBs
11 |
12 | protocol UrlHandler: class {
13 | func handle(_ url: URL) -> Bool
14 | }
15 |
16 | @UIApplicationMain
17 | class AppDelegate: UIResponder, UIApplicationDelegate {
18 |
19 | var window: UIWindow?
20 |
21 | private var launchRouter: LaunchRouting?
22 | private var urlHandler: UrlHandler?
23 |
24 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
25 | // Override point for customization after application launch.
26 |
27 | let window = UIWindow(frame: UIScreen.main.bounds)
28 |
29 | self.window = window
30 |
31 | let appComponent = AppComponent(application: application, launchOptions: launchOptions)
32 | let rib = RootBuilder(dependency: appComponent).build()
33 |
34 | launchRouter = rib.launchRouter
35 | urlHandler = rib.urlHandler
36 |
37 | launchRouter?.launchFromWindow(window)
38 |
39 | return true
40 | }
41 |
42 | func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
43 | return urlHandler?.handle(url) ?? false
44 | }
45 |
46 | func applicationWillResignActive(_ application: UIApplication) {
47 | // 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.
48 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
49 | }
50 |
51 | func applicationDidEnterBackground(_ application: UIApplication) {
52 | // 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.
53 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
54 | }
55 |
56 | func applicationWillEnterForeground(_ application: UIApplication) {
57 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
58 | }
59 |
60 | func applicationDidBecomeActive(_ application: UIApplication) {
61 | // 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.
62 | }
63 |
64 | func applicationWillTerminate(_ application: UIApplication) {
65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
66 | }
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/ToDo/Models/Config.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Config.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Config {
12 | let maxIncompleteItems: Int
13 |
14 | var appVersion: String {
15 | var defaultVersion = "1.0"
16 |
17 | if let dictionary = Bundle.main.infoDictionary {
18 | if let version = dictionary["CFBundleShortVersionString"] as? String {
19 | defaultVersion = version
20 | }
21 | }
22 |
23 | return defaultVersion
24 | }
25 |
26 | var buildNumber: String {
27 | var defaultNumber = "1"
28 |
29 | if let dictionary = Bundle.main.infoDictionary {
30 | if let build = dictionary["CFBundleVersion"] as? String {
31 | defaultNumber = build
32 | }
33 | }
34 |
35 | return defaultNumber
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/ToDo/Models/Item.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Item.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Item {
12 | let id: Int
13 | let createdAt: Date
14 | let details: String
15 | let isComplete: Bool
16 | }
17 |
--------------------------------------------------------------------------------
/ToDo/Models/ItemViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemViewModel.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | final class ItemViewModel {
13 | let model: Item
14 | private static let dateFormatter = DateFormatter()
15 |
16 | init(model: Item) {
17 | self.model = model
18 | }
19 |
20 | var key: Int {
21 | return model.id
22 | }
23 |
24 | var isComplete: Bool {
25 | return model.isComplete
26 | }
27 |
28 | var statusColor: UIColor {
29 | return isComplete ? .blue : .green
30 | }
31 |
32 | var details: String {
33 | return model.details
34 | }
35 |
36 | var createdAt: String {
37 | get {
38 | ItemViewModel.dateFormatter.dateStyle = .short
39 | ItemViewModel.dateFormatter.timeStyle = .short
40 |
41 | return ItemViewModel.dateFormatter.string(from: model.createdAt)
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ToDo/RIBs/ActionableItems/DrawerActionableItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawerActionableItem.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public protocol DrawerActionableItem: class {
12 | func waitForList() -> Observable<(ListActionableItem, ())>
13 | func sayHi() -> Observable<(ListActionableItem, ())>
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/ToDo/RIBs/ActionableItems/ListActionableItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListActionableItem.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 2/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public protocol ListActionableItem: class {
12 | func createTask(with description: String) -> Observable<(ListActionableItem, ())>
13 | func createBlankTask() -> Observable<(ListActionableItem, ())>
14 | func doNothing() -> Observable<(ListActionableItem, ())>
15 | }
16 |
--------------------------------------------------------------------------------
/ToDo/RIBs/ActionableItems/LoggedInActionableItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedInActionableItem.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public protocol LoggedInActionableItem: class {
12 | func waitForDrawer() -> Observable<(DrawerActionableItem, ())>
13 | }
14 |
--------------------------------------------------------------------------------
/ToDo/RIBs/ActionableItems/RootActionableItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootActionableItem.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public protocol RootActionableItem: class {
12 | func waitForLogin() -> Observable<(LoggedInActionableItem, ())>
13 | }
14 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Common/LoadingPresentable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadingPresentable.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SVProgressHUD
11 |
12 | protocol LoadingPresentable: class {
13 | func showLoading(with status: String?)
14 | func showSuccess(with status: String?)
15 | func showError(with status: String?)
16 |
17 | func hideLoading()
18 | }
19 |
20 | extension UIViewController: LoadingPresentable {
21 | func showLoading(with status: String?) {
22 | SVProgressHUD.show(withStatus: status)
23 | }
24 |
25 | func showSuccess(with status: String?) {
26 | SVProgressHUD.showSuccess(withStatus: status)
27 | }
28 |
29 | func showError(with status: String?) {
30 | SVProgressHUD.showError(withStatus: status)
31 | }
32 |
33 | func hideLoading() {
34 | SVProgressHUD.dismiss()
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Common/UINavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UINavigationController.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RIBs
11 |
12 | extension UINavigationController: ViewControllable {
13 | public var uiviewController: UIViewController { return self }
14 |
15 | public convenience init(root: ViewControllable) {
16 | self.init(rootViewController: root.uiviewController)
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Drawer/DrawerBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol DrawerDependency: Dependency {
13 | // TODO: Declare the set of dependencies required by this RIB, but cannot be
14 | // created by this RIB.
15 |
16 | var service: ToDoServiceProtocol { get }
17 | var config: Variable { get }
18 | }
19 |
20 | final class DrawerComponent: Component {
21 | var service: ToDoServiceProtocol {
22 | return dependency.service
23 | }
24 |
25 | var config: Variable {
26 | return dependency.config
27 | }
28 | }
29 |
30 | // MARK: - Builder
31 |
32 | protocol DrawerBuildable: Buildable {
33 | func build(withListener listener: DrawerListener) -> (router: DrawerRouting, actionableItem: DrawerActionableItem)
34 | }
35 |
36 | final class DrawerBuilder: Builder, DrawerBuildable {
37 |
38 | override init(dependency: DrawerDependency) {
39 | super.init(dependency: dependency)
40 | }
41 |
42 | func build(withListener listener: DrawerListener) -> (router: DrawerRouting, actionableItem: DrawerActionableItem) {
43 | let screenWidth = UIScreen.main.bounds.size.width
44 | var menuWidth: CGFloat = screenWidth - 75.0
45 |
46 | if screenWidth > 375 {
47 | menuWidth = screenWidth - 150.0
48 | } else if screenWidth > 320 {
49 | menuWidth = screenWidth - 82.0
50 | }
51 |
52 | let component = DrawerComponent(dependency: dependency)
53 | let viewController = DrawerViewController(menuWidth: menuWidth)
54 | let interactor = DrawerInteractor(presenter: viewController)
55 |
56 | interactor.listener = listener
57 |
58 | let menuBuilder = MenuBuilder(dependency: component)
59 | let listBuilder = ListBuilder(dependency: component)
60 |
61 | let router = DrawerRouter(interactor: interactor, viewController: viewController,
62 | menuBuilder: menuBuilder, listBuilder: listBuilder)
63 |
64 | return (router, interactor)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Drawer/DrawerComponent+List.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawerComponent+List.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | /// The dependencies needed from the parent scope of Drawer to provide for the List scope.
12 | // TODO: Update DrawerDependency protocol to inherit this protocol.
13 | protocol DrawerDependencyList: Dependency {
14 | // TODO: Declare dependencies needed from the parent scope of Drawer to provide dependencies
15 | // for the List scope.
16 | }
17 |
18 | extension DrawerComponent: ListDependency {
19 | // TODO: Implement properties to provide for List scope.
20 | }
21 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Drawer/DrawerComponent+Menu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawerComponent+Menu.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 2/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | /// The dependencies needed from the parent scope of Drawer to provide for the Menu scope.
12 | // TODO: Update DrawerDependency protocol to inherit this protocol.
13 | protocol DrawerDependencyMenu: Dependency {
14 | // TODO: Declare dependencies needed from the parent scope of Drawer to provide dependencies
15 | // for the Menu scope.
16 | }
17 |
18 | extension DrawerComponent: MenuDependency {
19 |
20 | // TODO: Implement properties to provide for Menu scope.
21 | }
22 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Drawer/DrawerInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol DrawerRouting: ViewableRouting {
13 | // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.
14 |
15 | func showContent() -> ListActionableItem
16 | }
17 |
18 | protocol DrawerPresentable: Presentable {
19 | var listener: DrawerPresentableListener? { get set }
20 | // TODO: Declare methods the interactor can invoke the presenter to present data.
21 |
22 | func showSelectedMenuItem(_ menuItem: String)
23 | func showMenu()
24 | func hideMenu()
25 | func showHi()
26 | }
27 |
28 | protocol DrawerListener: class {
29 | // TODO: Declare methods the interactor can invoke to communicate with other RIBs.
30 |
31 | func drawerDidAppear()
32 | }
33 |
34 | final class DrawerInteractor: PresentableInteractor, DrawerInteractable,
35 | DrawerPresentableListener, DrawerActionableItem {
36 |
37 | weak var router: DrawerRouting?
38 | weak var listener: DrawerListener?
39 |
40 | var listActionableItem: ListActionableItem?
41 |
42 | private var isMenuOpened: Bool = false
43 | private let listActionableItemSubject = ReplaySubject.create(bufferSize: 1)
44 |
45 | // TODO: Add additional dependencies to constructor. Do not perform any logic
46 | // in constructor.
47 | override init(presenter: DrawerPresentable) {
48 | super.init(presenter: presenter)
49 |
50 | presenter.listener = self
51 | }
52 |
53 | override func didBecomeActive() {
54 | super.didBecomeActive()
55 | // TODO: Implement business logic here.
56 |
57 | listActionableItem = router?.showContent()
58 | }
59 |
60 | override func willResignActive() {
61 | super.willResignActive()
62 | // TODO: Pause any business logic.
63 | }
64 |
65 | // MARK: - DrawerPresentableListener
66 |
67 | func didOpenMenu() {
68 | isMenuOpened = true
69 | }
70 |
71 | func didCloseMenu() {
72 | isMenuOpened = false
73 | }
74 |
75 | func didAppear() {
76 | listener?.drawerDidAppear()
77 | }
78 |
79 | // MARK: - MenuListener
80 |
81 | func didSelectMenu(_ menuItem: String) {
82 | presenter.hideMenu()
83 | presenter.showSelectedMenuItem(menuItem)
84 | }
85 |
86 | // MARK: - ListListener
87 | func listDidAppear() {
88 | if let listActionableItem = listActionableItem {
89 | listActionableItemSubject.onNext(listActionableItem)
90 | }
91 |
92 | listActionableItem = nil
93 | }
94 |
95 | func didSelectMenu() {
96 | presenter.showMenu()
97 | }
98 |
99 | // MARK: - DrawerActionableItem
100 |
101 | func waitForList() -> Observable<(ListActionableItem, ())> {
102 | return listActionableItemSubject
103 | .map { (listItem: ListActionableItem) -> (ListActionableItem, ()) in
104 | (listItem, ())
105 | }
106 | }
107 |
108 | func sayHi() -> Observable<(ListActionableItem, ())> {
109 | presenter.showHi()
110 |
111 | return listActionableItemSubject
112 | .map { (listItem: ListActionableItem) -> (ListActionableItem, ()) in
113 | (listItem, ())
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Drawer/DrawerRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol DrawerInteractable: Interactable, MenuListener, ListListener {
13 |
14 | var router: DrawerRouting? { get set }
15 | var listener: DrawerListener? { get set }
16 | }
17 |
18 | protocol DrawerViewControllable: ViewControllable {
19 | // TODO: Declare methods the router invokes to manipulate the view hierarchy.
20 |
21 | func presentContent(viewController: ViewControllable)
22 | func presentMenu(viewController: ViewControllable)
23 | }
24 |
25 | final class DrawerRouter: ViewableRouter, DrawerRouting {
26 | private var menuBuilder: MenuBuildable
27 | private var listBuilder: ListBuildable
28 |
29 | private (set) var menuItemChild: ViewableRouting?
30 |
31 | // TODO: Constructor inject child builder protocols to allow building children.
32 | required init(interactor: DrawerInteractable, viewController: DrawerViewControllable,
33 | menuBuilder: MenuBuildable, listBuilder: ListBuildable) {
34 |
35 | self.menuBuilder = menuBuilder
36 | self.listBuilder = listBuilder
37 |
38 | super.init(interactor: interactor, viewController: viewController)
39 |
40 | interactor.router = self
41 | }
42 |
43 | override func didLoad() {
44 | super.didLoad()
45 |
46 | attachMenu()
47 | }
48 |
49 | func showContent() -> ListActionableItem {
50 | let rib = listBuilder.build(withListener: interactor)
51 |
52 | attachChild(rib.router)
53 |
54 | let navController = UINavigationController(root: rib.router.viewControllable)
55 |
56 | viewController.presentContent(viewController: navController)
57 |
58 | return rib.actionableItem
59 | }
60 |
61 | func attachMenu() {
62 | let rib = menuBuilder.build(withListener: interactor)
63 |
64 | attachChild(rib)
65 |
66 | viewController.presentMenu(viewController: rib.viewControllable)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Drawer/DrawerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 | import UIKit
12 | import REFrostedViewController
13 |
14 | protocol DrawerPresentableListener: class {
15 | // TODO: Declare properties and methods that the view controller can invoke to perform
16 | // business logic, such as signIn(). This protocol is implemented by the corresponding
17 | // interactor class.
18 |
19 | func didOpenMenu()
20 | func didCloseMenu()
21 | func didAppear()
22 | }
23 |
24 | final class DrawerViewController: REFrostedViewController, DrawerPresentable, DrawerViewControllable {
25 |
26 | /// The UIKit view representation of this view.
27 | public final var uiviewController: UIViewController { return self }
28 |
29 | weak var listener: DrawerPresentableListener?
30 |
31 | init(menuWidth: CGFloat) {
32 | super.init(contentViewController: nil, menuViewController: nil)
33 |
34 | let screenHeight = UIScreen.main.bounds.size.height
35 |
36 | self.direction = .right
37 | self.liveBlurBackgroundStyle = .dark
38 | self.liveBlur = true
39 | self.panGestureEnabled = true
40 | self.limitMenuViewSize = true
41 | self.menuViewSize = CGSize(width: menuWidth, height: screenHeight)
42 | self.panGestureRecognizer.delegate = self
43 | self.delegate = self
44 | }
45 |
46 | override func viewDidAppear(_ animated: Bool) {
47 | super.viewDidAppear(animated)
48 |
49 | listener?.didAppear()
50 | }
51 |
52 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
53 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
54 | }
55 |
56 | required init?(coder aDecoder: NSCoder) {
57 | fatalError("init(coder:) has not been implemented")
58 | }
59 |
60 | func presentContent(viewController: ViewControllable) {
61 | contentViewController = viewController.uiviewController
62 | }
63 |
64 | func presentMenu(viewController: ViewControllable) {
65 | viewController.uiviewController.view.frame = CGRect(x: 0, y: 0,
66 | width: menuViewSize.width,
67 | height: menuViewSize.height)
68 | menuViewController = viewController.uiviewController
69 | }
70 |
71 | func presentMenuItem(viewController: ViewControllable) {
72 | present(viewController.uiviewController, animated: true)
73 | }
74 |
75 | func dismissMenuItem(viewController: ViewControllable) {
76 | viewController.uiviewController.dismiss(animated: true, completion: nil)
77 | }
78 |
79 | // MARK: - DrawerPresentable
80 |
81 | func showMenu() {
82 | presentMenuViewController()
83 | }
84 |
85 | func hideMenu() {
86 | hideMenuViewController()
87 | }
88 |
89 | func showHi() {
90 | let alert = UIAlertController(title: "Hi! 👋🏻", message: nil, preferredStyle: .alert)
91 |
92 | alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
93 |
94 | present(alert, animated: true, completion: nil)
95 | }
96 |
97 | func showSelectedMenuItem(_ menuItem: String) {
98 | let alert = UIAlertController(title: "You've selected:", message: menuItem, preferredStyle: .alert)
99 |
100 | alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
101 |
102 | present(alert, animated: true, completion: nil)
103 | }
104 | }
105 |
106 | // MARK: - UIGestureRecognizerDelegate
107 |
108 | extension DrawerViewController: UIGestureRecognizerDelegate {
109 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
110 | shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
111 |
112 | guard let view = gestureRecognizer.view else { return true }
113 |
114 | if view.isKind(of: UICollectionView.self) {
115 | return false
116 | } else {
117 | return true
118 | }
119 | }
120 | }
121 |
122 | // MARK: - REFrostedViewControllerDelegate
123 |
124 | extension DrawerViewController: REFrostedViewControllerDelegate {
125 | func frostedViewController(_ frostedViewController: REFrostedViewController!,
126 | didShowMenuViewController menuViewController: UIViewController!) {
127 | listener?.didOpenMenu()
128 | }
129 |
130 | func frostedViewController(_ frostedViewController: REFrostedViewController!,
131 | didHideMenuViewController menuViewController: UIViewController!) {
132 | listener?.didCloseMenu()
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Item/ItemBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemBuilder.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol ItemDependency: Dependency {
13 | // TODO: Declare the set of dependencies required by this RIB, but cannot be
14 | // created by this RIB.
15 |
16 | var config: Variable { get }
17 | var service: ToDoServiceProtocol { get }
18 | }
19 |
20 | final class ItemComponent: Component {
21 |
22 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.
23 |
24 | var config: Variable {
25 | return dependency.config
26 | }
27 |
28 | var service: ToDoServiceProtocol {
29 | return dependency.service
30 | }
31 | }
32 |
33 | // MARK: - Builder
34 |
35 | protocol ItemBuildable: Buildable {
36 | func build(withListener listener: ItemListener, item: ItemViewModel) -> ItemRouting
37 | }
38 |
39 | final class ItemBuilder: Builder, ItemBuildable {
40 |
41 | override init(dependency: ItemDependency) {
42 | super.init(dependency: dependency)
43 | }
44 |
45 | func build(withListener listener: ItemListener, item: ItemViewModel) -> ItemRouting {
46 | let component = ItemComponent(dependency: dependency)
47 | let viewController = ItemViewController()
48 | let interactor = ItemInteractor(presenter: viewController, item: item,
49 | service: component.service,
50 | config: component.config)
51 | interactor.listener = listener
52 |
53 | return ItemRouter(interactor: interactor, viewController: viewController)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Item/ItemInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol ItemRouting: ViewableRouting {
13 | // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.
14 | }
15 |
16 | protocol ItemPresentable: Presentable {
17 | var listener: ItemPresentableListener? { get set }
18 | // TODO: Declare methods the interactor can invoke the presenter to present data.
19 |
20 | func showTitle(_ title: String)
21 | func showDetails(_ value: String)
22 | }
23 |
24 | protocol ItemListener: class {
25 | // TODO: Declare methods the interactor can invoke to communicate with other RIBs.
26 |
27 | func closeItem()
28 | func didUpdateItemDetails(_ item: ItemViewModel)
29 | func didMarkAsDone(_ item: ItemViewModel)
30 | }
31 |
32 | final class ItemInteractor: PresentableInteractor, ItemInteractable, ItemPresentableListener {
33 |
34 | weak var router: ItemRouting?
35 | weak var listener: ItemListener?
36 |
37 | private let item: ItemViewModel
38 | private let service: ToDoServiceProtocol
39 | private let config: Variable
40 | private var newDetails: String
41 |
42 | // TODO: Add additional dependencies to constructor. Do not perform any logic
43 | // in constructor.
44 | init(presenter: ItemPresentable, item: ItemViewModel,
45 | service: ToDoServiceProtocol, config: Variable) {
46 |
47 | self.item = item
48 | self.config = config
49 | self.service = service
50 | self.newDetails = item.details
51 |
52 | super.init(presenter: presenter)
53 |
54 | presenter.listener = self
55 | }
56 |
57 | override func didBecomeActive() {
58 | super.didBecomeActive()
59 | // TODO: Implement business logic here.
60 | }
61 |
62 | override func willResignActive() {
63 | super.willResignActive()
64 | // TODO: Pause any business logic.
65 | }
66 |
67 | // MARK: - ItemPresentableListener
68 |
69 | func didNavigateBack() {
70 | listener?.closeItem()
71 | }
72 |
73 | func didPrepareView() {
74 | presenter.showTitle("ToDo")
75 | presenter.showDetails(item.details)
76 | }
77 |
78 | func didChangeDetails(_ value: String) {
79 | newDetails = value
80 | }
81 |
82 | func didMarkAsDone() {
83 | let newItem = Item(id: item.key,
84 | createdAt: item.model.createdAt,
85 | details: newDetails,
86 | isComplete: true)
87 |
88 | listener?.didMarkAsDone(ItemViewModel(model: newItem))
89 | }
90 |
91 | func didSave() {
92 | let newItem = Item(id: item.key,
93 | createdAt: item.model.createdAt,
94 | details: newDetails,
95 | isComplete: item.model.isComplete)
96 |
97 | listener?.didUpdateItemDetails(ItemViewModel(model: newItem))
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Item/ItemRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemRouter.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | protocol ItemInteractable: Interactable {
12 | var router: ItemRouting? { get set }
13 | var listener: ItemListener? { get set }
14 | }
15 |
16 | protocol ItemViewControllable: ViewControllable {
17 | // TODO: Declare methods the router invokes to manipulate the view hierarchy.
18 | }
19 |
20 | final class ItemRouter: ViewableRouter, ItemRouting {
21 |
22 | // TODO: Constructor inject child builder protocols to allow building children.
23 | override init(interactor: ItemInteractable, viewController: ItemViewControllable) {
24 | super.init(interactor: interactor, viewController: viewController)
25 | interactor.router = self
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Item/ItemViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemViewController.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 | import UIKit
12 |
13 | protocol ItemPresentableListener: class {
14 | // TODO: Declare properties and methods that the view controller can invoke to perform
15 | // business logic, such as signIn(). This protocol is implemented by the corresponding
16 | // interactor class.
17 |
18 | func didChangeDetails(_ value: String)
19 | func didMarkAsDone()
20 | func didSave()
21 | func didPrepareView()
22 | func didNavigateBack()
23 | }
24 |
25 | final class ItemViewController: UIViewController, ItemPresentable, ItemViewControllable, UITextFieldDelegate {
26 |
27 | /// The UIKit view representation of this view.
28 | public final var uiviewController: UIViewController { return self }
29 |
30 | weak var listener: ItemPresentableListener?
31 |
32 | @IBOutlet private weak var detailsField: UITextField!
33 | @IBOutlet private weak var doneButton: UIButton!
34 | @IBOutlet private weak var saveButton: UIButton!
35 |
36 | override func viewDidLoad() {
37 | super.viewDidLoad()
38 |
39 | listener?.didPrepareView()
40 | }
41 |
42 | @IBAction private func onSaveButtonTap() {
43 | listener?.didChangeDetails(detailsField.text ?? "")
44 | listener?.didSave()
45 | }
46 |
47 | @IBAction private func onDoneButtonTap() {
48 | listener?.didChangeDetails(detailsField.text ?? "")
49 | listener?.didMarkAsDone()
50 | }
51 |
52 | override func viewDidDisappear(_ animated: Bool) {
53 | super.viewDidDisappear(animated)
54 |
55 | if isMovingFromParent {
56 | listener?.didNavigateBack()
57 | }
58 | }
59 |
60 | // MARK: - ItemPresentable
61 |
62 | func showDetails(_ value: String) {
63 | detailsField.text = value
64 | }
65 |
66 | func showTitle(_ title: String) {
67 | self.title = title
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Item/ItemViewController.xib:
--------------------------------------------------------------------------------
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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/ToDo/RIBs/List/ListBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListBuilder.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol ListDependency: Dependency {
13 | // TODO: Declare the set of dependencies required by this RIB, but cannot be
14 | // created by this RIB.
15 |
16 | var service: ToDoServiceProtocol { get }
17 | var config: Variable { get }
18 | }
19 |
20 | final class ListComponent: Component {
21 |
22 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.
23 |
24 | var service: ToDoServiceProtocol {
25 | return dependency.service
26 | }
27 |
28 | var config: Variable {
29 | return dependency.config
30 | }
31 | }
32 |
33 | // MARK: - Builder
34 |
35 | protocol ListBuildable: Buildable {
36 | func build(withListener listener: ListListener) -> (router: ListRouting, actionableItem: ListActionableItem)
37 | }
38 |
39 | final class ListBuilder: Builder, ListBuildable {
40 |
41 | override init(dependency: ListDependency) {
42 | super.init(dependency: dependency)
43 | }
44 |
45 | func build(withListener listener: ListListener) -> (router: ListRouting, actionableItem: ListActionableItem) {
46 | let component = ListComponent(dependency: dependency)
47 | let viewController = ListViewController(nibName: "ListViewController", bundle: Bundle.main)
48 | let itemBuilder = ItemBuilder(dependency: component)
49 | let interactor = ListInteractor(presenter: viewController,
50 | service: component.service,
51 | config: component.config)
52 | interactor.listener = listener
53 |
54 | let router = ListRouter(interactor: interactor, viewController: viewController,
55 | itemBuilder: itemBuilder)
56 |
57 | return (router, interactor)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ToDo/RIBs/List/ListComponent+Item.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListComponent+Item.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | /// The dependencies needed from the parent scope of List to provide for the Item scope.
12 | // TODO: Update ListDependency protocol to inherit this protocol.
13 | protocol ListDependencyItem: Dependency {
14 | // TODO: Declare dependencies needed from the parent scope of List to provide dependencies
15 | // for the Item scope.
16 | }
17 |
18 | extension ListComponent: ItemDependency {
19 | // TODO: Implement properties to provide for Item scope.
20 | }
21 |
--------------------------------------------------------------------------------
/ToDo/RIBs/List/ListInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol RevertConfirmationDialogListener: class {
13 | func didSelectRevert(_ item: ItemViewModel)
14 | }
15 |
16 | protocol ListRouting: ViewableRouting {
17 | // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.
18 |
19 | func routeToItem(_ item: ItemViewModel)
20 | func closeItem()
21 | }
22 |
23 | protocol ListPresentable: Presentable, LoadingPresentable {
24 | var listener: ListPresentableListener? { get set }
25 | // TODO: Declare methods the interactor can invoke the presenter to present data.
26 |
27 | func showIncompleteItems(_ items: [ItemViewModel])
28 | func showCompletedItems(_ items: [ItemViewModel])
29 | func showTitle(_ title: String)
30 | func showMenu()
31 |
32 | func showRevertConfirmationDialog(item: ItemViewModel,
33 | listener: RevertConfirmationDialogListener)
34 | }
35 |
36 | protocol ListListener: class {
37 | // TODO: Declare methods the interactor can invoke to communicate with other RIBs.
38 |
39 | func listDidAppear()
40 | func didSelectMenu()
41 | }
42 |
43 | final class ListInteractor: PresentableInteractor, ListInteractable,
44 | ListPresentableListener, RevertConfirmationDialogListener,
45 | ListActionableItem {
46 |
47 | weak var router: ListRouting?
48 | weak var listener: ListListener?
49 |
50 | private let service: ToDoServiceProtocol
51 | private let config: Variable
52 |
53 | private var incompleItems: [ItemViewModel] = []
54 | private var completedItems: [ItemViewModel] = []
55 |
56 | // TODO: Add additional dependencies to constructor. Do not perform any logic
57 | // in constructor.
58 | init(presenter: ListPresentable, service: ToDoServiceProtocol, config: Variable) {
59 | self.service = service
60 | self.config = config
61 |
62 | super.init(presenter: presenter)
63 |
64 | presenter.listener = self
65 | }
66 |
67 | override func didBecomeActive() {
68 | super.didBecomeActive()
69 | // TODO: Implement business logic here.
70 |
71 | reloadItems()
72 |
73 | presenter.showTitle("ToDo List")
74 | presenter.showMenu()
75 | }
76 |
77 | override func willResignActive() {
78 | super.willResignActive()
79 | // TODO: Pause any business logic.
80 | }
81 |
82 | private func reloadItems() {
83 | presenter.showLoading(with: "Loading...")
84 |
85 | service.getIncompleteItems { [weak self] (response) in
86 | guard let this = self else { return }
87 |
88 | this.presenter.hideLoading()
89 |
90 | switch response {
91 | case .success(let items):
92 | let itemVMs = items.map { ItemViewModel(model: $0) }
93 |
94 | this.incompleItems = itemVMs
95 |
96 | this.presenter.showIncompleteItems(itemVMs)
97 | this.reloadCompletedItems()
98 | case .error(let error):
99 | this.presenter.showError(with: error.message)
100 | }
101 | }
102 | }
103 |
104 | private func reloadCompletedItems() {
105 | presenter.showLoading(with: "Loading...")
106 |
107 | service.getCompletedItems { [weak self] (response) in
108 | guard let this = self else { return }
109 |
110 | this.presenter.hideLoading()
111 |
112 | switch response {
113 | case .success(let items):
114 | let itemVMs = items.map { ItemViewModel(model: $0) }
115 |
116 | this.completedItems = itemVMs
117 |
118 | this.presenter.showCompletedItems(itemVMs)
119 | case .error(let error):
120 | this.presenter.showError(with: error.message)
121 | }
122 | }
123 | }
124 |
125 | // MARK: - ListPresentableListener
126 |
127 | func didAppear() {
128 | listener?.listDidAppear()
129 | }
130 |
131 | func didSelectItem(_ item: ItemViewModel) {
132 | if item.isComplete {
133 | presenter.showRevertConfirmationDialog(item: item, listener: self)
134 | } else {
135 | router?.routeToItem(item)
136 | }
137 | }
138 |
139 | func didSelectMenu() {
140 | listener?.didSelectMenu()
141 | }
142 |
143 | // MARK: - ItemListener
144 |
145 | func closeItem() {
146 | router?.closeItem()
147 | }
148 |
149 | func didUpdateItemDetails(_ item: ItemViewModel) {
150 | router?.closeItem()
151 |
152 | guard let index = incompleItems.index(where: { (model) -> Bool in
153 | return model.key == item.key
154 | }) else { return }
155 |
156 | incompleItems[index] = item
157 |
158 | presenter.showIncompleteItems(incompleItems)
159 | }
160 |
161 | func didMarkAsDone(_ item: ItemViewModel) {
162 | router?.closeItem()
163 |
164 | completedItems.append(item)
165 |
166 | presenter.showCompletedItems(completedItems)
167 |
168 | guard let index = incompleItems.index(where: { (model) -> Bool in
169 | return model.key == item.key
170 | }) else { return }
171 |
172 | incompleItems.remove(at: index)
173 | presenter.showIncompleteItems(incompleItems)
174 | }
175 |
176 | // MARK: - RevertConfirmationDialogListener
177 |
178 | func didSelectRevert(_ item: ItemViewModel) {
179 | let newItem = Item(id: item.key,
180 | createdAt: item.model.createdAt,
181 | details: item.details,
182 | isComplete: false)
183 |
184 | incompleItems.append(ItemViewModel(model: newItem))
185 | presenter.showIncompleteItems(incompleItems)
186 |
187 | guard let index = completedItems.index(where: { (model) -> Bool in
188 | return model.key == item.key
189 | }) else { return }
190 |
191 | completedItems.remove(at: index)
192 | presenter.showCompletedItems(completedItems)
193 | }
194 |
195 | // MARK: - ListActionableItem
196 |
197 | func createTask(with description: String) -> Observable<(ListActionableItem, ())> {
198 | let newItem = Item(id: Date().timestampInt, createdAt: Date(), details: description, isComplete: false)
199 |
200 | incompleItems.append(ItemViewModel(model: newItem))
201 |
202 | presenter.showIncompleteItems(incompleItems)
203 |
204 | return Observable.just((self, ()))
205 | }
206 |
207 | func createBlankTask() -> Observable<(ListActionableItem, ())> {
208 | let newItem = Item(id: Date().timestampInt, createdAt: Date(), details: "", isComplete: false)
209 | let itemVM = ItemViewModel(model: newItem)
210 |
211 | incompleItems.append(itemVM)
212 |
213 | presenter.showIncompleteItems(incompleItems)
214 |
215 | router?.routeToItem(itemVM)
216 |
217 | return Observable.just((self, ()))
218 | }
219 |
220 | func doNothing() -> Observable<(ListActionableItem, ())> {
221 | return Observable.just((self, ()))
222 | }
223 |
224 | }
225 |
--------------------------------------------------------------------------------
/ToDo/RIBs/List/ListRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListRouter.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | protocol ListInteractable: Interactable, ItemListener {
12 | var router: ListRouting? { get set }
13 | var listener: ListListener? { get set }
14 | }
15 |
16 | protocol ListViewControllable: ViewControllable {
17 | // TODO: Declare methods the router invokes to manipulate the view hierarchy.
18 |
19 | func present(view: ViewControllable)
20 | func dismiss(view: ViewControllable)
21 | }
22 |
23 | final class ListRouter: LaunchRouter, ListRouting {
24 | private let itemBuilder: ItemBuildable
25 | private var child: ViewableRouting?
26 |
27 | // TODO: Constructor inject child builder protocols to allow building children.
28 | init(interactor: ListInteractable, viewController: ListViewControllable,
29 | itemBuilder: ItemBuildable) {
30 |
31 | self.itemBuilder = itemBuilder
32 |
33 | super.init(interactor: interactor, viewController: viewController)
34 |
35 | interactor.router = self
36 | }
37 |
38 | // MARK: - ListRouting
39 |
40 | func routeToItem(_ item: ItemViewModel) {
41 | let rib = itemBuilder.build(withListener: interactor, item: item)
42 |
43 | attachChild(rib)
44 |
45 | viewController.present(view: rib.viewControllable)
46 |
47 | child = rib
48 | }
49 |
50 | func closeItem() {
51 | guard let child = self.child else { return }
52 |
53 | detachChild(child)
54 |
55 | viewController.dismiss(view: child.viewControllable)
56 |
57 | self.child = nil
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ToDo/RIBs/List/ListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListViewController.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 | import UIKit
12 |
13 | protocol ListPresentableListener: class {
14 | // TODO: Declare properties and methods that the view controller can invoke to perform
15 | // business logic, such as signIn(). This protocol is implemented by the corresponding
16 | // interactor class.
17 |
18 | func didSelectItem(_ item: ItemViewModel)
19 | func didSelectMenu()
20 | func didAppear()
21 | }
22 |
23 | final class ListViewController: UIViewController, ListPresentable, ListViewControllable {
24 |
25 | /// The UIKit view representation of this view.
26 | public final var uiviewController: UIViewController { return self }
27 |
28 | weak var listener: ListPresentableListener?
29 |
30 | @IBOutlet private weak var tableView: UITableView!
31 |
32 | private let tableViewCellId = "ListViewControllerItemCellId"
33 | private var incompleteItems: [ItemViewModel] = []
34 | private var completeItems: [ItemViewModel] = []
35 |
36 | override func viewDidLoad() {
37 | super.viewDidLoad()
38 |
39 | tableView.dataSource = self
40 | tableView.delegate = self
41 | tableView.rowHeight = 60
42 |
43 | tableView.register(UINib(nibName: "ItemCell", bundle: nil), forCellReuseIdentifier: tableViewCellId)
44 |
45 | view.addSubview(tableView)
46 | }
47 |
48 | override func viewDidAppear(_ animated: Bool) {
49 | super.viewDidAppear(animated)
50 |
51 | listener?.didAppear()
52 | }
53 |
54 | @objc
55 | private func onMenuButtonTap() {
56 | listener?.didSelectMenu()
57 | }
58 |
59 | // MARK: - ListPresentable
60 |
61 | func showIncompleteItems(_ items: [ItemViewModel]) {
62 | incompleteItems = items
63 |
64 | tableView.reloadData()
65 | }
66 |
67 | func showCompletedItems(_ items: [ItemViewModel]) {
68 | self.completeItems = items
69 |
70 | tableView.reloadData()
71 | }
72 |
73 | func showTitle(_ title: String) {
74 | self.title = title
75 | }
76 |
77 | func showMenu() {
78 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "More", style: .plain,
79 | target: self,
80 | action: #selector(self.onMenuButtonTap))
81 | }
82 |
83 | func showRevertConfirmationDialog(item: ItemViewModel,
84 | listener: RevertConfirmationDialogListener) {
85 |
86 | let alert = UIAlertController(title: "Revert changes?", message: nil, preferredStyle: .actionSheet)
87 |
88 | alert.addAction(UIAlertAction(title: "Mark as incomplete", style: .destructive,
89 | handler: { (action) in
90 | listener.didSelectRevert(item)
91 | }))
92 |
93 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
94 |
95 | present(alert, animated: true, completion: nil)
96 | }
97 |
98 | // MARK: - ListViewControllable
99 |
100 | func present(view: ViewControllable) {
101 | navigationController?.pushViewController(view.uiviewController, animated: true)
102 | }
103 |
104 | func dismiss(view: ViewControllable) {
105 | navigationController?.popViewController(animated: true)
106 | }
107 | }
108 |
109 | extension ListViewController: UITableViewDelegate {
110 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
111 | tableView.deselectRow(at: indexPath, animated: true)
112 |
113 | if indexPath.section == 0 {
114 | listener?.didSelectItem(incompleteItems[indexPath.row])
115 | } else {
116 | listener?.didSelectItem(completeItems[indexPath.row])
117 | }
118 | }
119 | }
120 |
121 | extension ListViewController: UITableViewDataSource {
122 | public func numberOfSections(in tableView: UITableView) -> Int {
123 | return 2
124 | }
125 |
126 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
127 | return section == 0 ? incompleteItems.count : completeItems.count
128 | }
129 |
130 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
131 | guard let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId) else { fatalError() }
132 | guard let itemCell = cell as? ItemCell else { fatalError() }
133 |
134 | if indexPath.section == 0 {
135 | itemCell.display(item: incompleteItems[indexPath.row])
136 | } else {
137 | itemCell.display(item: completeItems[indexPath.row])
138 | }
139 |
140 | return cell
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/ToDo/RIBs/List/ListViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedIn/LoggedInBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedInBuilder.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol LoggedInDependency: Dependency {
13 | var loggedInViewController: LoggedInViewControllable { get }
14 |
15 | var service: ToDoServiceProtocol { get }
16 | var config: Variable { get }
17 | }
18 |
19 | final class LoggedInComponent: Component {
20 | let sessionToken: String
21 |
22 | fileprivate var loggedInViewController: LoggedInViewControllable {
23 | return dependency.loggedInViewController
24 | }
25 |
26 | var service: ToDoServiceProtocol {
27 | return dependency.service
28 | }
29 |
30 | var config: Variable {
31 | return dependency.config
32 | }
33 |
34 | init(dependency: LoggedInDependency, sessionToken: String) {
35 | self.sessionToken = sessionToken
36 |
37 | super.init(dependency: dependency)
38 | }
39 | }
40 |
41 | // MARK: - Builder
42 |
43 | protocol LoggedInBuildable: Buildable {
44 | func build(withListener listener: LoggedInListener, session: String) ->
45 | (router: LoggedInRouting, actionableItem: LoggedInActionableItem)
46 | }
47 |
48 | final class LoggedInBuilder: Builder, LoggedInBuildable {
49 |
50 | override init(dependency: LoggedInDependency) {
51 | super.init(dependency: dependency)
52 | }
53 |
54 | func build(withListener listener: LoggedInListener, session: String) ->
55 | (router: LoggedInRouting, actionableItem: LoggedInActionableItem) {
56 |
57 | let component = LoggedInComponent(dependency: dependency, sessionToken: session)
58 | let interactor = LoggedInInteractor(sessionToken: session)
59 | let drawerBuilder = DrawerBuilder(dependency: component)
60 |
61 | interactor.listener = listener
62 |
63 | let router = LoggedInRouter(interactor: interactor,
64 | viewController: component.loggedInViewController,
65 | drawerBuilder: drawerBuilder)
66 | return (router, interactor)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedIn/LoggedInComponent+Drawer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedInComponent+Drawer.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | /// The dependencies needed from the parent scope of Root to provide for the LoggedOut scope.
12 | // TODO: Update RootDependency protocol to inherit this protocol.
13 | protocol LoggedInDependencyDrawer: Dependency {
14 |
15 | // TODO: Declare dependencies needed from the parent scope of LoggedIn to provide dependencies
16 | // for the child scope.
17 | }
18 |
19 | extension LoggedInComponent: DrawerDependency {
20 | // TODO: Implement properties to provide for AccountSelection scope.
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedIn/LoggedInInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedInInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol LoggedInRouting: Routing {
13 | func cleanupViews()
14 | func routeToDrawer() -> DrawerActionableItem
15 | }
16 |
17 | protocol LoggedInListener: class {
18 | // TODO: Declare methods the interactor can invoke to communicate with other RIBs.
19 | func didFinishLoggedInJob()
20 | }
21 |
22 | final class LoggedInInteractor: Interactor, LoggedInInteractable, LoggedInActionableItem {
23 | weak var router: LoggedInRouting?
24 | weak var listener: LoggedInListener?
25 |
26 | var drawerActionableItem: DrawerActionableItem?
27 |
28 | private let sessionToken: String
29 |
30 | init(sessionToken: String) {
31 | self.sessionToken = sessionToken
32 |
33 | super.init()
34 | }
35 |
36 | override func didBecomeActive() {
37 | super.didBecomeActive()
38 |
39 | drawerActionableItem = router?.routeToDrawer()
40 | }
41 |
42 | override func willResignActive() {
43 | super.willResignActive()
44 |
45 | // TODO: Pause any business logic.
46 |
47 | router?.cleanupViews()
48 | }
49 |
50 | // MARK: - DrawerListener
51 |
52 | func drawerDidAppear() {
53 | if let drawerActionableItem = drawerActionableItem {
54 | drawerActionableItemSubject.onNext(drawerActionableItem)
55 | }
56 |
57 | drawerActionableItem = nil
58 | }
59 |
60 | // MARK: - LoggedInActionableItem
61 |
62 | func waitForDrawer() -> Observable<(DrawerActionableItem, ())> {
63 | return drawerActionableItemSubject
64 | .map { (drawerItem: DrawerActionableItem) -> (DrawerActionableItem, ()) in
65 | (drawerItem, ())
66 | }
67 | }
68 |
69 | // MARK: - Private
70 |
71 | private let drawerActionableItemSubject = ReplaySubject.create(bufferSize: 1)
72 | }
73 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedIn/LoggedInRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedInRouter.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol LoggedInInteractable: Interactable, DrawerListener {
13 | var router: LoggedInRouting? { get set }
14 | var listener: LoggedInListener? { get set }
15 | }
16 |
17 | protocol LoggedInViewControllable: ViewControllable {
18 | func replaceModal(viewController: ViewControllable?)
19 | }
20 |
21 | final class LoggedInRouter: Router, LoggedInRouting {
22 | private let viewController: LoggedInViewControllable
23 | private let drawerBuilder: DrawerBuildable
24 | private var drawerChild: (router: DrawerRouting, actionableItem: DrawerActionableItem)?
25 |
26 | required init(interactor: LoggedInInteractable,
27 | viewController: LoggedInViewControllable,
28 | drawerBuilder: DrawerBuildable) {
29 |
30 | self.drawerBuilder = drawerBuilder
31 | self.viewController = viewController
32 |
33 | super.init(interactor: interactor)
34 |
35 | interactor.router = self
36 | }
37 |
38 | // MARK: - LoggedInRouting
39 |
40 | func cleanupViews() {
41 | if drawerChild != nil {
42 | viewController.replaceModal(viewController: nil)
43 | }
44 | }
45 |
46 | func routeToDrawer() -> DrawerActionableItem {
47 | let rib = drawerBuilder.build(withListener: interactor)
48 |
49 | self.drawerChild = rib
50 |
51 | attachChild(rib.router)
52 |
53 | viewController.replaceModal(viewController: rib.router.viewControllable)
54 |
55 | return rib.actionableItem
56 | }
57 |
58 | // MARK: - Private
59 |
60 | private func detachCurrentChild() {
61 | if let child = drawerChild {
62 | detachChild(child.router)
63 | viewController.replaceModal(viewController: nil)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedOut/LoggedOutBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutBuilder.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol LoggedOutDependency: Dependency {
13 | // TODO: Declare the set of dependencies required by this RIB, but cannot be
14 | // created by this RIB.
15 |
16 | var service: ToDoServiceProtocol { get }
17 | var config: Variable { get }
18 | }
19 |
20 | final class LoggedOutComponent: Component {
21 |
22 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.
23 |
24 | var service: ToDoServiceProtocol {
25 | return dependency.service
26 | }
27 |
28 | var config: Variable {
29 | return dependency.config
30 | }
31 | }
32 |
33 | // MARK: - Builder
34 |
35 | protocol LoggedOutBuildable: Buildable {
36 | func build(withListener listener: LoggedOutListener) -> LoggedOutRouting
37 | }
38 |
39 | final class LoggedOutBuilder: Builder, LoggedOutBuildable {
40 |
41 | override init(dependency: LoggedOutDependency) {
42 | super.init(dependency: dependency)
43 | }
44 |
45 | func build(withListener listener: LoggedOutListener) -> LoggedOutRouting {
46 | let component = LoggedOutComponent(dependency: dependency)
47 | let viewController = LoggedOutViewController(nibName: "LoggedOutViewController", bundle: Bundle.main)
48 | let interactor = LoggedOutInteractor(presenter: viewController,
49 | service: component.service,
50 | config: component.config)
51 | interactor.listener = listener
52 |
53 | return LoggedOutRouter(interactor: interactor, viewController: viewController)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedOut/LoggedOutInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol LoggedOutRouting: ViewableRouting {
13 | // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.
14 | }
15 |
16 | protocol LoggedOutPresentable: Presentable {
17 | var listener: LoggedOutPresentableListener? { get set }
18 | // TODO: Declare methods the interactor can invoke the presenter to present data.
19 |
20 | func showLoadingIndicator()
21 | func hideLoadingIndicator()
22 | func showError(_ text: String)
23 | }
24 |
25 | protocol LoggedOutListener: class {
26 | func didLogin(sessionToken: String)
27 | }
28 |
29 | final class LoggedOutInteractor: PresentableInteractor, LoggedOutInteractable,
30 | LoggedOutPresentableListener {
31 |
32 | weak var router: LoggedOutRouting?
33 | weak var listener: LoggedOutListener?
34 |
35 | private let service: ToDoServiceProtocol
36 | private let config: Variable
37 |
38 | // TODO: Add additional dependencies to constructor. Do not perform any logic
39 | // in constructor.
40 | init(presenter: LoggedOutPresentable, service: ToDoServiceProtocol, config: Variable) {
41 | self.service = service
42 | self.config = config
43 |
44 | super.init(presenter: presenter)
45 |
46 | presenter.listener = self
47 | }
48 |
49 | override func didBecomeActive() {
50 | super.didBecomeActive()
51 | // TODO: Implement business logic here.
52 | }
53 |
54 | override func willResignActive() {
55 | super.willResignActive()
56 | // TODO: Pause any business logic.
57 | }
58 |
59 | // MARK: - LoggedOutPresentableListener
60 |
61 | func login(withName userName: String?, password: String?) {
62 | guard let name = userName, let pass = password else { return }
63 |
64 | if name.isEmpty || pass.isEmpty {
65 | presenter.showError("Missing username or password")
66 |
67 | return
68 | }
69 |
70 | presenter.showLoadingIndicator()
71 |
72 | // TODO: Here should be a service call to request an access token
73 | service.requestAccessToken(login: name, password: pass) { [weak self] (response) in
74 | guard let this = self else { return }
75 |
76 | this.presenter.hideLoadingIndicator()
77 |
78 | switch response {
79 | case .success(let token):
80 | this.listener?.didLogin(sessionToken: token)
81 | default:
82 | break
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedOut/LoggedOutRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutRouter.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | protocol LoggedOutInteractable: Interactable {
12 | var router: LoggedOutRouting? { get set }
13 | var listener: LoggedOutListener? { get set }
14 | }
15 |
16 | protocol LoggedOutViewControllable: ViewControllable {
17 | // TODO: Declare methods the router invokes to manipulate the view hierarchy.
18 | }
19 |
20 | final class LoggedOutRouter: ViewableRouter, LoggedOutRouting {
21 |
22 | // TODO: Constructor inject child builder protocols to allow building children.
23 | override init(interactor: LoggedOutInteractable, viewController: LoggedOutViewControllable) {
24 | super.init(interactor: interactor, viewController: viewController)
25 |
26 | interactor.router = self
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedOut/LoggedOutViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggedOutViewController.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxCocoa
11 | import RxSwift
12 | import UIKit
13 | import TransitionButton
14 | import SVProgressHUD
15 |
16 | protocol LoggedOutPresentableListener: class {
17 | func login(withName: String?, password: String?)
18 | }
19 |
20 | final class LoggedOutViewController: UIViewController, LoggedOutPresentable, LoggedOutViewControllable {
21 | // MARK: - Private
22 |
23 | private let disposeBag = DisposeBag()
24 |
25 | @IBOutlet private var loginButton: TransitionButton!
26 | @IBOutlet private var usernameField: UITextField!
27 | @IBOutlet private var passwordField: UITextField!
28 |
29 | weak var listener: LoggedOutPresentableListener?
30 |
31 | override func viewDidLoad() {
32 | super.viewDidLoad()
33 |
34 | loginButton.rx.tap
35 | .debounce(0.5, scheduler: MainScheduler.instance)
36 | .subscribe(onNext: { [weak self] in
37 | guard let this = self else { return }
38 |
39 | this.usernameField.resignFirstResponder()
40 | this.passwordField.resignFirstResponder()
41 |
42 | this.listener?.login(withName: this.usernameField?.text, password: this.passwordField?.text)
43 | })
44 | .disposed(by: disposeBag)
45 | }
46 |
47 | // MARK: - LoggedOutPresentable
48 |
49 | func showLoadingIndicator() {
50 | loginButton.startAnimation()
51 | }
52 |
53 | func hideLoadingIndicator() {
54 | loginButton.stopAnimation()
55 | }
56 |
57 | func showError(_ text: String) {
58 | SVProgressHUD.showError(withStatus: text)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ToDo/RIBs/LoggedOut/LoggedOutViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Menu/MenuBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuBuilder.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 2/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol MenuDependency: Dependency {
13 | // TODO: Declare the set of dependencies required by this RIB, but cannot be
14 | // created by this RIB.
15 |
16 | var service: ToDoServiceProtocol { get }
17 | var config: Variable { get }
18 | }
19 |
20 | final class MenuComponent: Component {
21 |
22 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.
23 |
24 | var service: ToDoServiceProtocol {
25 | return dependency.service
26 | }
27 |
28 | var config: Variable {
29 | return dependency.config
30 | }
31 | }
32 |
33 | // MARK: - Builder
34 |
35 | protocol MenuBuildable: Buildable {
36 | func build(withListener listener: MenuListener) -> MenuRouting
37 | }
38 |
39 | final class MenuBuilder: Builder, MenuBuildable {
40 |
41 | override init(dependency: MenuDependency) {
42 | super.init(dependency: dependency)
43 | }
44 |
45 | func build(withListener listener: MenuListener) -> MenuRouting {
46 | let component = MenuComponent(dependency: dependency)
47 | let viewController = MenuViewController(nibName: "MenuViewController", bundle: Bundle.main)
48 | let interactor = MenuInteractor(presenter: viewController,
49 | service: component.service,
50 | config: component.config)
51 | interactor.listener = listener
52 |
53 | return MenuRouter(interactor: interactor, viewController: viewController)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Menu/MenuInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 2/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 | import Eureka
12 |
13 | protocol MenuRouting: ViewableRouting {
14 | // TODO: Declare methods the interactor can invoke to manage sub-tree via the router.
15 | }
16 |
17 | protocol MenuPresentable: Presentable {
18 | var listener: MenuPresentableListener? { get set }
19 | // TODO: Declare methods the interactor can invoke the presenter to present data.
20 |
21 | func showMenuItems(_ sections: [Section])
22 | }
23 |
24 | protocol MenuListener: class {
25 | // TODO: Declare methods the interactor can invoke to communicate with other RIBs.
26 |
27 | func didSelectMenu(_ menuItem: String)
28 | }
29 |
30 | final class MenuInteractor: PresentableInteractor, MenuInteractable, MenuPresentableListener {
31 |
32 | weak var router: MenuRouting?
33 | weak var listener: MenuListener?
34 |
35 | private let service: ToDoServiceProtocol
36 | private let config: Variable
37 |
38 | // TODO: Add additional dependencies to constructor. Do not perform any logic
39 | // in constructor.
40 | init(presenter: MenuPresentable, service: ToDoServiceProtocol, config: Variable) {
41 | self.service = service
42 | self.config = config
43 |
44 | super.init(presenter: presenter)
45 | presenter.listener = self
46 | }
47 |
48 | override func didBecomeActive() {
49 | super.didBecomeActive()
50 | // TODO: Implement business logic here.
51 | }
52 |
53 | override func willResignActive() {
54 | super.willResignActive()
55 | // TODO: Pause any business logic.
56 | }
57 |
58 | // MARK: - MenuPresentableListener
59 |
60 | func didPrepareView() {
61 | var form: [Section] = []
62 | var section = Section("Support")
63 |
64 | form.append(section)
65 |
66 | var buttonRow = ButtonRow(tag: "feedback")
67 |
68 | buttonRow.title = "Feedback"
69 |
70 | buttonRow.onCellSelection { [unowned self] (cell, row) in
71 | self.onFeedbackTap()
72 | }
73 |
74 | buttonRow.cellUpdate { (cell, row) in
75 | cell.tintColor = .darkGray
76 | cell.accessoryType = .disclosureIndicator
77 | cell.textLabel?.textAlignment = .left
78 | }
79 |
80 | section.append(buttonRow)
81 |
82 | buttonRow = ButtonRow(tag: "share")
83 |
84 | buttonRow.title = "Share"
85 |
86 | buttonRow.onCellSelection { [unowned self] (cell, row) in
87 | self.onShareTap()
88 | }
89 |
90 | buttonRow.cellUpdate { (cell, row) in
91 | cell.tintColor = .darkGray
92 | cell.accessoryType = .disclosureIndicator
93 | cell.textLabel?.textAlignment = .left
94 | }
95 |
96 | section.append(buttonRow)
97 |
98 | buttonRow = ButtonRow(tag: "rate")
99 |
100 | buttonRow.title = "Rate us"
101 |
102 | buttonRow.onCellSelection { [unowned self] (cell, row) in
103 | self.onRateTap()
104 | }
105 |
106 | buttonRow.cellUpdate { (cell, row) in
107 | cell.tintColor = .darkGray
108 | cell.accessoryType = .disclosureIndicator
109 | cell.textLabel?.textAlignment = .left
110 | }
111 |
112 | section.append(buttonRow)
113 |
114 | section = Section("Extra")
115 |
116 | form.append(section)
117 |
118 | buttonRow = ButtonRow(tag: "tos")
119 |
120 | buttonRow.title = "Terms and Conditions"
121 |
122 | buttonRow.onCellSelection { [unowned self] (cell, row) in
123 | self.onTermsOfServiceTap()
124 | }
125 |
126 | buttonRow.cellUpdate { (cell, row) in
127 | cell.tintColor = .darkGray
128 | cell.accessoryType = .disclosureIndicator
129 | cell.textLabel?.textAlignment = .left
130 | }
131 |
132 | section.append(buttonRow)
133 |
134 | buttonRow = ButtonRow(tag: "pp")
135 |
136 | buttonRow.title = "Privacy Policy"
137 |
138 | buttonRow.onCellSelection { [unowned self] (cell, row) in
139 | self.onPrivacyPolicyTap()
140 | }
141 |
142 | buttonRow.cellUpdate { (cell, row) in
143 | cell.tintColor = .darkGray
144 | cell.accessoryType = .disclosureIndicator
145 | cell.textLabel?.textAlignment = .left
146 | }
147 |
148 | section.append(buttonRow)
149 |
150 | section = Section()
151 |
152 | form.append(section)
153 |
154 | let labelRow = LabelRow(tag: "version")
155 |
156 | labelRow.title = "Version"
157 | labelRow.value = "\(config.value.appVersion) (\(config.value.buildNumber))"
158 |
159 | section.append(labelRow)
160 |
161 | presenter.showMenuItems(form)
162 | }
163 |
164 | private func onFeedbackTap() {
165 | listener?.didSelectMenu("Feedback")
166 | }
167 |
168 | private func onShareTap() {
169 | listener?.didSelectMenu("Share")
170 | }
171 |
172 | private func onRateTap() {
173 | listener?.didSelectMenu("Rate us")
174 | }
175 |
176 | private func onTermsOfServiceTap() {
177 | listener?.didSelectMenu("Terms of Service")
178 | }
179 |
180 | private func onPrivacyPolicyTap() {
181 | listener?.didSelectMenu("Privacy Policy")
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Menu/MenuRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuRouter.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 2/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | protocol MenuInteractable: Interactable {
12 | var router: MenuRouting? { get set }
13 | var listener: MenuListener? { get set }
14 | }
15 |
16 | protocol MenuViewControllable: ViewControllable {
17 | // TODO: Declare methods the router invokes to manipulate the view hierarchy.
18 | }
19 |
20 | final class MenuRouter: ViewableRouter, MenuRouting {
21 |
22 | // TODO: Constructor inject child builder protocols to allow building children.
23 | override init(interactor: MenuInteractable, viewController: MenuViewControllable) {
24 | super.init(interactor: interactor, viewController: viewController)
25 | interactor.router = self
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Menu/MenuViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuViewController.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 2/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 | import UIKit
12 | import Eureka
13 |
14 | protocol MenuPresentableListener: class {
15 | // TODO: Declare properties and methods that the view controller can invoke to perform
16 | // business logic, such as signIn(). This protocol is implemented by the corresponding
17 | // interactor class.
18 |
19 | func didPrepareView()
20 | }
21 |
22 | final class MenuViewController: FormViewController, MenuPresentable, MenuViewControllable {
23 | /// The UIKit view representation of this view.
24 | public final var uiviewController: UIViewController { return self }
25 |
26 | weak var listener: MenuPresentableListener?
27 |
28 | override func viewDidLoad() {
29 | super.viewDidLoad()
30 |
31 | listener?.didPrepareView()
32 | }
33 |
34 | // MARK: - MenuPresentable
35 |
36 | func showMenuItems(_ sections: [Section]) {
37 | LabelRow.defaultCellUpdate = { cell, row in cell.tintColor = .darkGray }
38 | URLRow.defaultCellUpdate = { cell, row in cell.textField.textColor = .darkGray }
39 | CheckRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
40 | SegmentedRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
41 | PasswordRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
42 | DateRow.defaultRowInitializer = { row in row.minimumDate = Date() }
43 | NameRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
44 | TextAreaRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
45 | EmailRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
46 | PhoneRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
47 | SwitchRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
48 | TimeInlineRow.defaultCellSetup = { cell, row in cell.tintColor = .darkGray }
49 | ButtonRow.defaultCellSetup = { cell, row in
50 | cell.tintColor = .darkGray
51 | cell.accessoryType = .disclosureIndicator
52 | cell.textLabel?.textAlignment = .left
53 | }
54 |
55 | form.append(contentsOf: sections)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Menu/MenuViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Root/RootBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootBuilder.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol RootDependency: Dependency {
13 | // TODO: Declare the set of dependencies required by this RIB, but cannot be
14 | // created by this RIB.
15 |
16 | var application: UIApplication { get }
17 | var launchOptions: [UIApplication.LaunchOptionsKey: Any]? { get }
18 | var service: ToDoServiceProtocol { get }
19 | var config: Variable { get }
20 | }
21 |
22 | final class RootComponent: Component {
23 | // TODO: Declare 'fileprivate' dependencies that are only used by this RIB.
24 |
25 | let rootViewController: RootViewController
26 |
27 | var application: UIApplication {
28 | return dependency.application
29 | }
30 |
31 | var launchOptions: [UIApplication.LaunchOptionsKey: Any]? {
32 | return dependency.launchOptions
33 | }
34 |
35 | var config: Variable {
36 | return dependency.config
37 | }
38 |
39 | var service: ToDoServiceProtocol {
40 | return dependency.service
41 | }
42 |
43 | init(dependency: RootDependency,
44 | rootViewController: RootViewController) {
45 |
46 | self.rootViewController = rootViewController
47 |
48 | super.init(dependency: dependency)
49 | }
50 | }
51 |
52 | // MARK: - Builder
53 |
54 | protocol RootBuildable: Buildable {
55 | func build() -> (launchRouter: LaunchRouting, urlHandler: UrlHandler)
56 | }
57 |
58 | final class RootBuilder: Builder, RootBuildable {
59 |
60 | override init(dependency: RootDependency) {
61 | super.init(dependency: dependency)
62 | }
63 |
64 | func build() -> (launchRouter: LaunchRouting, urlHandler: UrlHandler) {
65 | let viewController = RootViewController()
66 | let component = RootComponent(dependency: dependency,
67 | rootViewController: viewController)
68 | let interactor = RootInteractor(presenter: viewController)
69 |
70 | let loggedOutBuilder = LoggedOutBuilder(dependency: component)
71 | let loggedInBuilder = LoggedInBuilder(dependency: component)
72 |
73 | let router = RootRouter(interactor: interactor,
74 | viewController: viewController,
75 | loggedOutBuilder: loggedOutBuilder,
76 | loggedInBuilder: loggedInBuilder)
77 |
78 | return (router, interactor)
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Root/RootComponent+LoggedIn.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootComponent+LoggedIn.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | /// The dependencies needed from the parent scope of Root to provide for the LoggedIn scope.
12 | // TODO: Update RootDependency protocol to inherit this protocol.
13 | protocol RootDependencyLoggedIn: Dependency {
14 |
15 | // TODO: Declare dependencies needed from the parent scope of Root to provide dependencies
16 | // for the LoggedIn scope.
17 | }
18 |
19 | extension RootComponent: LoggedInDependency {
20 | var loggedInViewController: LoggedInViewControllable {
21 | return rootViewController
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Root/RootComponent+LoggedOut.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootComponent+LoggedOut.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | /// The dependencies needed from the parent scope of Root to provide for the LoggedOut scope.
12 | // TODO: Update RootDependency protocol to inherit this protocol.
13 | protocol RootDependencyLoggedOut: Dependency {
14 |
15 | // TODO: Declare dependencies needed from the parent scope of Root to provide dependencies
16 | // for the LoggedOut scope.
17 | }
18 |
19 | extension RootComponent: LoggedOutDependency {
20 | // TODO: Implement properties to provide for LoggedOut scope.
21 | }
22 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Root/RootInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootInteractor.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | protocol RootRouting: ViewableRouting {
13 | func routeToLoggedIn(sessionToken: String) -> LoggedInActionableItem
14 | func routeToLoggedOut()
15 | }
16 |
17 | protocol RootPresentable: Presentable {
18 | var listener: RootPresentableListener? { get set }
19 | // TODO: Declare methods the interactor can invoke the presenter to present data.
20 | }
21 |
22 | protocol RootListener: class {
23 | // TODO: Declare methods the interactor can invoke to communicate with other RIBs.
24 | }
25 |
26 | final class RootInteractor: PresentableInteractor, RootInteractable, RootPresentableListener,
27 | RootActionableItem, UrlHandler {
28 |
29 | weak var router: RootRouting?
30 | weak var listener: RootListener?
31 |
32 | private var isFirstTimeLaunch: Bool = true
33 |
34 | // TODO: Add additional dependencies to constructor. Do not perform any logic
35 | // in constructor.
36 | override init(presenter: RootPresentable) {
37 | super.init(presenter: presenter)
38 |
39 | presenter.listener = self
40 | }
41 |
42 | override func didBecomeActive() {
43 | super.didBecomeActive()
44 | // TODO: Implement business logic here.
45 | }
46 |
47 | override func willResignActive() {
48 | super.willResignActive()
49 | // TODO: Pause any business logic.
50 | }
51 |
52 | private func nextAction(with session: String, animated: Bool) {
53 | let loggedInActionableItem = router?.routeToLoggedIn(sessionToken: session)
54 |
55 | if let loggedInActionableItem = loggedInActionableItem {
56 | loggedInActionableItemSubject.onNext(loggedInActionableItem)
57 | }
58 | }
59 |
60 | // MARK: - RootPresentableListener
61 |
62 | func viewDidAppear() {
63 | if isFirstTimeLaunch {
64 | isFirstTimeLaunch = false
65 |
66 | router?.routeToLoggedOut()
67 | }
68 | }
69 |
70 | // MARK: - LoggedOutListener
71 |
72 | func didLogin(sessionToken: String) {
73 | let loggedInActionableItem = router?.routeToLoggedIn(sessionToken: sessionToken)
74 |
75 | if let loggedInActionableItem = loggedInActionableItem {
76 | loggedInActionableItemSubject.onNext(loggedInActionableItem)
77 | }
78 | }
79 |
80 | // MARK: - LoggedInListener
81 |
82 | func didFinishLoggedInJob() {
83 | router?.routeToLoggedOut()
84 | }
85 |
86 | // MARK: - UrlHandler
87 |
88 | func handle(_ url: URL) -> Bool{
89 | let deeplinkFlow = DeeplinkWorkflow(url: url)
90 |
91 | deeplinkFlow.subscribe(self).disposeOnDeactivate(interactor: self)
92 |
93 | return true
94 | }
95 |
96 | // MARK: - RootActionableItem
97 |
98 | func waitForLogin() -> Observable<(LoggedInActionableItem, ())> {
99 | return loggedInActionableItemSubject
100 | .map { (loggedInItem: LoggedInActionableItem) -> (LoggedInActionableItem, ()) in
101 | (loggedInItem, ())
102 | }
103 | }
104 |
105 | // MARK: - Private
106 |
107 | private let loggedInActionableItemSubject = ReplaySubject.create(bufferSize: 1)
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Root/RootRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootRouter.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | protocol RootInteractable: Interactable, LoggedOutListener, LoggedInListener {
12 | var router: RootRouting? { get set }
13 | var listener: RootListener? { get set }
14 | }
15 |
16 | protocol RootViewControllable: ViewControllable {
17 | func replaceModal(viewController: ViewControllable?)
18 | }
19 |
20 | final class RootRouter: LaunchRouter, RootRouting {
21 | // MARK: - Private properties
22 |
23 | private let loggedOutBuilder: LoggedOutBuildable
24 | private let loggedInBuilder: LoggedInBuildable
25 |
26 | private var loggedIn: (router: LoggedInRouting, actionableItem: LoggedInActionableItem)?
27 | private var loggedOut: ViewableRouting?
28 |
29 | init(interactor: RootInteractable,
30 | viewController: RootViewControllable,
31 | loggedOutBuilder: LoggedOutBuildable,
32 | loggedInBuilder: LoggedInBuildable) {
33 |
34 | self.loggedOutBuilder = loggedOutBuilder
35 | self.loggedInBuilder = loggedInBuilder
36 |
37 | super.init(interactor: interactor, viewController: viewController)
38 |
39 | interactor.router = self
40 | }
41 |
42 | override func didLoad() {
43 | super.didLoad()
44 |
45 | // If need to start with login screen:
46 | //routeToLoggedOut()
47 | }
48 |
49 | func routeToLoggedIn(sessionToken: String) -> LoggedInActionableItem {
50 | // Detach logged out.
51 | if let child = self.loggedOut {
52 | detachChild(child)
53 | viewController.replaceModal(viewController: nil)
54 |
55 | self.loggedOut = nil
56 | }
57 |
58 | loggedIn = loggedInBuilder.build(withListener: interactor, session: sessionToken)
59 |
60 | guard let loggedIn = self.loggedIn else { fatalError("failed to allocate rib") }
61 |
62 | attachChild(loggedIn.router)
63 |
64 | return loggedIn.actionableItem
65 | }
66 |
67 | func routeToLoggedOut() {
68 | // Detach loggedIn out.
69 | if let child = loggedIn {
70 | detachChild(child.router)
71 | }
72 |
73 | if loggedOut == nil {
74 | loggedOut = loggedOutBuilder.build(withListener: interactor)
75 | }
76 |
77 | guard let loggedOut = self.loggedOut else { fatalError("failed to allocate rib") }
78 |
79 | attachChild(loggedOut)
80 | viewController.replaceModal(viewController: loggedOut.viewControllable)
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Root/RootViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewController.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import SnapKit
11 | import UIKit
12 |
13 | protocol RootPresentableListener: class {
14 | func viewDidAppear()
15 | }
16 |
17 | final class RootViewController: UIViewController, RootPresentable, RootViewControllable {
18 |
19 | weak var listener: RootPresentableListener?
20 |
21 | init() {
22 | super.init(nibName: nil, bundle: nil)
23 | }
24 |
25 | required init?(coder aDecoder: NSCoder) {
26 | fatalError("Method is not supported")
27 | }
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 |
32 | view.backgroundColor = UIColor.white
33 | }
34 |
35 | override func viewDidAppear(_ animated: Bool) {
36 | super.viewDidAppear(animated)
37 |
38 | listener?.viewDidAppear()
39 | }
40 |
41 | // MARK: - RootViewControllable
42 |
43 | func replaceModal(viewController: ViewControllable?) {
44 | targetViewController = viewController
45 |
46 | guard !animationInProgress else { return }
47 |
48 | if presentedViewController != nil {
49 | animationInProgress = true
50 | dismiss(animated: true) { [weak self] in
51 | guard let this = self else { return }
52 |
53 | if this.targetViewController != nil {
54 | this.presentTargetViewController()
55 | } else {
56 | this.animationInProgress = false
57 | }
58 | }
59 | } else {
60 | presentTargetViewController()
61 | }
62 | }
63 |
64 | // MARK: - Private
65 |
66 | private var targetViewController: ViewControllable?
67 | private var animationInProgress = false
68 |
69 | private func presentTargetViewController() {
70 | if let targetViewController = targetViewController {
71 | animationInProgress = true
72 | present(targetViewController.uiviewController, animated: true) { [weak self] in
73 | self?.animationInProgress = false
74 | }
75 | }
76 | }
77 | }
78 |
79 | // MARK: LoggedInViewControllable
80 |
81 | extension RootViewController: LoggedInViewControllable {
82 | }
83 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Workflows/DeeplinkWorkflow/DeeplinkWorkflow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DeeplinkWorkflow.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 | import RxSwift
11 |
12 | public class DeeplinkWorkflow: Workflow {
13 | enum FlowType {
14 | case task(String)
15 | case blankTask
16 | case hi
17 | }
18 |
19 | public init(url: URL) {
20 | super.init()
21 |
22 | let flowType = parseDeeplinkData(from: url)
23 |
24 | self
25 | .onStep { (rootItem: RootActionableItem) -> Observable<(LoggedInActionableItem, ())> in
26 | rootItem.waitForLogin()
27 | }
28 | .onStep { (loggedInItem: LoggedInActionableItem, _) -> Observable<(DrawerActionableItem, ())> in
29 | loggedInItem.waitForDrawer()
30 | }
31 | .onStep { (drawerItem: DrawerActionableItem, _) -> Observable<(ListActionableItem, ())> in
32 | switch flowType {
33 | case .hi:
34 | return drawerItem.sayHi()
35 | default:
36 | return drawerItem.waitForList()
37 | }
38 | }
39 | .onStep { (listItem: ListActionableItem, _) -> Observable<(ListActionableItem, ())> in
40 | switch flowType {
41 | case .blankTask:
42 | return listItem.createBlankTask()
43 | case .task(let details):
44 | return listItem.createTask(with: details)
45 | case .hi:
46 | return listItem.doNothing()
47 | }
48 | }
49 | .commit()
50 | }
51 |
52 | private func parseDeeplinkData(from url: URL) -> FlowType {
53 | guard let host = url.host else { return .blankTask }
54 |
55 | let value = url.path.replacingOccurrences(of: "/", with: "")
56 |
57 | if host == "task" {
58 | return .task(value)
59 | } else if host == "sayhi" {
60 | return .hi
61 | }
62 |
63 | return .blankTask
64 | }
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/ToDo/RIBs/Workflows/RootWorkflow/RootWorkflow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootWorkflow.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import RIBs
10 |
11 | public typealias RootWorkflow = Workflow
12 |
--------------------------------------------------------------------------------
/ToDo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/ToDo/Resources/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 |
--------------------------------------------------------------------------------
/ToDo/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleTypeRole
23 | Editor
24 | CFBundleURLName
25 | au.com.dev4jam-todo-ios
26 | CFBundleURLSchemes
27 |
28 | todo
29 |
30 |
31 |
32 | CFBundleVersion
33 | 1
34 | LSRequiresIPhoneOS
35 |
36 | UILaunchStoryboardName
37 | LaunchScreen
38 | UIRequiredDeviceCapabilities
39 |
40 | armv7
41 |
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/ToDo/Services/ToDoService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToDoService.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum ToDoServiceError: Error {
12 | case failedToGetItems(message: String)
13 | case failedToGetItemDetails(message: String)
14 |
15 | var message: String {
16 | switch self {
17 | case .failedToGetItems(let message),
18 | .failedToGetItemDetails(let message):
19 |
20 | return message
21 | }
22 | }
23 | }
24 |
25 | enum ToDoServiceListResponse {
26 | case error(ToDoServiceError)
27 | case success(items: [Item])
28 | }
29 |
30 | enum ToDoServiceAuthResponse {
31 | case error(ToDoServiceError)
32 | case success(sessionToken: String)
33 | }
34 |
35 | typealias ToDoServiceItemsCallback = ((ToDoServiceListResponse) -> Void)
36 | typealias ToDoServiceAuthCallback = ((ToDoServiceAuthResponse) -> Void)
37 |
38 | protocol ToDoServiceProtocol: class {
39 | func requestAccessToken(login: String, password: String, callback: @escaping ToDoServiceAuthCallback)
40 | func getAllItems(callback: @escaping ToDoServiceItemsCallback)
41 | func getIncompleteItems(callback: @escaping ToDoServiceItemsCallback)
42 | func getCompletedItems(callback: @escaping ToDoServiceItemsCallback)
43 | }
44 |
45 | final class ToDoService: ToDoServiceProtocol {
46 | private let fakeItems: [Item] = [
47 | Item(id: Date().timestampInt, createdAt: Date(), details: "Run a project", isComplete: false),
48 | Item(id: Date().timestampInt, createdAt: Date(), details: "Add new RIB", isComplete: false),
49 | Item(id: Date().timestampInt, createdAt: Date(), details: "Wire new RIB", isComplete: false),
50 | Item(id: Date().timestampInt, createdAt: Date(), details: "Schedule a session", isComplete: true),
51 | ]
52 |
53 | func requestAccessToken(login: String, password: String,
54 | callback: @escaping ToDoServiceAuthCallback) {
55 |
56 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
57 | callback(.success(sessionToken: "owheoasdhfalsdif"))
58 | }
59 | }
60 |
61 | func getAllItems(callback: @escaping ToDoServiceItemsCallback) {
62 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
63 | callback(.success(items: self.fakeItems))
64 | }
65 | }
66 |
67 | func getIncompleteItems(callback: @escaping ToDoServiceItemsCallback) {
68 | let incompleteItems = fakeItems.filter { !$0.isComplete }
69 |
70 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
71 | callback(.success(items: incompleteItems))
72 | }
73 | }
74 |
75 | func getCompletedItems(callback: @escaping ToDoServiceItemsCallback) {
76 | let incompleteItems = fakeItems.filter { $0.isComplete }
77 |
78 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
79 | callback(.success(items: incompleteItems))
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/ToDo/Utils/Date.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public extension Date {
12 | public var timestamp: String {
13 | return String(format: "%.0f", timeIntervalSince1970 * 1000)
14 | }
15 |
16 | public var timestampInt: Int {
17 | return Int(round(timeIntervalSince1970 * 1000))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ToDo/Views/ItemCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemCell.swift
3 | // ToDo
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | final class ItemCell: UITableViewCell {
12 | @IBOutlet private weak var detailsLabel: UILabel!
13 | @IBOutlet private weak var timeLabel: UILabel!
14 | @IBOutlet private weak var statusIndicator: UIView!
15 |
16 | override func awakeFromNib() {
17 | super.awakeFromNib()
18 | // Initialization code
19 | }
20 |
21 | func display(item: ItemViewModel) {
22 | detailsLabel.text = item.details
23 | timeLabel.text = item.createdAt
24 | statusIndicator.backgroundColor = item.statusColor
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ToDo/Views/ItemCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
31 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/ToDoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ToDoTests/ToDoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToDoTests.swift
3 | // ToDoTests
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ToDo
11 |
12 | class ToDoTests: 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.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ToDoUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ToDoUITests/ToDoUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToDoUITests.swift
3 | // ToDoUITests
4 | //
5 | // Created by Dmitry Klimkin on 1/2/18.
6 | // Copyright © 2018 Dev4Jam. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class ToDoUITests: 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 |
--------------------------------------------------------------------------------