├── README.md ├── VIPER-SWIFT ├── Images.xcassets │ ├── line.imageset │ │ ├── line.png │ │ ├── line@2x.png │ │ └── Contents.json │ ├── alarm.imageset │ │ ├── alarm.png │ │ ├── alarm@2x.png │ │ └── Contents.json │ ├── check.imageset │ │ ├── check.png │ │ ├── check@2x.png │ │ └── Contents.json │ ├── circle.imageset │ │ ├── circle.png │ │ ├── circle@2x.png │ │ └── Contents.json │ ├── empty.imageset │ │ ├── empty.png │ │ ├── empty@2x.png │ │ └── Contents.json │ ├── month.imageset │ │ ├── month.png │ │ ├── month@2x.png │ │ └── Contents.json │ ├── notes.imageset │ │ ├── notes.png │ │ ├── notes@2x.png │ │ └── Contents.json │ ├── paper.imageset │ │ ├── paper.png │ │ ├── paper@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Icon@2x.png │ │ └── Contents.json │ ├── calendar.imageset │ │ ├── calendar.png │ │ ├── calendar@2x.png │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ ├── Splash-4.png │ │ ├── Splash-4@2x.png │ │ ├── LaunchImage@2x.png │ │ └── Contents.json ├── Classes │ ├── Common │ │ ├── Clock │ │ │ ├── Clock.swift │ │ │ └── DeviceClock.swift │ │ ├── Model │ │ │ ├── TodoItem.swift │ │ │ └── NearTermDateRelation.swift │ │ ├── Store │ │ │ ├── Entities │ │ │ │ └── ManagedTodoItem.swift │ │ │ ├── TODO.xcdatamodeld │ │ │ │ └── TODO.xcdatamodel │ │ │ │ │ └── contents │ │ │ └── CoreDataStore.swift │ │ ├── View │ │ │ └── RootWireframe.swift │ │ └── Categories │ │ │ └── NSCalendar+CalendarAdditions.swift │ ├── Modules │ │ ├── List │ │ │ ├── Module Interface │ │ │ │ └── ListModuleInterface.swift │ │ │ ├── User Interface │ │ │ │ ├── View │ │ │ │ │ ├── ListViewInterface.swift │ │ │ │ │ └── ListViewController.swift │ │ │ │ ├── Presenter │ │ │ │ │ ├── UpcomingDisplayData.swift │ │ │ │ │ ├── UpcomingDisplaySection.swift │ │ │ │ │ ├── UpcomingDisplayItem.swift │ │ │ │ │ ├── ListPresenter.swift │ │ │ │ │ └── UpcomingDisplayDataCollection.swift │ │ │ │ └── Wireframe │ │ │ │ │ └── ListWireframe.swift │ │ │ └── Application Logic │ │ │ │ ├── Interactor │ │ │ │ ├── ListInteractorIO.swift │ │ │ │ ├── UpcomingItem.swift │ │ │ │ └── ListInteractor.swift │ │ │ │ └── Manager │ │ │ │ └── ListDataManager.swift │ │ └── Add │ │ │ ├── Module Interface │ │ │ ├── AddModuleDelegateInterface.swift │ │ │ └── AddModuleInterface.swift │ │ │ ├── User Interface │ │ │ ├── View │ │ │ │ ├── AddViewInterface.swift │ │ │ │ └── AddViewController.swift │ │ │ ├── Presenter │ │ │ │ └── AddPresenter.swift │ │ │ ├── Transition │ │ │ │ ├── AddDismissalTransition.swift │ │ │ │ └── AddPresentationTransition.swift │ │ │ └── Wireframe │ │ │ │ └── AddWireframe.swift │ │ │ └── Application Logic │ │ │ ├── Interactor │ │ │ └── AddInteractor.swift │ │ │ └── Manager │ │ │ └── AddDataManager.swift │ ├── AppDelegate.swift │ └── AppDependencies.swift ├── Info.plist └── Base.lproj │ └── Main.storyboard ├── VIPER-SWIFT.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── .gitignore ├── VIPER-SWIFTTests ├── Info.plist ├── DataTests.swift ├── CalendarTests.swift └── RelativeDateTests.swift └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | VIPER-SWIFT 2 | =========== 3 | 4 | An example Todo list app written in Swift using the VIPER architecture. 5 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/line.imageset/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/line.imageset/line.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/check.imageset/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/check.imageset/check.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/circle.imageset/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/circle.imageset/circle.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/empty.imageset/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/empty.imageset/empty.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/line.imageset/line@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/line.imageset/line@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/month.imageset/month.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/month.imageset/month.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/notes.imageset/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/notes.imageset/notes.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/paper.imageset/paper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/paper.imageset/paper.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/check.imageset/check@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/check.imageset/check@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/empty.imageset/empty@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/empty.imageset/empty@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/month.imageset/month@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/month.imageset/month@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/notes.imageset/notes@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/notes.imageset/notes@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/paper.imageset/paper@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/paper.imageset/paper@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/circle.imageset/circle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/circle.imageset/circle@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/Splash-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/Splash-4.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/Splash-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/Splash-4@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutualmobile/VIPER-SWIFT/HEAD/VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/LaunchImage@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | 18 | /Build 19 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Clock/Clock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Clock.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Clock { 12 | func today() -> Date 13 | } 14 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Model/TodoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TodoItem.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TodoItem { 12 | let dueDate : Date 13 | let name : String 14 | } 15 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Clock/DeviceClock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceClock.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class DeviceClock : NSObject, Clock { 12 | func today() -> Date { 13 | return Date() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/Module Interface/ListModuleInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListModuleInterface.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ListModuleInterface { 12 | func addNewEntry() 13 | func updateView() 14 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/line.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "line.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "line@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/alarm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "alarm.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "alarm@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "check.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "check@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/empty.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "empty.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "empty@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/month.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "month.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "month@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/notes.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "notes.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "notes@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/paper.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "paper.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "paper@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/circle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "circle.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "circle@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/calendar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "calendar.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "calendar@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Store/Entities/ManagedTodoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManagedTodoItem.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Conrad Stoll. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class ManagedTodoItem : NSManagedObject { 13 | @NSManaged var name : String 14 | @NSManaged var date : Date 15 | } 16 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/Module Interface/AddModuleDelegateInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddModuleDelegateInterface.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol AddModuleDelegate { 12 | func addModuleDidCancelAddAction() 13 | func addModuleDidSaveAddAction() 14 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/Module Interface/AddModuleInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddModuleInterface.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol AddModuleInterface { 12 | func cancelAddAction() 13 | func saveAddActionWithName(_ name: String, dueDate: Date) 14 | } 15 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Model/NearTermDateRelation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NearTermDateRelation.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum NearTermDateRelation { 12 | case outOfRange, 13 | today, 14 | tomorrow, 15 | laterThisWeek, 16 | nextWeek 17 | } 18 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/User Interface/View/AddViewInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddViewInterface.swift 3 | // VIPER TODO 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol AddViewInterface { 12 | func setEntryName(_ name: String) 13 | func setEntryDueDate(_ date: Date) 14 | func setMinimumDueDate(_ date: Date) 15 | } 16 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/View/ListViewInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListViewInterface.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ListViewInterface { 12 | func showNoContentMessage() 13 | func showUpcomingDisplayData(_ data: UpcomingDisplayData) 14 | func reloadEntries() 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/Application Logic/Interactor/ListInteractorIO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListInteractorIO.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | protocol ListInteractorInput { 13 | func findUpcomingItems() 14 | } 15 | 16 | protocol ListInteractorOutput { 17 | func foundUpcomingItems(_ upcomingItems: [UpcomingItem]) 18 | } 19 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "size" : "60x60", 15 | "idiom" : "iphone", 16 | "filename" : "Icon@2x.png", 17 | "scale" : "2x" 18 | } 19 | ], 20 | "info" : { 21 | "version" : 1, 22 | "author" : "xcode" 23 | } 24 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/Application Logic/Interactor/AddInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddInteractor.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class AddInteractor : NSObject { 12 | var addDataManager : AddDataManager? 13 | 14 | func saveNewEntryWithName(_ name: String, dueDate: Date) { 15 | let newEntry = TodoItem(dueDate: dueDate, name: name) 16 | addDataManager?.addNewEntry(newEntry) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/Presenter/UpcomingDisplayData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpcomingDisplayData.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UpcomingDisplayData { 12 | let sections : [UpcomingDisplaySection] 13 | } 14 | 15 | extension UpcomingDisplayData : Equatable { 16 | static func == (leftSide: UpcomingDisplayData, rightSide: UpcomingDisplayData) -> Bool { 17 | return rightSide.sections == leftSide.sections 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/Application Logic/Manager/AddDataManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddDataManager.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class AddDataManager : NSObject { 12 | var dataStore : CoreDataStore! 13 | 14 | func addNewEntry(_ entry: TodoItem) { 15 | let newEntry = dataStore.newTodoItem() 16 | newEntry.name = entry.name 17 | newEntry.date = entry.dueDate; 18 | 19 | dataStore.save() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/Presenter/UpcomingDisplaySection.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // UpcomingDisplaySection.swift 4 | // VIPER-SWIFT 5 | // 6 | // Created by Conrad Stoll on 6/5/14. 7 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | struct UpcomingDisplaySection { 13 | let name : String 14 | let imageName : String 15 | var items : [UpcomingDisplayItem] = [] 16 | } 17 | 18 | extension UpcomingDisplaySection : Equatable { 19 | static func == (leftSide: UpcomingDisplaySection, rightSide: UpcomingDisplaySection) -> Bool { 20 | return rightSide.items == leftSide.items 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Conrad Stoll. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | let appDependencies = AppDependencies() 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | appDependencies.installRootViewControllerIntoWindow(window!) 19 | 20 | return true 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Store/TODO.xcdatamodeld/TODO.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/View/RootWireframe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootWireframe.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class RootWireframe : NSObject { 13 | func showRootViewController(_ viewController: UIViewController, inWindow: UIWindow) { 14 | let navigationController = navigationControllerFromWindow(inWindow) 15 | navigationController.viewControllers = [viewController] 16 | } 17 | 18 | func navigationControllerFromWindow(_ window: UIWindow) -> UINavigationController { 19 | let navigationController = window.rootViewController as! UINavigationController 20 | return navigationController 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/Presenter/UpcomingDisplayItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpcomingDisplayItem.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UpcomingDisplayItem { 12 | let title : String 13 | let dueDate : String 14 | } 15 | 16 | extension UpcomingDisplayItem : Equatable { 17 | static func == (leftSide: UpcomingDisplayItem, rightSide: UpcomingDisplayItem) -> Bool { 18 | return rightSide.title == leftSide.title && rightSide.dueDate == rightSide.dueDate 19 | } 20 | } 21 | 22 | extension UpcomingDisplayItem: CustomStringConvertible { 23 | var description : String { 24 | return "\(title) -- \(dueDate)" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /VIPER-SWIFTTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /VIPER-SWIFTTests/DataTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataStoreTests.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/6/14. 6 | // Copyright (c) 2014 Conrad Stoll. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class DataTests: XCTestCase { 12 | var dataStore = CoreDataStore() 13 | var dataManager = ListDataManager() 14 | 15 | override func setUp() { 16 | super.setUp() 17 | 18 | dataManager.coreDataStore = dataStore 19 | } 20 | 21 | func testPerformance() { 22 | self.measure() { 23 | let startDate = Date() 24 | let endDate = Date(timeIntervalSinceReferenceDate: 0) 25 | self.dataManager.todoItemsBetweenStartDate(startDate, endDate: endDate, completion: { entries in 26 | 27 | }) 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "Splash-4@2x.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "LaunchImage@2x.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "iphone", 23 | "extent" : "full-screen", 24 | "filename" : "Splash-4.png", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "subtype" : "retina4", 32 | "scale" : "2x" 33 | } 34 | ], 35 | "info" : { 36 | "version" : 1, 37 | "author" : "xcode" 38 | } 39 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/User Interface/Presenter/AddPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPresenter.swift 3 | // VIPER TODO 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class AddPresenter : NSObject, AddModuleInterface { 12 | var addInteractor : AddInteractor? 13 | var addWireframe : AddWireframe? 14 | var addModuleDelegate : AddModuleDelegate? 15 | 16 | func configureUserInterfaceForPresentation(_ addViewUserInterface: AddViewInterface) { 17 | addViewUserInterface.setMinimumDueDate(Date()) 18 | } 19 | 20 | // MARK: AddModuleInterface 21 | 22 | func cancelAddAction() { 23 | addWireframe?.dismissAddInterface() 24 | addModuleDelegate?.addModuleDidCancelAddAction() 25 | } 26 | 27 | func saveAddActionWithName(_ name: String, dueDate: Date) { 28 | addInteractor?.saveNewEntryWithName(name, dueDate: dueDate); 29 | addWireframe?.dismissAddInterface() 30 | addModuleDelegate?.addModuleDidSaveAddAction() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mutual Mobile 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. -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/Application Logic/Interactor/UpcomingItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpcomingItem.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UpcomingItem : Equatable { 12 | let title : String 13 | let dueDate : Date 14 | let dateRelation : NearTermDateRelation 15 | 16 | init(title: String, dueDate: Date = Date(), dateRelation: NearTermDateRelation = NearTermDateRelation.outOfRange) { 17 | self.title = title 18 | self.dueDate = dueDate 19 | self.dateRelation = dateRelation 20 | } 21 | } 22 | 23 | func == (leftSide: UpcomingItem, rightSide: UpcomingItem) -> Bool { 24 | var hasEqualSections = false 25 | hasEqualSections = rightSide.title == leftSide.title 26 | 27 | if hasEqualSections == false { 28 | return false 29 | } 30 | 31 | hasEqualSections = rightSide.dueDate == rightSide.dueDate 32 | 33 | if hasEqualSections == false { 34 | return false 35 | } 36 | 37 | hasEqualSections = rightSide.dateRelation == rightSide.dateRelation 38 | 39 | return hasEqualSections 40 | } 41 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarStyle 34 | UIStatusBarStyleLightContent 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/Application Logic/Manager/ListDataManager.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // ListDataManager.swift 4 | // VIPER-SWIFT 5 | // 6 | // Created by Conrad Stoll on 6/5/14. 7 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | class ListDataManager : NSObject { 13 | var coreDataStore : CoreDataStore? 14 | 15 | func todoItemsBetweenStartDate(_ startDate: Date, endDate: Date, completion: (([TodoItem]) -> Void)!) { 16 | let calendar = Calendar.autoupdatingCurrent 17 | let beginning = calendar.dateForBeginningOfDay(startDate) 18 | let end = calendar.dateForEndOfDay(endDate) 19 | 20 | let predicate = NSPredicate(format: "(date >= %@) AND (date <= %@)", beginning as CVarArg, end as CVarArg) 21 | let sortDescriptors: [NSSortDescriptor] = [] 22 | 23 | coreDataStore?.fetchEntriesWithPredicate(predicate, 24 | sortDescriptors: sortDescriptors, 25 | completionBlock: { entries in 26 | let todoItems = self.todoItemsFromDataStoreEntries(entries) 27 | completion(todoItems) 28 | }) 29 | } 30 | 31 | func todoItemsFromDataStoreEntries(_ entries: [ManagedTodoItem]) -> [TodoItem] { 32 | let todoItems : [TodoItem] = entries.map { entry in 33 | TodoItem(dueDate: entry.date, name: entry.name as String) 34 | } 35 | 36 | return todoItems 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/User Interface/Transition/AddDismissalTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddDismissalTransition.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class AddDismissalTransition : NSObject, UIViewControllerAnimatedTransitioning { 13 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 14 | return 0.72 15 | } 16 | 17 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 18 | let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! AddViewController 19 | 20 | let finalCenter = CGPoint(x: 160.0, y: (fromVC.view.bounds.size.height / 2) - 1000.0) 21 | 22 | let options = UIViewAnimationOptions.curveEaseIn 23 | 24 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), 25 | delay: 0.0, 26 | usingSpringWithDamping: 0.64, 27 | initialSpringVelocity: 0.22, 28 | options: options, 29 | animations: { 30 | fromVC.view.center = finalCenter 31 | fromVC.transitioningBackgroundView.alpha = 0.0 32 | }, 33 | completion: { finished in 34 | fromVC.view.removeFromSuperview() 35 | transitionContext.completeTransition(true) 36 | } 37 | ) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/Wireframe/ListWireframe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListWireframe.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | let ListViewControllerIdentifier = "ListViewController" 13 | 14 | class ListWireframe : NSObject { 15 | var addWireframe : AddWireframe? 16 | var listPresenter : ListPresenter? 17 | var rootWireframe : RootWireframe? 18 | var listViewController : ListViewController? 19 | 20 | func presentListInterfaceFromWindow(_ window: UIWindow) { 21 | let viewController = listViewControllerFromStoryboard() 22 | viewController.eventHandler = listPresenter 23 | listViewController = viewController 24 | listPresenter?.userInterface = viewController 25 | rootWireframe?.showRootViewController(viewController, inWindow: window) 26 | } 27 | 28 | func presentAddInterface() { 29 | addWireframe?.presentAddInterfaceFromViewController(listViewController!) 30 | } 31 | 32 | func listViewControllerFromStoryboard() -> ListViewController { 33 | let storyboard = mainStoryboard() 34 | let viewController = storyboard.instantiateViewController(withIdentifier: ListViewControllerIdentifier) as! ListViewController 35 | return viewController 36 | } 37 | 38 | func mainStoryboard() -> UIStoryboard { 39 | let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main) 40 | return storyboard 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/Application Logic/Interactor/ListInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListInteractor.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ListInteractor : NSObject, ListInteractorInput { 12 | var output : ListInteractorOutput? 13 | 14 | let clock : Clock 15 | let dataManager : ListDataManager 16 | 17 | init(dataManager: ListDataManager, clock: Clock) { 18 | self.dataManager = dataManager 19 | self.clock = clock 20 | } 21 | 22 | // MARK: ListInteractorInput 23 | 24 | func findUpcomingItems() { 25 | let today = clock.today() 26 | let endOfNextWeek = Calendar.current.dateForEndOfFollowingWeekWithDate(today) 27 | 28 | dataManager.todoItemsBetweenStartDate(today, 29 | endDate: endOfNextWeek, 30 | completion: { todoItems in 31 | let upcomingItems = self.upcomingItemsFromToDoItems(todoItems) 32 | self.output?.foundUpcomingItems(upcomingItems) 33 | }) 34 | } 35 | 36 | func upcomingItemsFromToDoItems(_ todoItems: [TodoItem]) -> [UpcomingItem] { 37 | let calendar = Calendar.autoupdatingCurrent 38 | 39 | let upcomingItems: [UpcomingItem] = todoItems.map() { todoItem in 40 | let dateRelation = calendar.nearTermRelationForDate(todoItem.dueDate, relativeToToday: clock.today()) 41 | return UpcomingItem(title: todoItem.name, dueDate: todoItem.dueDate, dateRelation: dateRelation) 42 | } 43 | return upcomingItems 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/Presenter/ListPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListPresenter.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class ListPresenter : NSObject, ListInteractorOutput, ListModuleInterface, AddModuleDelegate { 13 | var listInteractor : ListInteractorInput? 14 | var listWireframe : ListWireframe? 15 | var userInterface : ListViewInterface? 16 | 17 | // MARK: ListInteractorOutput 18 | 19 | func foundUpcomingItems(_ upcomingItems: [UpcomingItem]) { 20 | if upcomingItems.count == 0 { 21 | userInterface?.showNoContentMessage() 22 | } else { 23 | updateUserInterfaceWithUpcomingItems(upcomingItems) 24 | } 25 | } 26 | 27 | func updateUserInterfaceWithUpcomingItems(_ upcomingItems: [UpcomingItem]) { 28 | let upcomingDisplayData = upcomingDisplayDataWithItems(upcomingItems) 29 | userInterface?.showUpcomingDisplayData(upcomingDisplayData) 30 | } 31 | 32 | func upcomingDisplayDataWithItems(_ upcomingItems: [UpcomingItem]) -> UpcomingDisplayData { 33 | let collection = UpcomingDisplayDataCollection() 34 | collection.addUpcomingItems(upcomingItems) 35 | return collection.collectedDisplayData() 36 | } 37 | 38 | // MARK: ListModuleInterface 39 | 40 | func addNewEntry() { 41 | listWireframe?.presentAddInterface() 42 | } 43 | 44 | func updateView() { 45 | listInteractor?.findUpcomingItems() 46 | } 47 | 48 | // MARK: AddModuleDelegate 49 | 50 | func addModuleDidCancelAddAction() { 51 | // No action necessary 52 | } 53 | 54 | func addModuleDidSaveAddAction() { 55 | updateView() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/AppDependencies.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDependencies.swift 3 | // VIPER TODO 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class AppDependencies { 13 | var listWireframe = ListWireframe() 14 | 15 | init() { 16 | configureDependencies() 17 | } 18 | 19 | func installRootViewControllerIntoWindow(_ window: UIWindow) { 20 | listWireframe.presentListInterfaceFromWindow(window) 21 | } 22 | 23 | func configureDependencies() { 24 | let coreDataStore = CoreDataStore() 25 | let clock = DeviceClock() 26 | let rootWireframe = RootWireframe() 27 | 28 | let listPresenter = ListPresenter() 29 | let listDataManager = ListDataManager() 30 | let listInteractor = ListInteractor(dataManager: listDataManager, clock: clock) 31 | 32 | let addWireframe = AddWireframe() 33 | let addInteractor = AddInteractor() 34 | let addPresenter = AddPresenter() 35 | let addDataManager = AddDataManager() 36 | 37 | listInteractor.output = listPresenter 38 | 39 | listPresenter.listInteractor = listInteractor 40 | listPresenter.listWireframe = listWireframe 41 | 42 | listWireframe.addWireframe = addWireframe 43 | listWireframe.listPresenter = listPresenter 44 | listWireframe.rootWireframe = rootWireframe 45 | 46 | listDataManager.coreDataStore = coreDataStore 47 | 48 | addInteractor.addDataManager = addDataManager 49 | 50 | addWireframe.addPresenter = addPresenter 51 | 52 | addPresenter.addWireframe = addWireframe 53 | addPresenter.addModuleDelegate = listPresenter 54 | addPresenter.addInteractor = addInteractor 55 | 56 | addDataManager.dataStore = coreDataStore 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/User Interface/Wireframe/AddWireframe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddWireframe.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | let AddViewControllerIdentifier = "AddViewController" 13 | 14 | class AddWireframe : NSObject, UIViewControllerTransitioningDelegate { 15 | 16 | var addPresenter : AddPresenter? 17 | var presentedViewController : UIViewController? 18 | 19 | func presentAddInterfaceFromViewController(_ viewController: UIViewController) { 20 | let newViewController = addViewController() 21 | newViewController.eventHandler = addPresenter 22 | newViewController.modalPresentationStyle = .custom 23 | newViewController.transitioningDelegate = self 24 | 25 | addPresenter?.configureUserInterfaceForPresentation(newViewController) 26 | 27 | viewController.present(newViewController, animated: true, completion: nil) 28 | 29 | presentedViewController = newViewController 30 | } 31 | 32 | func dismissAddInterface() { 33 | presentedViewController?.dismiss(animated: true, completion: nil) 34 | } 35 | 36 | func addViewController() -> AddViewController { 37 | let storyboard = mainStoryboard() 38 | let addViewController: AddViewController = storyboard.instantiateViewController(withIdentifier: AddViewControllerIdentifier) as! AddViewController 39 | return addViewController 40 | } 41 | 42 | func mainStoryboard() -> UIStoryboard { 43 | let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main) 44 | return storyboard 45 | } 46 | 47 | // MARK: UIViewControllerTransitioningDelegate 48 | 49 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 50 | return AddDismissalTransition() 51 | } 52 | 53 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 54 | return AddPresentationTransition() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/User Interface/Transition/AddPresentationTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddPresentationTransition.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class AddPresentationTransition: NSObject, UIViewControllerAnimatedTransitioning { 13 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 14 | return 0.72 15 | } 16 | 17 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 18 | let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! 19 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! AddViewController 20 | 21 | toVC.transitioningBackgroundView.backgroundColor = UIColor.darkGray 22 | toVC.transitioningBackgroundView.alpha = 0.0 23 | toVC.transitioningBackgroundView.frame = UIScreen.main.bounds 24 | 25 | let containerView = transitionContext.containerView 26 | 27 | containerView.addSubview(toVC.transitioningBackgroundView) 28 | containerView.addSubview(toVC.view) 29 | 30 | let toViewFrame = CGRect(x: 0, y: 0, width: 260, height: 300) 31 | toVC.view.frame = toViewFrame 32 | 33 | let finalCenter = CGPoint(x: fromVC.view.bounds.size.width / 2, y: 20 + toViewFrame.size.height / 2) 34 | toVC.view.center = CGPoint(x: finalCenter.x, y: finalCenter.y - 1000) 35 | 36 | let options = UIViewAnimationOptions.curveEaseIn 37 | 38 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), 39 | delay: 0.0, 40 | usingSpringWithDamping: 0.64, 41 | initialSpringVelocity: 0.22, 42 | options: options, 43 | animations: { 44 | toVC.view.center = finalCenter 45 | toVC.transitioningBackgroundView.alpha = 0.7 46 | }, 47 | completion: { finished in 48 | toVC.view.center = finalCenter 49 | transitionContext.completeTransition(true) 50 | } 51 | ) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/Add/User Interface/View/AddViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddViewController.swift 3 | // VIPER TODO 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class AddViewController: UIViewController, UITextFieldDelegate, AddViewInterface { 13 | var eventHandler : AddModuleInterface? 14 | 15 | @IBOutlet var nameTextField : UITextField! 16 | @IBOutlet var datePicker : UIDatePicker! 17 | 18 | var minimumDate : Date = Date() 19 | var transitioningBackgroundView : UIView = UIView() 20 | 21 | @IBAction func save(_ sender: AnyObject) { 22 | if let text = nameTextField.text { 23 | eventHandler?.saveAddActionWithName(text, dueDate: datePicker.date) 24 | } 25 | } 26 | 27 | @IBAction func cancel(_ sender: AnyObject) { 28 | nameTextField.resignFirstResponder() 29 | eventHandler?.cancelAddAction() 30 | } 31 | 32 | override func viewDidAppear(_ animated: Bool) { 33 | super.viewDidAppear(animated) 34 | 35 | let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(AddViewController.dismissFunc)) 36 | transitioningBackgroundView.isUserInteractionEnabled = true 37 | transitioningBackgroundView.addGestureRecognizer(gestureRecognizer) 38 | 39 | nameTextField.becomeFirstResponder() 40 | 41 | if let realDatePicker = datePicker { 42 | realDatePicker.minimumDate = minimumDate 43 | } 44 | } 45 | 46 | override func viewWillDisappear(_ animated: Bool) { 47 | super.viewWillDisappear(animated) 48 | 49 | nameTextField.resignFirstResponder() 50 | } 51 | 52 | @objc func dismissFunc() { 53 | eventHandler?.cancelAddAction() 54 | } 55 | 56 | // MARK: AddViewInterface 57 | 58 | func setEntryName(_ name: String) { 59 | nameTextField.text = name 60 | } 61 | 62 | func setEntryDueDate(_ date: Date) { 63 | if let realDatePicker = datePicker { 64 | realDatePicker.minimumDate = date 65 | } 66 | } 67 | 68 | func setMinimumDueDate(_ date: Date) { 69 | minimumDate = date 70 | 71 | if let realDatePicker = datePicker { 72 | realDatePicker.minimumDate = date 73 | } 74 | } 75 | 76 | // MARK: UITextFieldDelegate 77 | 78 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 79 | textField.resignFirstResponder() 80 | 81 | return true 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Store/CoreDataStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataStore.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/4/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class CoreDataStore : NSObject { 13 | var persistentStoreCoordinator : NSPersistentStoreCoordinator! 14 | var managedObjectModel : NSManagedObjectModel! 15 | var managedObjectContext : NSManagedObjectContext! 16 | 17 | override init() { 18 | managedObjectModel = NSManagedObjectModel.mergedModel(from: nil) 19 | 20 | persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) 21 | 22 | let domains = FileManager.SearchPathDomainMask.userDomainMask 23 | let directory = FileManager.SearchPathDirectory.documentDirectory 24 | 25 | let applicationDocumentsDirectory = FileManager.default.urls(for: directory, in: domains).first! 26 | let options = [NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true] 27 | 28 | let storeURL = applicationDocumentsDirectory.appendingPathComponent("VIPER-SWIFT.sqlite") 29 | 30 | try! persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: "", at: storeURL, options: options) 31 | 32 | managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType) 33 | managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator 34 | managedObjectContext.undoManager = nil 35 | 36 | super.init() 37 | } 38 | 39 | func fetchEntriesWithPredicate(_ predicate: NSPredicate, sortDescriptors: [NSSortDescriptor], completionBlock: (([ManagedTodoItem]) -> Void)!) { 40 | let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: "TodoItem") 41 | fetchRequest.predicate = predicate 42 | fetchRequest.sortDescriptors = sortDescriptors 43 | 44 | managedObjectContext.perform { 45 | let queryResults = try? self.managedObjectContext.fetch(fetchRequest) 46 | let managedResults = queryResults! as! [ManagedTodoItem] 47 | completionBlock(managedResults) 48 | } 49 | } 50 | 51 | func newTodoItem() -> ManagedTodoItem { 52 | let newEntry = NSEntityDescription.insertNewObject(forEntityName: "TodoItem", into: managedObjectContext) as! ManagedTodoItem 53 | 54 | return newEntry 55 | } 56 | 57 | func save() { 58 | do { 59 | try managedObjectContext.save() 60 | } catch _ { 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/View/ListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListViewController.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | var ListEntryCellIdentifier = "ListEntryCell" 13 | 14 | class ListViewController : UITableViewController, ListViewInterface { 15 | var eventHandler : ListModuleInterface? 16 | var dataProperty : UpcomingDisplayData? 17 | var strongTableView : UITableView? 18 | 19 | @IBOutlet var noContentView : UIView! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | strongTableView = tableView 25 | configureView() 26 | } 27 | 28 | override func viewWillAppear(_ animated: Bool) { 29 | super.viewWillAppear(animated) 30 | 31 | eventHandler?.updateView() 32 | } 33 | 34 | func configureView() { 35 | navigationItem.title = "VIPER TODO" 36 | 37 | let addItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.add, target: self, action: #selector(ListViewController.didTapAddButton)) 38 | 39 | navigationItem.rightBarButtonItem = addItem 40 | } 41 | 42 | @objc func didTapAddButton () { 43 | eventHandler?.addNewEntry() 44 | } 45 | 46 | // MARK: ListViewInterface 47 | 48 | func showNoContentMessage() { 49 | view = noContentView 50 | } 51 | 52 | func showUpcomingDisplayData(_ data: UpcomingDisplayData) { 53 | view = strongTableView 54 | 55 | dataProperty = data 56 | reloadEntries() 57 | } 58 | 59 | func reloadEntries() { 60 | tableView.reloadData() 61 | } 62 | 63 | // MARK: UITableViewDataSource 64 | 65 | override func numberOfSections(in tableView: UITableView) -> Int { 66 | return dataProperty?.sections.count ?? 0 67 | } 68 | 69 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 70 | let upcomingSection = dataProperty?.sections[section] 71 | return upcomingSection!.items.count 72 | } 73 | 74 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 75 | let upcomingSection = dataProperty?.sections[section] 76 | return upcomingSection!.name 77 | } 78 | 79 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 80 | let upcomingSection = dataProperty?.sections[(indexPath as NSIndexPath).section] 81 | let upcomingItem = upcomingSection!.items[(indexPath as NSIndexPath).row] 82 | 83 | let cell = tableView.dequeueReusableCell(withIdentifier: ListEntryCellIdentifier, for: indexPath) as UITableViewCell 84 | 85 | cell.textLabel?.text = upcomingItem.title; 86 | cell.detailTextLabel?.text = upcomingItem.dueDate; 87 | cell.imageView?.image = UIImage(named: upcomingSection!.imageName) 88 | cell.selectionStyle = UITableViewCellSelectionStyle.none; 89 | 90 | return cell 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /VIPER-SWIFTTests/CalendarTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalendarTests.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/6/14. 6 | // Copyright (c) 2014 Conrad Stoll. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class CalendarTests: XCTestCase { 12 | var calendar: Calendar! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | calendar = Calendar.gregorianCalendar() 18 | } 19 | 20 | func testEarlyYearMonthDayIsBeforeLaterYearMonthDay() { 21 | let earlyDate = calendar.dateWithYear(2004, month: 2, day: 29) 22 | let laterDate = calendar.dateWithYear(2004, month: 3, day: 1) 23 | let comparison = calendar.isDate(earlyDate, beforeYearMonthDay: laterDate) 24 | XCTAssert(comparison, "\(earlyDate) should be before \(laterDate)") 25 | } 26 | 27 | func testYearMonthDayIsNotBeforeSameYearMonthDay() { 28 | let earlyDate = calendar.dateWithYear(2005, month: 6, day: 1) 29 | let laterDate = calendar.dateWithYear(2005, month: 6, day: 1) 30 | let comparison = calendar.isDate(earlyDate, beforeYearMonthDay: laterDate) 31 | XCTAssertFalse(comparison, "\(earlyDate) should not be before \(laterDate)") 32 | } 33 | 34 | func testLaterYearMonthDayIsNotBeforeEarlyYearMonthDay() { 35 | let earlyDate = calendar.dateWithYear(2006, month: 4, day: 15) 36 | let laterDate = calendar.dateWithYear(2006, month: 4, day: 16) 37 | let comparison = calendar.isDate(laterDate, beforeYearMonthDay: earlyDate) 38 | XCTAssertFalse(comparison, "\(earlyDate) should not be before \(laterDate)") 39 | } 40 | 41 | func testEqualYearMonthDaysCompareAsEqual() { 42 | let earlyDate = calendar.dateWithYear(2005, month: 6, day: 1) 43 | let laterDate = calendar.dateWithYear(2005, month: 6, day: 1) 44 | let comparison = calendar.isDate(earlyDate, equalToYearMonthDay: laterDate) 45 | XCTAssert(comparison, "\(earlyDate) should equal \(laterDate)") 46 | } 47 | 48 | func testDifferentYearMonthDaysCompareAsNotEqual() { 49 | let earlyDate = calendar.dateWithYear(2005, month: 6, day: 1) 50 | let laterDate = calendar.dateWithYear(2005, month: 6, day: 2) 51 | let comparison = calendar.isDate(earlyDate, equalToYearMonthDay: laterDate) 52 | XCTAssertFalse(comparison, "\(earlyDate) should not equal \(laterDate)") 53 | } 54 | 55 | func testEndOfNextWeekDuringSameYear() { 56 | let date = calendar.dateWithYear(2005, month: 8, day: 2) 57 | let expectedNextWeek = calendar.dateWithYear(2005, month: 8, day: 13) 58 | let nextWeek = calendar.dateForEndOfFollowingWeekWithDate(date) 59 | let comparison = calendar.isDate(nextWeek, equalToYearMonthDay: expectedNextWeek) 60 | XCTAssert(comparison, "Next week should end on \(expectedNextWeek) (not \(nextWeek))") 61 | } 62 | 63 | func testEndOfNextWeekDuringFollowingYear() { 64 | let date = calendar.dateWithYear(2005, month: 12, day: 27) 65 | let expectedNextWeek = calendar.dateWithYear(2006, month: 1, day: 7) 66 | let nextWeek = calendar.dateForEndOfFollowingWeekWithDate(date) 67 | let comparison = calendar.isDate(nextWeek, equalToYearMonthDay: expectedNextWeek) 68 | XCTAssert(comparison, "Next week should end on \(expectedNextWeek) (not \(nextWeek))") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /VIPER-SWIFTTests/RelativeDateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelativeDateTests.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/6/14. 6 | // Copyright (c) 2014 Conrad Stoll. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class RelativeDateTests: XCTestCase { 12 | var calendar: Calendar! 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | calendar = Calendar.gregorianCalendar() 18 | } 19 | 20 | func testDateBeforeTodayIsOutOfBounds() { 21 | let date = calendar.dateWithYear(2000, month: 1, day: 1) 22 | let today = calendar.dateWithYear(2000, month: 1, day: 2) 23 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 24 | XCTAssertEqual(relation, NearTermDateRelation.outOfRange, "\(date) should be out of range.") 25 | } 26 | 27 | func testTodayRelatesAsToday() { 28 | let date = calendar.dateWithYear(2000, month: 1, day: 1) 29 | let today = calendar.dateWithYear(2000, month: 1, day: 1) 30 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 31 | XCTAssertEqual(relation, NearTermDateRelation.today, "\(date) should be today.") 32 | } 33 | 34 | func testTomorrowDuringTheSameWeekAsTodayRelatesAsTomorrow() { 35 | let date = calendar.dateWithYear(2004, month: 1, day: 3) 36 | let today = calendar.dateWithYear(2004, month: 1, day: 2) 37 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 38 | XCTAssertEqual(relation, NearTermDateRelation.tomorrow, "\(date) should be tomorrow.") 39 | } 40 | 41 | func testTomorrowDuringNextWeekRelatesAsNextWeek() { 42 | let date = calendar.dateWithYear(2004, month: 10, day: 10) 43 | let today = calendar.dateWithYear(2004, month: 10, day: 9) 44 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 45 | XCTAssertEqual(relation, NearTermDateRelation.nextWeek, "\(date) should be next week.") 46 | } 47 | 48 | func testDateAfterTomorrowButDuringTheSameWeekAsTodayRelatesAsLaterThisWeek() { 49 | let date = calendar.dateWithYear(2004, month: 3, day: 6) 50 | let today = calendar.dateWithYear(2004, month: 3, day: 2) 51 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 52 | XCTAssertEqual(relation, NearTermDateRelation.laterThisWeek, "\(date) should be later this week.") 53 | } 54 | 55 | func testDateDuringWeekAfterTodayRelatesAsNextWeek() { 56 | let date = calendar.dateWithYear(2004, month: 7, day: 11) 57 | let today = calendar.dateWithYear(2004, month: 7, day: 6) 58 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 59 | XCTAssertEqual(relation, NearTermDateRelation.nextWeek, "\(date) should be next week.") 60 | } 61 | 62 | func testDateDuringWeekAfterTodayThatFallsInNextYearRelatesAsNextWeek() { 63 | let date = calendar.dateWithYear(2006, month: 1, day: 2) 64 | let today = calendar.dateWithYear(2005, month: 12, day: 29) 65 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 66 | XCTAssertEqual(relation, NearTermDateRelation.nextWeek, "\(date) should be next week.") 67 | } 68 | 69 | func testDateBeyondNextWeekRelatesAsOutOfRange() { 70 | let date = calendar.dateWithYear(2005, month: 9, day: 25) 71 | let today = calendar.dateWithYear(2005, month: 9, day: 14) 72 | let relation = calendar.nearTermRelationForDate(date, relativeToToday: today) 73 | XCTAssertEqual(relation, NearTermDateRelation.outOfRange, "\(date) should be next week.") 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Modules/List/User Interface/Presenter/UpcomingDisplayDataCollection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpcomingDisplayDataCollection.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UpcomingDisplayDataCollection { 12 | let dayFormatter = DateFormatter() 13 | var sections : [NearTermDateRelation: [UpcomingDisplayItem]] = [:] 14 | 15 | init() { 16 | dayFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "EEEE", options: 0, locale: Locale.autoupdatingCurrent) 17 | } 18 | 19 | func addUpcomingItems(_ upcomingItems: [UpcomingItem]) { 20 | for upcomingItem in upcomingItems { 21 | addUpcomingItem(upcomingItem) 22 | } 23 | } 24 | 25 | func addUpcomingItem(_ upcomingItem: UpcomingItem) { 26 | let displayItem = displayItemForUpcomingItem(upcomingItem) 27 | addDisplayItem(displayItem, dateRelation: upcomingItem.dateRelation) 28 | } 29 | 30 | func addDisplayItem(_ displayItem: UpcomingDisplayItem, dateRelation: NearTermDateRelation) { 31 | if var realSection : [UpcomingDisplayItem] = sections[dateRelation] { 32 | realSection.append(displayItem) 33 | sections[dateRelation] = realSection 34 | } else { 35 | var newSection : [UpcomingDisplayItem] = [] 36 | newSection.append(displayItem) 37 | sections[dateRelation] = newSection 38 | } 39 | } 40 | 41 | func displayItemForUpcomingItem(_ upcomingItem: UpcomingItem) -> UpcomingDisplayItem { 42 | let day = formattedDay(upcomingItem.dueDate as Date, dateRelation: upcomingItem.dateRelation) 43 | let displayItem = UpcomingDisplayItem(title: upcomingItem.title, dueDate: day) 44 | return displayItem 45 | } 46 | 47 | func formattedDay(_ date: Date, dateRelation: NearTermDateRelation) -> String { 48 | return dateRelation == NearTermDateRelation.today ? "" : dayFormatter.string(from: date) 49 | } 50 | 51 | func collectedDisplayData() -> UpcomingDisplayData { 52 | let collectedSections : [UpcomingDisplaySection] = sortedUpcomingDisplaySections() 53 | return UpcomingDisplayData(sections: collectedSections) 54 | } 55 | 56 | func displaySectionForDateRelation(_ dateRelation: NearTermDateRelation) -> UpcomingDisplaySection { 57 | let sectionTitle = sectionTitleForDateRelation(dateRelation) 58 | let imageName = sectionImageNameForDateRelation(dateRelation) 59 | let items: [UpcomingDisplayItem] = sections[dateRelation]! 60 | 61 | return UpcomingDisplaySection(name: sectionTitle, imageName: imageName, items: items) 62 | } 63 | 64 | func sortedUpcomingDisplaySections() -> [UpcomingDisplaySection] { 65 | let keys = sortedNearTermDateRelations() 66 | var displaySections : [UpcomingDisplaySection] = [] 67 | 68 | for dateRelation in keys { 69 | let itemArray = sections[dateRelation] 70 | 71 | if (itemArray != nil) { 72 | let displaySection = displaySectionForDateRelation(dateRelation) 73 | displaySections.insert(displaySection, at: displaySections.endIndex) 74 | } 75 | } 76 | 77 | return displaySections 78 | } 79 | 80 | func sortedNearTermDateRelations() -> [NearTermDateRelation] { 81 | return [.today, .tomorrow, .laterThisWeek, .nextWeek] 82 | } 83 | 84 | func sectionTitleForDateRelation(_ dateRelation: NearTermDateRelation) -> String { 85 | switch dateRelation { 86 | case .today: 87 | return "Today" 88 | case .tomorrow: 89 | return "Tomorrow" 90 | case .laterThisWeek: 91 | return "Later This Week" 92 | case .nextWeek: 93 | return "Next Week" 94 | case .outOfRange: 95 | return "Unknown" 96 | } 97 | } 98 | 99 | func sectionImageNameForDateRelation(_ dateRelation: NearTermDateRelation) -> String { 100 | switch dateRelation { 101 | case .today: 102 | return "check" 103 | case .tomorrow: 104 | return "alarm" 105 | case .laterThisWeek: 106 | return "circle" 107 | case .nextWeek: 108 | return "calendar" 109 | case .outOfRange: 110 | return "paper" 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Categories/NSCalendar+CalendarAdditions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSCalendar+CalendarAdditions.swift 3 | // VIPER-SWIFT 4 | // 5 | // Created by Conrad Stoll on 6/5/14. 6 | // Copyright (c) 2014 Mutual Mobile. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Calendar { 12 | static func gregorianCalendar() -> Calendar { 13 | return Calendar(identifier: Calendar.Identifier.gregorian) 14 | } 15 | 16 | func dateWithYear(_ year: Int, month: Int, day: Int) -> Date { 17 | var components = DateComponents() 18 | components.year = year 19 | components.month = month 20 | components.day = day 21 | components.hour = 12 22 | return date(from: components)! 23 | } 24 | 25 | func dateForTomorrowRelativeToToday(_ today: Date) -> Date { 26 | var tomorrowComponents = DateComponents() 27 | tomorrowComponents.day = 1 28 | return date(byAdding: tomorrowComponents, to: today, wrappingComponents: false)! 29 | 30 | } 31 | 32 | func dateForEndOfWeekWithDate(_ date: Date) -> Date { 33 | let daysRemainingThisWeek = daysRemainingInWeekWithDate(date) 34 | var remainingDaysComponent = DateComponents() 35 | remainingDaysComponent.day = daysRemainingThisWeek 36 | return self.date(byAdding: remainingDaysComponent, to: date, wrappingComponents: false)! 37 | } 38 | 39 | func dateForBeginningOfDay(_ date: Date) -> Date { 40 | let newComponent = dateComponents(([.year, .month, .day]), from: date) 41 | let newDate = self.date(from: newComponent) 42 | return newDate! 43 | } 44 | 45 | func dateForEndOfDay(_ date: Date) -> Date { 46 | var components = DateComponents() 47 | components.day = 1 48 | let toDate = dateForBeginningOfDay(date) 49 | let nextDay = self.date(byAdding: components, to: toDate, wrappingComponents: false)! 50 | // let endDay = nextDay.dateByAddingTimeInterval(-1) 51 | return nextDay 52 | } 53 | 54 | func daysRemainingInWeekWithDate(_ date: Date) -> Int { 55 | let weekdayComponent = dateComponents([.weekday], from: date) 56 | let daysRange = CountableRange(range(of: .weekday, in: .weekOfYear, for: date)!) 57 | let daysPerWeek = daysRange.count 58 | let daysRemaining = daysPerWeek - weekdayComponent.weekday! 59 | return daysRemaining 60 | } 61 | 62 | func dateForEndOfFollowingWeekWithDate(_ date: Date) -> Date { 63 | let endOfWeek = dateForEndOfWeekWithDate(date) 64 | var nextWeekComponent = DateComponents() 65 | nextWeekComponent.weekOfYear = 1 66 | let followingWeekDate = self.date(byAdding: nextWeekComponent, to: endOfWeek, wrappingComponents: false) 67 | return followingWeekDate! 68 | } 69 | 70 | func isDate(_ date: Date, beforeYearMonthDay: Date) -> Bool { 71 | let comparison = compareYearMonthDay(date, toYearMonthDay: beforeYearMonthDay) 72 | let result = comparison == ComparisonResult.orderedAscending 73 | return result 74 | } 75 | 76 | func isDate(_ date: Date, equalToYearMonthDay: Date) -> Bool { 77 | let comparison = compareYearMonthDay(date, toYearMonthDay: equalToYearMonthDay) 78 | let result = comparison == ComparisonResult.orderedSame 79 | return result 80 | } 81 | 82 | func isDate(_ date: Date, duringSameWeekAsDate: Date) -> Bool { 83 | let dateC = dateComponents([.weekOfYear], from: date) 84 | let duringSameWeekComponents = dateComponents([.weekOfYear], from: duringSameWeekAsDate) 85 | let result = dateC.weekOfYear == duringSameWeekComponents.weekOfYear 86 | return result 87 | } 88 | 89 | func isDate(_ date: Date, duringWeekAfterDate: Date) -> Bool { 90 | let nextWeek = dateForEndOfFollowingWeekWithDate(duringWeekAfterDate) 91 | let dateC = dateComponents([.weekOfYear], from: date) 92 | let nextWeekComponents = dateComponents([.weekOfYear], from: nextWeek) 93 | let result = dateC.weekOfYear == nextWeekComponents.weekOfYear 94 | return result 95 | } 96 | 97 | func compareYearMonthDay(_ date: Date, toYearMonthDay: Date) -> ComparisonResult { 98 | let dateComponents = yearMonthDayComponentsFromDate(date) 99 | let yearMonthDayComponents = yearMonthDayComponentsFromDate(toYearMonthDay) 100 | 101 | var result = compareInteger(dateComponents.year!, right: yearMonthDayComponents.year!) 102 | 103 | if result == ComparisonResult.orderedSame { 104 | result = compareInteger(dateComponents.month!, right: yearMonthDayComponents.month!) 105 | 106 | if result == ComparisonResult.orderedSame { 107 | result = compareInteger(dateComponents.day!, right: yearMonthDayComponents.day!) 108 | } 109 | } 110 | 111 | return result 112 | } 113 | 114 | func yearMonthDayComponentsFromDate(_ date: Date) -> DateComponents { 115 | let newComponents = dateComponents(([.year, .month, .day]), from: date) 116 | return newComponents 117 | } 118 | 119 | func compareInteger(_ left: Int, right: Int) -> ComparisonResult { 120 | var result = ComparisonResult.orderedDescending 121 | 122 | if left == right { 123 | result = ComparisonResult.orderedSame 124 | } else if left < right { 125 | result = ComparisonResult.orderedAscending 126 | } else { 127 | result = ComparisonResult.orderedDescending 128 | } 129 | 130 | return result 131 | } 132 | 133 | func nearTermRelationForDate(_ date: Date, relativeToToday: Date) -> NearTermDateRelation { 134 | var relation = NearTermDateRelation.outOfRange 135 | 136 | let dateForTomorrow = dateForTomorrowRelativeToToday(relativeToToday) 137 | 138 | let isDateBeforeYearMonthDay = isDate(date, beforeYearMonthDay: relativeToToday) 139 | let isDateEqualToYearMonthDay = isDate(date, equalToYearMonthDay: relativeToToday) 140 | let isDateEqualToYearMonthDayRelativeToTomorrow = isDate(date, equalToYearMonthDay: dateForTomorrow) 141 | let isDateDuringSameWeekAsDate = isDate(date, duringSameWeekAsDate: relativeToToday) 142 | let isDateDuringSameWeekAfterDate = isDate(date, duringWeekAfterDate: relativeToToday) 143 | 144 | if isDateBeforeYearMonthDay { 145 | relation = NearTermDateRelation.outOfRange 146 | } else if isDateEqualToYearMonthDay { 147 | relation = NearTermDateRelation.today 148 | } else if isDateEqualToYearMonthDayRelativeToTomorrow { 149 | let isRelativeDateDuringSameWeek = isDate(relativeToToday, duringSameWeekAsDate: date) 150 | 151 | if isRelativeDateDuringSameWeek { 152 | relation = NearTermDateRelation.tomorrow 153 | } else { 154 | relation = NearTermDateRelation.nextWeek 155 | } 156 | } else if isDateDuringSameWeekAsDate { 157 | relation = NearTermDateRelation.laterThisWeek 158 | } else if isDateDuringSameWeekAfterDate { 159 | relation = NearTermDateRelation.nextWeek 160 | } 161 | 162 | return relation 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /VIPER-SWIFT/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /VIPER-SWIFT.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 261FA1B919414A720029F589 /* AppDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA17219414A720029F589 /* AppDependencies.swift */; }; 11 | 261FA1BC19414A720029F589 /* NSCalendar+CalendarAdditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA17919414A720029F589 /* NSCalendar+CalendarAdditions.swift */; }; 12 | 261FA1BE19414A720029F589 /* Clock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA17D19414A720029F589 /* Clock.swift */; }; 13 | 261FA1BF19414A720029F589 /* DeviceClock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA17E19414A720029F589 /* DeviceClock.swift */; }; 14 | 261FA1C019414A720029F589 /* NearTermDateRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18019414A720029F589 /* NearTermDateRelation.swift */; }; 15 | 261FA1C119414A720029F589 /* TodoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18119414A720029F589 /* TodoItem.swift */; }; 16 | 261FA1C219414A720029F589 /* CoreDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18319414A720029F589 /* CoreDataStore.swift */; }; 17 | 261FA1C419414A720029F589 /* TODO.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18719414A720029F589 /* TODO.xcdatamodeld */; }; 18 | 261FA1C519414A720029F589 /* RootWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18A19414A720029F589 /* RootWireframe.swift */; }; 19 | 261FA1C619414A720029F589 /* AddInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18F19414A720029F589 /* AddInteractor.swift */; }; 20 | 261FA1C719414A720029F589 /* AddDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19119414A720029F589 /* AddDataManager.swift */; }; 21 | 261FA1C819414A720029F589 /* AddModuleDelegateInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19419414A720029F589 /* AddModuleDelegateInterface.swift */; }; 22 | 261FA1C919414A720029F589 /* AddModuleInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19519414A720029F589 /* AddModuleInterface.swift */; }; 23 | 261FA1CA19414A720029F589 /* AddPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19819414A720029F589 /* AddPresenter.swift */; }; 24 | 261FA1CB19414A720029F589 /* AddDismissalTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19A19414A720029F589 /* AddDismissalTransition.swift */; }; 25 | 261FA1CC19414A720029F589 /* AddPresentationTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19B19414A720029F589 /* AddPresentationTransition.swift */; }; 26 | 261FA1CD19414A720029F589 /* AddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19D19414A720029F589 /* AddViewController.swift */; }; 27 | 261FA1CE19414A720029F589 /* AddViewInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA19E19414A720029F589 /* AddViewInterface.swift */; }; 28 | 261FA1CF19414A720029F589 /* AddWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A019414A720029F589 /* AddWireframe.swift */; }; 29 | 261FA1D019414A720029F589 /* ListInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A419414A720029F589 /* ListInteractor.swift */; }; 30 | 261FA1D119414A720029F589 /* ListInteractorIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A519414A720029F589 /* ListInteractorIO.swift */; }; 31 | 261FA1D219414A720029F589 /* UpcomingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A619414A720029F589 /* UpcomingItem.swift */; }; 32 | 261FA1D319414A720029F589 /* ListDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A819414A720029F589 /* ListDataManager.swift */; }; 33 | 261FA1D419414A720029F589 /* ListModuleInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1AB19414A720029F589 /* ListModuleInterface.swift */; }; 34 | 261FA1D519414A720029F589 /* ListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1AE19414A720029F589 /* ListPresenter.swift */; }; 35 | 261FA1D619414A720029F589 /* UpcomingDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1AF19414A720029F589 /* UpcomingDisplayData.swift */; }; 36 | 261FA1D719414A720029F589 /* UpcomingDisplayDataCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B019414A720029F589 /* UpcomingDisplayDataCollection.swift */; }; 37 | 261FA1D819414A720029F589 /* UpcomingDisplayItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B119414A720029F589 /* UpcomingDisplayItem.swift */; }; 38 | 261FA1D919414A720029F589 /* UpcomingDisplaySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B219414A720029F589 /* UpcomingDisplaySection.swift */; }; 39 | 261FA1DA19414A720029F589 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B419414A720029F589 /* ListViewController.swift */; }; 40 | 261FA1DB19414A720029F589 /* ListViewInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B519414A720029F589 /* ListViewInterface.swift */; }; 41 | 261FA1DC19414A720029F589 /* ListWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B719414A720029F589 /* ListWireframe.swift */; }; 42 | 261FA1E719414CBE0029F589 /* ManagedTodoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1E619414CBE0029F589 /* ManagedTodoItem.swift */; }; 43 | 261FA1EC1942320E0029F589 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 261FA1EA1942320E0029F589 /* Main.storyboard */; }; 44 | 261FA1EE194232180029F589 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 261FA1ED194232180029F589 /* Images.xcassets */; }; 45 | 26A605EB1942A1EE0036C71F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A605EA1942A1EE0036C71F /* AppDelegate.swift */; }; 46 | 26D96F9C19428C3600F117F5 /* CalendarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26D96F9B19428C3600F117F5 /* CalendarTests.swift */; }; 47 | 26D96F9D19428C7E00F117F5 /* NSCalendar+CalendarAdditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA17919414A720029F589 /* NSCalendar+CalendarAdditions.swift */; }; 48 | 26D96F9E19428D8100F117F5 /* NearTermDateRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18019414A720029F589 /* NearTermDateRelation.swift */; }; 49 | 26D96FA11942912A00F117F5 /* UpcomingDisplayData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1AF19414A720029F589 /* UpcomingDisplayData.swift */; }; 50 | 26D96FA21942912A00F117F5 /* UpcomingDisplayDataCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B019414A720029F589 /* UpcomingDisplayDataCollection.swift */; }; 51 | 26D96FA31942912A00F117F5 /* UpcomingDisplayItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B119414A720029F589 /* UpcomingDisplayItem.swift */; }; 52 | 26D96FA41942912A00F117F5 /* UpcomingDisplaySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B219414A720029F589 /* UpcomingDisplaySection.swift */; }; 53 | 26D96FA51942912A00F117F5 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B419414A720029F589 /* ListViewController.swift */; }; 54 | 26D96FA61942913600F117F5 /* ListViewInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1B519414A720029F589 /* ListViewInterface.swift */; }; 55 | 26D96FA71942913B00F117F5 /* ListModuleInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1AB19414A720029F589 /* ListModuleInterface.swift */; }; 56 | 26D96FA81942915D00F117F5 /* UpcomingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A619414A720029F589 /* UpcomingItem.swift */; }; 57 | 26D96FA91942932A00F117F5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 261FA1EA1942320E0029F589 /* Main.storyboard */; }; 58 | 26D96FAA1942932A00F117F5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 261FA1ED194232180029F589 /* Images.xcassets */; }; 59 | 26D96FAC1942967900F117F5 /* RelativeDateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26D96FAB1942967900F117F5 /* RelativeDateTests.swift */; }; 60 | 26D96FAE19429A9000F117F5 /* DataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26D96FAD19429A9000F117F5 /* DataTests.swift */; }; 61 | 26D96FAF19429AD000F117F5 /* ManagedTodoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1E619414CBE0029F589 /* ManagedTodoItem.swift */; }; 62 | 26D96FB019429AD000F117F5 /* CoreDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18319414A720029F589 /* CoreDataStore.swift */; }; 63 | 26D96FB119429B1A00F117F5 /* TodoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA18119414A720029F589 /* TodoItem.swift */; }; 64 | 26D96FB219429B1A00F117F5 /* ListDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261FA1A819414A720029F589 /* ListDataManager.swift */; }; 65 | /* End PBXBuildFile section */ 66 | 67 | /* Begin PBXContainerItemProxy section */ 68 | 261FA1611941497E0029F589 /* PBXContainerItemProxy */ = { 69 | isa = PBXContainerItemProxy; 70 | containerPortal = 261FA1461941497E0029F589 /* Project object */; 71 | proxyType = 1; 72 | remoteGlobalIDString = 261FA14D1941497E0029F589; 73 | remoteInfo = "VIPER-SWIFT"; 74 | }; 75 | /* End PBXContainerItemProxy section */ 76 | 77 | /* Begin PBXFileReference section */ 78 | 261FA14E1941497E0029F589 /* VIPER-SWIFT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VIPER-SWIFT.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | 261FA1601941497E0029F589 /* VIPER-SWIFTTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "VIPER-SWIFTTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 261FA1651941497E0029F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | 261FA17219414A720029F589 /* AppDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDependencies.swift; sourceTree = ""; }; 82 | 261FA17919414A720029F589 /* NSCalendar+CalendarAdditions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSCalendar+CalendarAdditions.swift"; sourceTree = ""; }; 83 | 261FA17D19414A720029F589 /* Clock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Clock.swift; sourceTree = ""; }; 84 | 261FA17E19414A720029F589 /* DeviceClock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceClock.swift; sourceTree = ""; }; 85 | 261FA18019414A720029F589 /* NearTermDateRelation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NearTermDateRelation.swift; sourceTree = ""; }; 86 | 261FA18119414A720029F589 /* TodoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoItem.swift; sourceTree = ""; }; 87 | 261FA18319414A720029F589 /* CoreDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStore.swift; sourceTree = ""; }; 88 | 261FA18819414A720029F589 /* TODO.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TODO.xcdatamodel; sourceTree = ""; }; 89 | 261FA18A19414A720029F589 /* RootWireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootWireframe.swift; sourceTree = ""; }; 90 | 261FA18F19414A720029F589 /* AddInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddInteractor.swift; sourceTree = ""; }; 91 | 261FA19119414A720029F589 /* AddDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddDataManager.swift; sourceTree = ""; }; 92 | 261FA19419414A720029F589 /* AddModuleDelegateInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddModuleDelegateInterface.swift; sourceTree = ""; }; 93 | 261FA19519414A720029F589 /* AddModuleInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddModuleInterface.swift; sourceTree = ""; }; 94 | 261FA19819414A720029F589 /* AddPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPresenter.swift; sourceTree = ""; }; 95 | 261FA19A19414A720029F589 /* AddDismissalTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddDismissalTransition.swift; sourceTree = ""; }; 96 | 261FA19B19414A720029F589 /* AddPresentationTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddPresentationTransition.swift; sourceTree = ""; }; 97 | 261FA19D19414A720029F589 /* AddViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddViewController.swift; sourceTree = ""; }; 98 | 261FA19E19414A720029F589 /* AddViewInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddViewInterface.swift; sourceTree = ""; }; 99 | 261FA1A019414A720029F589 /* AddWireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddWireframe.swift; sourceTree = ""; }; 100 | 261FA1A419414A720029F589 /* ListInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListInteractor.swift; sourceTree = ""; }; 101 | 261FA1A519414A720029F589 /* ListInteractorIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListInteractorIO.swift; sourceTree = ""; }; 102 | 261FA1A619414A720029F589 /* UpcomingItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpcomingItem.swift; sourceTree = ""; }; 103 | 261FA1A819414A720029F589 /* ListDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListDataManager.swift; sourceTree = ""; }; 104 | 261FA1AB19414A720029F589 /* ListModuleInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListModuleInterface.swift; sourceTree = ""; }; 105 | 261FA1AE19414A720029F589 /* ListPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListPresenter.swift; sourceTree = ""; }; 106 | 261FA1AF19414A720029F589 /* UpcomingDisplayData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpcomingDisplayData.swift; sourceTree = ""; }; 107 | 261FA1B019414A720029F589 /* UpcomingDisplayDataCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpcomingDisplayDataCollection.swift; sourceTree = ""; }; 108 | 261FA1B119414A720029F589 /* UpcomingDisplayItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpcomingDisplayItem.swift; sourceTree = ""; }; 109 | 261FA1B219414A720029F589 /* UpcomingDisplaySection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpcomingDisplaySection.swift; sourceTree = ""; }; 110 | 261FA1B419414A720029F589 /* ListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; 111 | 261FA1B519414A720029F589 /* ListViewInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewInterface.swift; sourceTree = ""; }; 112 | 261FA1B719414A720029F589 /* ListWireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListWireframe.swift; sourceTree = ""; }; 113 | 261FA1E619414CBE0029F589 /* ManagedTodoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedTodoItem.swift; sourceTree = ""; }; 114 | 261FA1E819414E600029F589 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 115 | 261FA1EB1942320E0029F589 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "VIPER-SWIFT/Base.lproj/Main.storyboard"; sourceTree = SOURCE_ROOT; }; 116 | 261FA1ED194232180029F589 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = "VIPER-SWIFT/Images.xcassets"; sourceTree = SOURCE_ROOT; }; 117 | 26A605EA1942A1EE0036C71F /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 118 | 26D96F9B19428C3600F117F5 /* CalendarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarTests.swift; sourceTree = ""; }; 119 | 26D96FAB1942967900F117F5 /* RelativeDateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeDateTests.swift; sourceTree = ""; }; 120 | 26D96FAD19429A9000F117F5 /* DataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataTests.swift; sourceTree = ""; }; 121 | /* End PBXFileReference section */ 122 | 123 | /* Begin PBXFrameworksBuildPhase section */ 124 | 261FA14B1941497E0029F589 /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | 261FA15D1941497E0029F589 /* Frameworks */ = { 132 | isa = PBXFrameworksBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXFrameworksBuildPhase section */ 139 | 140 | /* Begin PBXGroup section */ 141 | 261FA1451941497E0029F589 = { 142 | isa = PBXGroup; 143 | children = ( 144 | 261FA1501941497E0029F589 /* VIPER-SWIFT */, 145 | 261FA1631941497E0029F589 /* VIPER-SWIFTTests */, 146 | 261FA14F1941497E0029F589 /* Products */, 147 | ); 148 | sourceTree = ""; 149 | }; 150 | 261FA14F1941497E0029F589 /* Products */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 261FA14E1941497E0029F589 /* VIPER-SWIFT.app */, 154 | 261FA1601941497E0029F589 /* VIPER-SWIFTTests.xctest */, 155 | ); 156 | name = Products; 157 | sourceTree = ""; 158 | }; 159 | 261FA1501941497E0029F589 /* VIPER-SWIFT */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 261FA17019414A720029F589 /* Classes */, 163 | 261FA1DD19414AC20029F589 /* Resources */, 164 | 261FA1DE19414AC60029F589 /* Supporting Files */, 165 | ); 166 | path = "VIPER-SWIFT"; 167 | sourceTree = ""; 168 | }; 169 | 261FA1631941497E0029F589 /* VIPER-SWIFTTests */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 26D96F9B19428C3600F117F5 /* CalendarTests.swift */, 173 | 26D96FAD19429A9000F117F5 /* DataTests.swift */, 174 | 26D96FAB1942967900F117F5 /* RelativeDateTests.swift */, 175 | 261FA1641941497E0029F589 /* Supporting Files */, 176 | ); 177 | path = "VIPER-SWIFTTests"; 178 | sourceTree = ""; 179 | }; 180 | 261FA1641941497E0029F589 /* Supporting Files */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | 261FA1651941497E0029F589 /* Info.plist */, 184 | ); 185 | name = "Supporting Files"; 186 | sourceTree = ""; 187 | }; 188 | 261FA17019414A720029F589 /* Classes */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 261FA17319414A720029F589 /* Common */, 192 | 261FA18B19414A720029F589 /* Modules */, 193 | 26A605EA1942A1EE0036C71F /* AppDelegate.swift */, 194 | 261FA17219414A720029F589 /* AppDependencies.swift */, 195 | ); 196 | path = Classes; 197 | sourceTree = ""; 198 | }; 199 | 261FA17319414A720029F589 /* Common */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 261FA17419414A720029F589 /* Categories */, 203 | 261FA17C19414A720029F589 /* Clock */, 204 | 261FA17F19414A720029F589 /* Model */, 205 | 261FA18219414A720029F589 /* Store */, 206 | 261FA18919414A720029F589 /* View */, 207 | ); 208 | path = Common; 209 | sourceTree = ""; 210 | }; 211 | 261FA17419414A720029F589 /* Categories */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 261FA17919414A720029F589 /* NSCalendar+CalendarAdditions.swift */, 215 | ); 216 | path = Categories; 217 | sourceTree = ""; 218 | }; 219 | 261FA17C19414A720029F589 /* Clock */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 261FA17D19414A720029F589 /* Clock.swift */, 223 | 261FA17E19414A720029F589 /* DeviceClock.swift */, 224 | ); 225 | path = Clock; 226 | sourceTree = ""; 227 | }; 228 | 261FA17F19414A720029F589 /* Model */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 261FA18019414A720029F589 /* NearTermDateRelation.swift */, 232 | 261FA18119414A720029F589 /* TodoItem.swift */, 233 | ); 234 | path = Model; 235 | sourceTree = ""; 236 | }; 237 | 261FA18219414A720029F589 /* Store */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | 261FA18419414A720029F589 /* Entities */, 241 | 261FA18319414A720029F589 /* CoreDataStore.swift */, 242 | 261FA18719414A720029F589 /* TODO.xcdatamodeld */, 243 | ); 244 | path = Store; 245 | sourceTree = ""; 246 | }; 247 | 261FA18419414A720029F589 /* Entities */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | 261FA1E619414CBE0029F589 /* ManagedTodoItem.swift */, 251 | ); 252 | path = Entities; 253 | sourceTree = ""; 254 | }; 255 | 261FA18919414A720029F589 /* View */ = { 256 | isa = PBXGroup; 257 | children = ( 258 | 261FA18A19414A720029F589 /* RootWireframe.swift */, 259 | ); 260 | path = View; 261 | sourceTree = ""; 262 | }; 263 | 261FA18B19414A720029F589 /* Modules */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | 261FA18C19414A720029F589 /* Add */, 267 | 261FA1A119414A720029F589 /* List */, 268 | ); 269 | path = Modules; 270 | sourceTree = ""; 271 | }; 272 | 261FA18C19414A720029F589 /* Add */ = { 273 | isa = PBXGroup; 274 | children = ( 275 | 261FA18D19414A720029F589 /* Application Logic */, 276 | 261FA19319414A720029F589 /* Module Interface */, 277 | 261FA19619414A720029F589 /* User Interface */, 278 | ); 279 | path = Add; 280 | sourceTree = ""; 281 | }; 282 | 261FA18D19414A720029F589 /* Application Logic */ = { 283 | isa = PBXGroup; 284 | children = ( 285 | 261FA18E19414A720029F589 /* Interactor */, 286 | 261FA19019414A720029F589 /* Manager */, 287 | ); 288 | path = "Application Logic"; 289 | sourceTree = ""; 290 | }; 291 | 261FA18E19414A720029F589 /* Interactor */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | 261FA18F19414A720029F589 /* AddInteractor.swift */, 295 | ); 296 | path = Interactor; 297 | sourceTree = ""; 298 | }; 299 | 261FA19019414A720029F589 /* Manager */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | 261FA19119414A720029F589 /* AddDataManager.swift */, 303 | ); 304 | path = Manager; 305 | sourceTree = ""; 306 | }; 307 | 261FA19319414A720029F589 /* Module Interface */ = { 308 | isa = PBXGroup; 309 | children = ( 310 | 261FA19419414A720029F589 /* AddModuleDelegateInterface.swift */, 311 | 261FA19519414A720029F589 /* AddModuleInterface.swift */, 312 | ); 313 | path = "Module Interface"; 314 | sourceTree = ""; 315 | }; 316 | 261FA19619414A720029F589 /* User Interface */ = { 317 | isa = PBXGroup; 318 | children = ( 319 | 261FA19719414A720029F589 /* Presenter */, 320 | 261FA19919414A720029F589 /* Transition */, 321 | 261FA19C19414A720029F589 /* View */, 322 | 261FA19F19414A720029F589 /* Wireframe */, 323 | ); 324 | path = "User Interface"; 325 | sourceTree = ""; 326 | }; 327 | 261FA19719414A720029F589 /* Presenter */ = { 328 | isa = PBXGroup; 329 | children = ( 330 | 261FA19819414A720029F589 /* AddPresenter.swift */, 331 | ); 332 | path = Presenter; 333 | sourceTree = ""; 334 | }; 335 | 261FA19919414A720029F589 /* Transition */ = { 336 | isa = PBXGroup; 337 | children = ( 338 | 261FA19A19414A720029F589 /* AddDismissalTransition.swift */, 339 | 261FA19B19414A720029F589 /* AddPresentationTransition.swift */, 340 | ); 341 | path = Transition; 342 | sourceTree = ""; 343 | }; 344 | 261FA19C19414A720029F589 /* View */ = { 345 | isa = PBXGroup; 346 | children = ( 347 | 261FA19D19414A720029F589 /* AddViewController.swift */, 348 | 261FA19E19414A720029F589 /* AddViewInterface.swift */, 349 | ); 350 | path = View; 351 | sourceTree = ""; 352 | }; 353 | 261FA19F19414A720029F589 /* Wireframe */ = { 354 | isa = PBXGroup; 355 | children = ( 356 | 261FA1A019414A720029F589 /* AddWireframe.swift */, 357 | ); 358 | path = Wireframe; 359 | sourceTree = ""; 360 | }; 361 | 261FA1A119414A720029F589 /* List */ = { 362 | isa = PBXGroup; 363 | children = ( 364 | 261FA1A219414A720029F589 /* Application Logic */, 365 | 261FA1AA19414A720029F589 /* Module Interface */, 366 | 261FA1AC19414A720029F589 /* User Interface */, 367 | ); 368 | path = List; 369 | sourceTree = ""; 370 | }; 371 | 261FA1A219414A720029F589 /* Application Logic */ = { 372 | isa = PBXGroup; 373 | children = ( 374 | 261FA1A319414A720029F589 /* Interactor */, 375 | 261FA1A719414A720029F589 /* Manager */, 376 | ); 377 | path = "Application Logic"; 378 | sourceTree = ""; 379 | }; 380 | 261FA1A319414A720029F589 /* Interactor */ = { 381 | isa = PBXGroup; 382 | children = ( 383 | 261FA1A419414A720029F589 /* ListInteractor.swift */, 384 | 261FA1A519414A720029F589 /* ListInteractorIO.swift */, 385 | 261FA1A619414A720029F589 /* UpcomingItem.swift */, 386 | ); 387 | path = Interactor; 388 | sourceTree = ""; 389 | }; 390 | 261FA1A719414A720029F589 /* Manager */ = { 391 | isa = PBXGroup; 392 | children = ( 393 | 261FA1A819414A720029F589 /* ListDataManager.swift */, 394 | ); 395 | path = Manager; 396 | sourceTree = ""; 397 | }; 398 | 261FA1AA19414A720029F589 /* Module Interface */ = { 399 | isa = PBXGroup; 400 | children = ( 401 | 261FA1AB19414A720029F589 /* ListModuleInterface.swift */, 402 | ); 403 | path = "Module Interface"; 404 | sourceTree = ""; 405 | }; 406 | 261FA1AC19414A720029F589 /* User Interface */ = { 407 | isa = PBXGroup; 408 | children = ( 409 | 261FA1AD19414A720029F589 /* Presenter */, 410 | 261FA1B319414A720029F589 /* View */, 411 | 261FA1B619414A720029F589 /* Wireframe */, 412 | ); 413 | path = "User Interface"; 414 | sourceTree = ""; 415 | }; 416 | 261FA1AD19414A720029F589 /* Presenter */ = { 417 | isa = PBXGroup; 418 | children = ( 419 | 261FA1AE19414A720029F589 /* ListPresenter.swift */, 420 | 261FA1AF19414A720029F589 /* UpcomingDisplayData.swift */, 421 | 261FA1B019414A720029F589 /* UpcomingDisplayDataCollection.swift */, 422 | 261FA1B119414A720029F589 /* UpcomingDisplayItem.swift */, 423 | 261FA1B219414A720029F589 /* UpcomingDisplaySection.swift */, 424 | ); 425 | path = Presenter; 426 | sourceTree = ""; 427 | }; 428 | 261FA1B319414A720029F589 /* View */ = { 429 | isa = PBXGroup; 430 | children = ( 431 | 261FA1B419414A720029F589 /* ListViewController.swift */, 432 | 261FA1B519414A720029F589 /* ListViewInterface.swift */, 433 | ); 434 | path = View; 435 | sourceTree = ""; 436 | }; 437 | 261FA1B619414A720029F589 /* Wireframe */ = { 438 | isa = PBXGroup; 439 | children = ( 440 | 261FA1B719414A720029F589 /* ListWireframe.swift */, 441 | ); 442 | path = Wireframe; 443 | sourceTree = ""; 444 | }; 445 | 261FA1DD19414AC20029F589 /* Resources */ = { 446 | isa = PBXGroup; 447 | children = ( 448 | 261FA1EA1942320E0029F589 /* Main.storyboard */, 449 | 261FA1ED194232180029F589 /* Images.xcassets */, 450 | ); 451 | name = Resources; 452 | path = "../../../../Documents/Repos/VIPER-TODO-SWIFT/viper-todo-swift/VIPER TODO/Resources"; 453 | sourceTree = ""; 454 | }; 455 | 261FA1DE19414AC60029F589 /* Supporting Files */ = { 456 | isa = PBXGroup; 457 | children = ( 458 | 261FA1E819414E600029F589 /* Info.plist */, 459 | ); 460 | name = "Supporting Files"; 461 | sourceTree = ""; 462 | }; 463 | /* End PBXGroup section */ 464 | 465 | /* Begin PBXNativeTarget section */ 466 | 261FA14D1941497E0029F589 /* VIPER-SWIFT */ = { 467 | isa = PBXNativeTarget; 468 | buildConfigurationList = 261FA16A1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFT" */; 469 | buildPhases = ( 470 | 261FA14A1941497E0029F589 /* Sources */, 471 | 261FA14B1941497E0029F589 /* Frameworks */, 472 | 261FA14C1941497E0029F589 /* Resources */, 473 | ); 474 | buildRules = ( 475 | ); 476 | dependencies = ( 477 | ); 478 | name = "VIPER-SWIFT"; 479 | productName = "VIPER-SWIFT"; 480 | productReference = 261FA14E1941497E0029F589 /* VIPER-SWIFT.app */; 481 | productType = "com.apple.product-type.application"; 482 | }; 483 | 261FA15F1941497E0029F589 /* VIPER-SWIFTTests */ = { 484 | isa = PBXNativeTarget; 485 | buildConfigurationList = 261FA16D1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFTTests" */; 486 | buildPhases = ( 487 | 261FA15C1941497E0029F589 /* Sources */, 488 | 261FA15D1941497E0029F589 /* Frameworks */, 489 | 261FA15E1941497E0029F589 /* Resources */, 490 | ); 491 | buildRules = ( 492 | ); 493 | dependencies = ( 494 | 261FA1621941497E0029F589 /* PBXTargetDependency */, 495 | ); 496 | name = "VIPER-SWIFTTests"; 497 | productName = "VIPER-SWIFTTests"; 498 | productReference = 261FA1601941497E0029F589 /* VIPER-SWIFTTests.xctest */; 499 | productType = "com.apple.product-type.bundle.unit-test"; 500 | }; 501 | /* End PBXNativeTarget section */ 502 | 503 | /* Begin PBXProject section */ 504 | 261FA1461941497E0029F589 /* Project object */ = { 505 | isa = PBXProject; 506 | attributes = { 507 | LastSwiftMigration = 0730; 508 | LastSwiftUpdateCheck = 0730; 509 | LastUpgradeCheck = 0920; 510 | ORGANIZATIONNAME = "Conrad Stoll"; 511 | TargetAttributes = { 512 | 261FA14D1941497E0029F589 = { 513 | CreatedOnToolsVersion = 6.0; 514 | LastSwiftMigration = 0920; 515 | }; 516 | 261FA15F1941497E0029F589 = { 517 | CreatedOnToolsVersion = 6.0; 518 | LastSwiftMigration = 0920; 519 | TestTargetID = 261FA14D1941497E0029F589; 520 | }; 521 | }; 522 | }; 523 | buildConfigurationList = 261FA1491941497E0029F589 /* Build configuration list for PBXProject "VIPER-SWIFT" */; 524 | compatibilityVersion = "Xcode 3.2"; 525 | developmentRegion = English; 526 | hasScannedForEncodings = 0; 527 | knownRegions = ( 528 | en, 529 | Base, 530 | ); 531 | mainGroup = 261FA1451941497E0029F589; 532 | productRefGroup = 261FA14F1941497E0029F589 /* Products */; 533 | projectDirPath = ""; 534 | projectRoot = ""; 535 | targets = ( 536 | 261FA14D1941497E0029F589 /* VIPER-SWIFT */, 537 | 261FA15F1941497E0029F589 /* VIPER-SWIFTTests */, 538 | ); 539 | }; 540 | /* End PBXProject section */ 541 | 542 | /* Begin PBXResourcesBuildPhase section */ 543 | 261FA14C1941497E0029F589 /* Resources */ = { 544 | isa = PBXResourcesBuildPhase; 545 | buildActionMask = 2147483647; 546 | files = ( 547 | 261FA1EE194232180029F589 /* Images.xcassets in Resources */, 548 | 261FA1EC1942320E0029F589 /* Main.storyboard in Resources */, 549 | ); 550 | runOnlyForDeploymentPostprocessing = 0; 551 | }; 552 | 261FA15E1941497E0029F589 /* Resources */ = { 553 | isa = PBXResourcesBuildPhase; 554 | buildActionMask = 2147483647; 555 | files = ( 556 | 26D96FA91942932A00F117F5 /* Main.storyboard in Resources */, 557 | 26D96FAA1942932A00F117F5 /* Images.xcassets in Resources */, 558 | ); 559 | runOnlyForDeploymentPostprocessing = 0; 560 | }; 561 | /* End PBXResourcesBuildPhase section */ 562 | 563 | /* Begin PBXSourcesBuildPhase section */ 564 | 261FA14A1941497E0029F589 /* Sources */ = { 565 | isa = PBXSourcesBuildPhase; 566 | buildActionMask = 2147483647; 567 | files = ( 568 | 261FA1C719414A720029F589 /* AddDataManager.swift in Sources */, 569 | 261FA1D119414A720029F589 /* ListInteractorIO.swift in Sources */, 570 | 261FA1D719414A720029F589 /* UpcomingDisplayDataCollection.swift in Sources */, 571 | 261FA1D219414A720029F589 /* UpcomingItem.swift in Sources */, 572 | 261FA1D519414A720029F589 /* ListPresenter.swift in Sources */, 573 | 261FA1D419414A720029F589 /* ListModuleInterface.swift in Sources */, 574 | 261FA1B919414A720029F589 /* AppDependencies.swift in Sources */, 575 | 261FA1D619414A720029F589 /* UpcomingDisplayData.swift in Sources */, 576 | 261FA1C619414A720029F589 /* AddInteractor.swift in Sources */, 577 | 261FA1D919414A720029F589 /* UpcomingDisplaySection.swift in Sources */, 578 | 261FA1C819414A720029F589 /* AddModuleDelegateInterface.swift in Sources */, 579 | 261FA1BF19414A720029F589 /* DeviceClock.swift in Sources */, 580 | 261FA1DA19414A720029F589 /* ListViewController.swift in Sources */, 581 | 261FA1BE19414A720029F589 /* Clock.swift in Sources */, 582 | 261FA1C519414A720029F589 /* RootWireframe.swift in Sources */, 583 | 261FA1C419414A720029F589 /* TODO.xcdatamodeld in Sources */, 584 | 261FA1CD19414A720029F589 /* AddViewController.swift in Sources */, 585 | 261FA1C119414A720029F589 /* TodoItem.swift in Sources */, 586 | 26A605EB1942A1EE0036C71F /* AppDelegate.swift in Sources */, 587 | 261FA1CB19414A720029F589 /* AddDismissalTransition.swift in Sources */, 588 | 261FA1C219414A720029F589 /* CoreDataStore.swift in Sources */, 589 | 261FA1CC19414A720029F589 /* AddPresentationTransition.swift in Sources */, 590 | 261FA1C919414A720029F589 /* AddModuleInterface.swift in Sources */, 591 | 261FA1D319414A720029F589 /* ListDataManager.swift in Sources */, 592 | 261FA1CA19414A720029F589 /* AddPresenter.swift in Sources */, 593 | 261FA1D019414A720029F589 /* ListInteractor.swift in Sources */, 594 | 261FA1C019414A720029F589 /* NearTermDateRelation.swift in Sources */, 595 | 261FA1E719414CBE0029F589 /* ManagedTodoItem.swift in Sources */, 596 | 261FA1D819414A720029F589 /* UpcomingDisplayItem.swift in Sources */, 597 | 261FA1BC19414A720029F589 /* NSCalendar+CalendarAdditions.swift in Sources */, 598 | 261FA1DB19414A720029F589 /* ListViewInterface.swift in Sources */, 599 | 261FA1DC19414A720029F589 /* ListWireframe.swift in Sources */, 600 | 261FA1CE19414A720029F589 /* AddViewInterface.swift in Sources */, 601 | 261FA1CF19414A720029F589 /* AddWireframe.swift in Sources */, 602 | ); 603 | runOnlyForDeploymentPostprocessing = 0; 604 | }; 605 | 261FA15C1941497E0029F589 /* Sources */ = { 606 | isa = PBXSourcesBuildPhase; 607 | buildActionMask = 2147483647; 608 | files = ( 609 | 26D96FB219429B1A00F117F5 /* ListDataManager.swift in Sources */, 610 | 26D96FAF19429AD000F117F5 /* ManagedTodoItem.swift in Sources */, 611 | 26D96F9D19428C7E00F117F5 /* NSCalendar+CalendarAdditions.swift in Sources */, 612 | 26D96FAC1942967900F117F5 /* RelativeDateTests.swift in Sources */, 613 | 26D96FAE19429A9000F117F5 /* DataTests.swift in Sources */, 614 | 26D96FA71942913B00F117F5 /* ListModuleInterface.swift in Sources */, 615 | 26D96FA41942912A00F117F5 /* UpcomingDisplaySection.swift in Sources */, 616 | 26D96FA31942912A00F117F5 /* UpcomingDisplayItem.swift in Sources */, 617 | 26D96F9C19428C3600F117F5 /* CalendarTests.swift in Sources */, 618 | 26D96FA61942913600F117F5 /* ListViewInterface.swift in Sources */, 619 | 26D96FB019429AD000F117F5 /* CoreDataStore.swift in Sources */, 620 | 26D96FA11942912A00F117F5 /* UpcomingDisplayData.swift in Sources */, 621 | 26D96FA81942915D00F117F5 /* UpcomingItem.swift in Sources */, 622 | 26D96F9E19428D8100F117F5 /* NearTermDateRelation.swift in Sources */, 623 | 26D96FA51942912A00F117F5 /* ListViewController.swift in Sources */, 624 | 26D96FB119429B1A00F117F5 /* TodoItem.swift in Sources */, 625 | 26D96FA21942912A00F117F5 /* UpcomingDisplayDataCollection.swift in Sources */, 626 | ); 627 | runOnlyForDeploymentPostprocessing = 0; 628 | }; 629 | /* End PBXSourcesBuildPhase section */ 630 | 631 | /* Begin PBXTargetDependency section */ 632 | 261FA1621941497E0029F589 /* PBXTargetDependency */ = { 633 | isa = PBXTargetDependency; 634 | target = 261FA14D1941497E0029F589 /* VIPER-SWIFT */; 635 | targetProxy = 261FA1611941497E0029F589 /* PBXContainerItemProxy */; 636 | }; 637 | /* End PBXTargetDependency section */ 638 | 639 | /* Begin PBXVariantGroup section */ 640 | 261FA1EA1942320E0029F589 /* Main.storyboard */ = { 641 | isa = PBXVariantGroup; 642 | children = ( 643 | 261FA1EB1942320E0029F589 /* Base */, 644 | ); 645 | name = Main.storyboard; 646 | sourceTree = ""; 647 | }; 648 | /* End PBXVariantGroup section */ 649 | 650 | /* Begin XCBuildConfiguration section */ 651 | 261FA1681941497E0029F589 /* Debug */ = { 652 | isa = XCBuildConfiguration; 653 | buildSettings = { 654 | ALWAYS_SEARCH_USER_PATHS = NO; 655 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 656 | CLANG_CXX_LIBRARY = "libc++"; 657 | CLANG_ENABLE_MODULES = YES; 658 | CLANG_ENABLE_OBJC_ARC = YES; 659 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 660 | CLANG_WARN_BOOL_CONVERSION = YES; 661 | CLANG_WARN_COMMA = YES; 662 | CLANG_WARN_CONSTANT_CONVERSION = YES; 663 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 664 | CLANG_WARN_EMPTY_BODY = YES; 665 | CLANG_WARN_ENUM_CONVERSION = YES; 666 | CLANG_WARN_INFINITE_RECURSION = YES; 667 | CLANG_WARN_INT_CONVERSION = YES; 668 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 669 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 670 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 671 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 672 | CLANG_WARN_STRICT_PROTOTYPES = YES; 673 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 674 | CLANG_WARN_UNREACHABLE_CODE = YES; 675 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 676 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 677 | COPY_PHASE_STRIP = NO; 678 | ENABLE_STRICT_OBJC_MSGSEND = YES; 679 | ENABLE_TESTABILITY = YES; 680 | GCC_C_LANGUAGE_STANDARD = gnu99; 681 | GCC_DYNAMIC_NO_PIC = NO; 682 | GCC_NO_COMMON_BLOCKS = YES; 683 | GCC_OPTIMIZATION_LEVEL = 0; 684 | GCC_PREPROCESSOR_DEFINITIONS = ( 685 | "DEBUG=1", 686 | "$(inherited)", 687 | ); 688 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 689 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 690 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 691 | GCC_WARN_UNDECLARED_SELECTOR = YES; 692 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 693 | GCC_WARN_UNUSED_FUNCTION = YES; 694 | GCC_WARN_UNUSED_VARIABLE = YES; 695 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 696 | METAL_ENABLE_DEBUG_INFO = YES; 697 | ONLY_ACTIVE_ARCH = YES; 698 | SDKROOT = iphoneos; 699 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 700 | }; 701 | name = Debug; 702 | }; 703 | 261FA1691941497E0029F589 /* Release */ = { 704 | isa = XCBuildConfiguration; 705 | buildSettings = { 706 | ALWAYS_SEARCH_USER_PATHS = NO; 707 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 708 | CLANG_CXX_LIBRARY = "libc++"; 709 | CLANG_ENABLE_MODULES = YES; 710 | CLANG_ENABLE_OBJC_ARC = YES; 711 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 712 | CLANG_WARN_BOOL_CONVERSION = YES; 713 | CLANG_WARN_COMMA = YES; 714 | CLANG_WARN_CONSTANT_CONVERSION = YES; 715 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 716 | CLANG_WARN_EMPTY_BODY = YES; 717 | CLANG_WARN_ENUM_CONVERSION = YES; 718 | CLANG_WARN_INFINITE_RECURSION = YES; 719 | CLANG_WARN_INT_CONVERSION = YES; 720 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 721 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 722 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 723 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 724 | CLANG_WARN_STRICT_PROTOTYPES = YES; 725 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 726 | CLANG_WARN_UNREACHABLE_CODE = YES; 727 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 728 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 729 | COPY_PHASE_STRIP = YES; 730 | ENABLE_NS_ASSERTIONS = NO; 731 | ENABLE_STRICT_OBJC_MSGSEND = YES; 732 | GCC_C_LANGUAGE_STANDARD = gnu99; 733 | GCC_NO_COMMON_BLOCKS = YES; 734 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 735 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 736 | GCC_WARN_UNDECLARED_SELECTOR = YES; 737 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 738 | GCC_WARN_UNUSED_FUNCTION = YES; 739 | GCC_WARN_UNUSED_VARIABLE = YES; 740 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 741 | METAL_ENABLE_DEBUG_INFO = NO; 742 | SDKROOT = iphoneos; 743 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 744 | VALIDATE_PRODUCT = YES; 745 | }; 746 | name = Release; 747 | }; 748 | 261FA16B1941497E0029F589 /* Debug */ = { 749 | isa = XCBuildConfiguration; 750 | buildSettings = { 751 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 752 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 753 | INFOPLIST_FILE = "VIPER-SWIFT/Info.plist"; 754 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 755 | PRODUCT_BUNDLE_IDENTIFIER = "com.mutualmobile.${PRODUCT_NAME:rfc1034identifier}"; 756 | PRODUCT_NAME = "$(TARGET_NAME)"; 757 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 758 | SWIFT_VERSION = 4.0; 759 | }; 760 | name = Debug; 761 | }; 762 | 261FA16C1941497E0029F589 /* Release */ = { 763 | isa = XCBuildConfiguration; 764 | buildSettings = { 765 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 766 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 767 | INFOPLIST_FILE = "VIPER-SWIFT/Info.plist"; 768 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 769 | PRODUCT_BUNDLE_IDENTIFIER = "com.mutualmobile.${PRODUCT_NAME:rfc1034identifier}"; 770 | PRODUCT_NAME = "$(TARGET_NAME)"; 771 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 772 | SWIFT_VERSION = 4.0; 773 | }; 774 | name = Release; 775 | }; 776 | 261FA16E1941497E0029F589 /* Debug */ = { 777 | isa = XCBuildConfiguration; 778 | buildSettings = { 779 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/VIPER-SWIFT.app/VIPER-SWIFT"; 780 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 781 | GCC_PREPROCESSOR_DEFINITIONS = ( 782 | "DEBUG=1", 783 | "$(inherited)", 784 | ); 785 | INFOPLIST_FILE = "VIPER-SWIFTTests/Info.plist"; 786 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 787 | METAL_ENABLE_DEBUG_INFO = YES; 788 | PRODUCT_BUNDLE_IDENTIFIER = "com.mutualmobile.${PRODUCT_NAME:rfc1034identifier}"; 789 | PRODUCT_NAME = "$(TARGET_NAME)"; 790 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 791 | SWIFT_VERSION = 4.0; 792 | TEST_HOST = "$(BUNDLE_LOADER)"; 793 | }; 794 | name = Debug; 795 | }; 796 | 261FA16F1941497E0029F589 /* Release */ = { 797 | isa = XCBuildConfiguration; 798 | buildSettings = { 799 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/VIPER-SWIFT.app/VIPER-SWIFT"; 800 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 801 | INFOPLIST_FILE = "VIPER-SWIFTTests/Info.plist"; 802 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 803 | METAL_ENABLE_DEBUG_INFO = NO; 804 | PRODUCT_BUNDLE_IDENTIFIER = "com.mutualmobile.${PRODUCT_NAME:rfc1034identifier}"; 805 | PRODUCT_NAME = "$(TARGET_NAME)"; 806 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 807 | SWIFT_VERSION = 4.0; 808 | TEST_HOST = "$(BUNDLE_LOADER)"; 809 | }; 810 | name = Release; 811 | }; 812 | /* End XCBuildConfiguration section */ 813 | 814 | /* Begin XCConfigurationList section */ 815 | 261FA1491941497E0029F589 /* Build configuration list for PBXProject "VIPER-SWIFT" */ = { 816 | isa = XCConfigurationList; 817 | buildConfigurations = ( 818 | 261FA1681941497E0029F589 /* Debug */, 819 | 261FA1691941497E0029F589 /* Release */, 820 | ); 821 | defaultConfigurationIsVisible = 0; 822 | defaultConfigurationName = Release; 823 | }; 824 | 261FA16A1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFT" */ = { 825 | isa = XCConfigurationList; 826 | buildConfigurations = ( 827 | 261FA16B1941497E0029F589 /* Debug */, 828 | 261FA16C1941497E0029F589 /* Release */, 829 | ); 830 | defaultConfigurationIsVisible = 0; 831 | defaultConfigurationName = Release; 832 | }; 833 | 261FA16D1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFTTests" */ = { 834 | isa = XCConfigurationList; 835 | buildConfigurations = ( 836 | 261FA16E1941497E0029F589 /* Debug */, 837 | 261FA16F1941497E0029F589 /* Release */, 838 | ); 839 | defaultConfigurationIsVisible = 0; 840 | defaultConfigurationName = Release; 841 | }; 842 | /* End XCConfigurationList section */ 843 | 844 | /* Begin XCVersionGroup section */ 845 | 261FA18719414A720029F589 /* TODO.xcdatamodeld */ = { 846 | isa = XCVersionGroup; 847 | children = ( 848 | 261FA18819414A720029F589 /* TODO.xcdatamodel */, 849 | ); 850 | currentVersion = 261FA18819414A720029F589 /* TODO.xcdatamodel */; 851 | path = TODO.xcdatamodeld; 852 | sourceTree = ""; 853 | versionGroupType = wrapper.xcdatamodel; 854 | }; 855 | /* End XCVersionGroup section */ 856 | }; 857 | rootObject = 261FA1461941497E0029F589 /* Project object */; 858 | } 859 | --------------------------------------------------------------------------------