├── 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 │ ├── 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 │ ├── circle.imageset │ │ ├── circle.png │ │ ├── circle@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 │ │ ├── Store │ │ │ ├── Entities │ │ │ │ └── ManagedTodoItem.swift │ │ │ ├── TODO.xcdatamodeld │ │ │ │ └── TODO.xcdatamodel │ │ │ │ │ └── contents │ │ │ └── CoreDataStore.swift │ │ ├── Model │ │ │ ├── NearTermDateRelation.swift │ │ │ └── TodoItem.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 ├── DataTests.swift ├── Info.plist ├── 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/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/line.imageset/line.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/check.imageset/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/check.imageset/check.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/empty.imageset/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/empty.imageset/empty.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/month.imageset/month.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/month.imageset/month.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/notes.imageset/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/notes.imageset/notes.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/paper.imageset/paper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/paper.imageset/paper.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/alarm.imageset/alarm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-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/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/check.imageset/check@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/circle.imageset/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/circle.imageset/circle.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/empty.imageset/empty@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/empty.imageset/empty@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/line.imageset/line@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/line.imageset/line@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/month.imageset/month@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-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/objcio/issue-13-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/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/paper.imageset/paper@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/circle.imageset/circle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/circle.imageset/circle@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-viper-swift/HEAD/VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar.png -------------------------------------------------------------------------------- /VIPER-SWIFT/Images.xcassets/calendar.imageset/calendar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/issue-13-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/objcio/issue-13-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/objcio/issue-13-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/objcio/issue-13-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() -> NSDate 13 | } -------------------------------------------------------------------------------- /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() -> NSDate { 13 | return NSDate() 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 : NSString 14 | @NSManaged var date : NSDate 15 | } -------------------------------------------------------------------------------- /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: NSString, dueDate: NSDate) 14 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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: NSString) 13 | func setEntryDueDate(date: NSDate) 14 | func setMinimumDueDate(date: NSDate) 15 | } -------------------------------------------------------------------------------- /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 : NSDate 13 | let name : String 14 | 15 | init(dueDate: NSDate, name: String) { 16 | self.dueDate = dueDate 17 | self.name = name 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /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: NSString, dueDate: NSDate) { 15 | let newEntry = TodoItem(dueDate: dueDate, name: name) 16 | addDataManager?.addNewEntry(newEntry) 17 | } 18 | } -------------------------------------------------------------------------------- /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() as ManagedTodoItem 16 | newEntry.name = entry.name 17 | newEntry.date = entry.dueDate; 18 | 19 | dataStore?.save() 20 | } 21 | } -------------------------------------------------------------------------------- /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: NSDictionary?) -> Bool { 18 | appDependencies.installRootViewControllerIntoWindow(window!) 19 | 20 | return true 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /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 : Equatable { 12 | let sections : UpcomingDisplaySection[] = [] 13 | 14 | init(sections: UpcomingDisplaySection[]) { 15 | self.sections = sections 16 | self.sections.unshare() 17 | } 18 | } 19 | 20 | func == (leftSide: UpcomingDisplayData, rightSide: UpcomingDisplayData) -> Bool { 21 | var hasEqualSections = false 22 | hasEqualSections = rightSide.sections == leftSide.sections 23 | return hasEqualSections 24 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /VIPER-SWIFT/Classes/Common/Store/TODO.xcdatamodeld/TODO.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /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.measureBlock() { 23 | let startDate = NSDate() 24 | let endDate = NSDate(timeIntervalSinceReferenceDate: 0) 25 | self.dataManager.todoItemsBetweenStartDate(startDate, endDate: endDate, completion: { entries in 26 | 27 | }) 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /VIPER-SWIFTTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.mutualmobile.${PRODUCT_NAME:rfc1034identifier} 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-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 : Equatable { 13 | let name : String = "" 14 | let imageName : String = "" 15 | var items : UpcomingDisplayItem[] = [] 16 | 17 | init(name: String, imageName: String, items: UpcomingDisplayItem[]?) { 18 | self.name = name 19 | self.imageName = imageName 20 | 21 | if items { 22 | self.items = items! 23 | self.items.unshare() 24 | } 25 | } 26 | } 27 | 28 | func == (leftSide: UpcomingDisplaySection, rightSide: UpcomingDisplaySection) -> Bool { 29 | var hasEqualSections = false 30 | hasEqualSections = rightSide.items == leftSide.items 31 | return hasEqualSections 32 | } -------------------------------------------------------------------------------- /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 : Equatable, Printable { 12 | let title : String = "" 13 | let dueDate : String = "" 14 | 15 | var description : String { get { 16 | return "\(title) -- \(dueDate)" 17 | }} 18 | 19 | init(title: String, dueDate: String) { 20 | self.title = title 21 | self.dueDate = dueDate 22 | } 23 | } 24 | 25 | func == (leftSide: UpcomingDisplayItem, rightSide: UpcomingDisplayItem) -> Bool { 26 | var hasEqualSections = false 27 | hasEqualSections = rightSide.title == leftSide.title 28 | 29 | if hasEqualSections == false { 30 | return false 31 | } 32 | 33 | hasEqualSections = rightSide.dueDate == rightSide.dueDate 34 | 35 | return hasEqualSections 36 | } -------------------------------------------------------------------------------- /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 cancelAddAction() { 17 | addWireframe?.dismissAddInterface() 18 | addModuleDelegate?.addModuleDidCancelAddAction() 19 | } 20 | 21 | func saveAddActionWithName(name: NSString, dueDate: NSDate) { 22 | addInteractor?.saveNewEntryWithName(name, dueDate: dueDate); 23 | addWireframe?.dismissAddInterface() 24 | addModuleDelegate?.addModuleDidSaveAddAction() 25 | } 26 | 27 | func configureUserInterfaceForPresentation(addViewUserInterface: AddViewInterface) { 28 | addViewUserInterface.setMinimumDueDate(NSDate()) 29 | } 30 | } -------------------------------------------------------------------------------- /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 : NSDate = NSDate() 14 | let dateRelation : NearTermDateRelation = NearTermDateRelation.OutOfRange 15 | 16 | init(title: String, dueDate: NSDate, dateRelation: NearTermDateRelation) { 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 | } -------------------------------------------------------------------------------- /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 | com.mutualmobile.${PRODUCT_NAME:rfc1034identifier} 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: NSDate, endDate: NSDate, completion: ((TodoItem[]) -> Void)!) { 16 | let calendar = NSCalendar.autoupdatingCurrentCalendar() 17 | let beginning = calendar.dateForBeginningOfDay(startDate) 18 | let end = calendar.dateForEndOfDay(endDate) 19 | 20 | let predicate = NSPredicate(format: "(date >= %@) AND (date <= %@)", beginning, end) 21 | let sortDescriptors = [] 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 | var todoItems : TodoItem[] = [] 33 | 34 | for managedTodoItem in entries { 35 | let todoItem = TodoItem(dueDate: managedTodoItem.date, name: managedTodoItem.name) 36 | todoItems.append(todoItem) 37 | } 38 | 39 | return todoItems 40 | } 41 | } -------------------------------------------------------------------------------- /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(transitionContext: UIViewControllerContextTransitioning!) -> NSTimeInterval { 14 | return 0.72 15 | } 16 | 17 | func animateTransition(transitionContext: UIViewControllerContextTransitioning!) { 18 | let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as AddViewController 19 | 20 | let finalCenter = CGPointMake(160.0, (fromVC.view.bounds.size.height / 2) - 1000.0) 21 | 22 | let options = UIViewAnimationOptions.CurveEaseIn 23 | 24 | UIView.animateWithDuration(self.transitionDuration(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 | } -------------------------------------------------------------------------------- /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.instantiateViewControllerWithIdentifier(ListViewControllerIdentifier) as ListViewController 35 | return viewController 36 | } 37 | 38 | func mainStoryboard() -> UIStoryboard { 39 | let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) 40 | return storyboard 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /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 | func updateView() { 18 | listInteractor?.findUpcomingItems() 19 | } 20 | 21 | func foundUpcomingItems(upcomingItems: UpcomingItem[]) { 22 | if upcomingItems.count == 0 { 23 | userInterface?.showNoContentMessage() 24 | } else { 25 | updateUserInterfaceWithUpcomingItems(upcomingItems) 26 | } 27 | } 28 | 29 | func updateUserInterfaceWithUpcomingItems(upcomingItems: UpcomingItem[]) { 30 | let upcomingDisplayData = upcomingDisplayDataWithItems(upcomingItems) 31 | userInterface?.showUpcomingDisplayData(upcomingDisplayData) 32 | } 33 | 34 | func upcomingDisplayDataWithItems(upcomingItems: UpcomingItem[]) -> UpcomingDisplayData { 35 | let collection = UpcomingDisplayDataCollection() 36 | collection.addUpcomingItems(upcomingItems) 37 | return collection.collectedDisplayData() 38 | } 39 | 40 | func addNewEntry() { 41 | listWireframe?.presentAddInterface() 42 | } 43 | 44 | func addModuleDidCancelAddAction() { 45 | // No action necessary 46 | } 47 | 48 | func addModuleDidSaveAddAction() { 49 | updateView() 50 | } 51 | } -------------------------------------------------------------------------------- /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 | func findUpcomingItems() { 23 | let today = clock.today() 24 | let endOfNextWeek = NSCalendar.currentCalendar().dateForEndOfFollowingWeekWithDate(today) 25 | 26 | dataManager.todoItemsBetweenStartDate(today, 27 | endDate: endOfNextWeek, 28 | completion: { todoItems in 29 | let upcomingItems = self.upcomingItemsFromToDoItems(todoItems) 30 | self.output?.foundUpcomingItems(upcomingItems) 31 | }) 32 | } 33 | 34 | func upcomingItemsFromToDoItems(todoItems: TodoItem[]) -> UpcomingItem[] { 35 | let calendar = NSCalendar.autoupdatingCurrentCalendar() 36 | 37 | var upcomingItems : UpcomingItem[] = [] 38 | 39 | for todoItem in todoItems { 40 | var dateRelation = calendar.nearTermRelationForDate(todoItem.dueDate, relativeToToday: clock.today()) 41 | let upcomingItem = UpcomingItem(title: todoItem.name, dueDate: todoItem.dueDate, dateRelation: dateRelation) 42 | upcomingItems.insert(upcomingItem, atIndex: upcomingItems.endIndex) 43 | } 44 | 45 | return upcomingItems 46 | } 47 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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.presentViewController(newViewController, animated: true, completion: nil) 28 | 29 | presentedViewController = newViewController 30 | } 31 | 32 | func dismissAddInterface() { 33 | presentedViewController?.dismissViewControllerAnimated(true, completion: nil) 34 | } 35 | 36 | func addViewController() -> AddViewController { 37 | let storyboard = mainStoryboard() 38 | let addViewController: AddViewController = storyboard.instantiateViewControllerWithIdentifier(AddViewControllerIdentifier) as AddViewController 39 | return addViewController 40 | } 41 | 42 | func mainStoryboard() -> UIStoryboard { 43 | let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) 44 | return storyboard 45 | } 46 | 47 | func animationControllerForDismissedController(dismissed: UIViewController!) -> UIViewControllerAnimatedTransitioning! { 48 | return AddDismissalTransition() 49 | } 50 | 51 | func animationControllerForPresentedController(presented: UIViewController!, presentingController presenting: UIViewController!, sourceController source: UIViewController!) -> UIViewControllerAnimatedTransitioning! { 52 | return AddPresentationTransition() 53 | } 54 | } -------------------------------------------------------------------------------- /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(transitionContext: UIViewControllerContextTransitioning!) -> NSTimeInterval { 14 | return 0.72 15 | } 16 | 17 | func animateTransition(transitionContext: UIViewControllerContextTransitioning!) { 18 | let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) 19 | let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as AddViewController 20 | 21 | toVC.transitioningBackgroundView.backgroundColor = UIColor.darkGrayColor() 22 | toVC.transitioningBackgroundView.alpha = 0.0 23 | toVC.transitioningBackgroundView.frame = UIScreen.mainScreen().bounds 24 | 25 | let containerView = transitionContext.containerView() 26 | containerView.addSubview(toVC.transitioningBackgroundView) 27 | containerView.addSubview(toVC.view) 28 | 29 | let toViewFrame = CGRectMake(0, 0, 260, 300) 30 | toVC.view.frame = toViewFrame 31 | 32 | let finalCenter = CGPointMake(fromVC.view.bounds.size.width / 2, 20 + toViewFrame.size.height / 2) 33 | toVC.view.center = CGPointMake(finalCenter.x, finalCenter.y - 1000) 34 | 35 | let options = UIViewAnimationOptions.CurveEaseIn 36 | 37 | UIView.animateWithDuration(self.transitionDuration(transitionContext), 38 | delay: 0.0, 39 | usingSpringWithDamping: 0.64, 40 | initialSpringVelocity: 0.22, 41 | options: options, 42 | animations: { 43 | toVC.view.center = finalCenter 44 | toVC.transitioningBackgroundView.alpha = 0.7 45 | }, 46 | completion: { finished in 47 | toVC.view.center = finalCenter 48 | transitionContext.completeTransition(true) 49 | } 50 | ) 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /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 : NSDate = NSDate() 19 | var transitioningBackgroundView : UIView = UIView() 20 | 21 | @IBAction func save(sender: AnyObject) { 22 | eventHandler?.saveAddActionWithName(nameTextField.text, dueDate: datePicker!.date) 23 | } 24 | 25 | @IBAction func cancel(sender: AnyObject) { 26 | nameTextField.resignFirstResponder() 27 | eventHandler?.cancelAddAction() 28 | } 29 | 30 | override func viewDidAppear(animated: Bool) { 31 | super.viewDidAppear(animated) 32 | 33 | var gestureRecognizer = UITapGestureRecognizer() 34 | gestureRecognizer.addTarget(self, action: Selector("dismiss")) 35 | 36 | transitioningBackgroundView.userInteractionEnabled = true 37 | 38 | nameTextField.becomeFirstResponder() 39 | 40 | if let realDatePicker = datePicker { 41 | realDatePicker.minimumDate = minimumDate 42 | } 43 | } 44 | 45 | override func viewWillDisappear(animated: Bool) { 46 | super.viewWillDisappear(animated) 47 | 48 | nameTextField.resignFirstResponder() 49 | } 50 | 51 | func dismiss() { 52 | eventHandler?.cancelAddAction() 53 | } 54 | 55 | func setEntryName(name: NSString) { 56 | nameTextField.text = name 57 | } 58 | 59 | func setEntryDueDate(date: NSDate) { 60 | if let realDatePicker = datePicker { 61 | realDatePicker.minimumDate = date 62 | } 63 | } 64 | 65 | func setMinimumDueDate(date: NSDate) { 66 | minimumDate = date 67 | 68 | if let realDatePicker = datePicker { 69 | realDatePicker.minimumDate = date 70 | } 71 | } 72 | 73 | func textFieldShouldReturn(textField: UITextField!) -> Bool { 74 | textField.resignFirstResponder() 75 | 76 | return true 77 | } 78 | } -------------------------------------------------------------------------------- /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 | extension Array { 13 | func lastObject() -> T { 14 | let endIndex = self.endIndex 15 | let lastItemIndex = endIndex - 1 16 | 17 | return self[lastItemIndex] 18 | } 19 | } 20 | 21 | class CoreDataStore : NSObject { 22 | var persistentStoreCoordinator : NSPersistentStoreCoordinator? 23 | var managedObjectModel : NSManagedObjectModel? 24 | var managedObjectContext : NSManagedObjectContext? 25 | 26 | init() { 27 | managedObjectModel = NSManagedObjectModel.mergedModelFromBundles(nil) 28 | 29 | persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) 30 | 31 | let domains = NSSearchPathDomainMask.UserDomainMask 32 | let directory = NSSearchPathDirectory.DocumentDirectory 33 | 34 | let error = NSError() 35 | let applicationDocumentsDirectory : AnyObject = NSFileManager.defaultManager().URLsForDirectory(directory, inDomains: domains).lastObject() 36 | let options = [NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true] 37 | 38 | let storeURL = applicationDocumentsDirectory.URLByAppendingPathComponent("VIPER-SWIFT.sqlite") 39 | 40 | persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: "", URL: storeURL, options: options, error: nil) 41 | 42 | managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType) 43 | managedObjectContext!.persistentStoreCoordinator = persistentStoreCoordinator 44 | managedObjectContext!.undoManager = nil 45 | 46 | super.init() 47 | } 48 | 49 | func fetchEntriesWithPredicate(predicate: NSPredicate, sortDescriptors: AnyObject[], completionBlock: ((ManagedTodoItem[]) -> Void)!) { 50 | let fetchRequest = NSFetchRequest(entityName: "TodoItem") 51 | fetchRequest.predicate = predicate 52 | fetchRequest.sortDescriptors = [] 53 | 54 | managedObjectContext?.performBlock { 55 | let queryResults = self.managedObjectContext?.executeFetchRequest(fetchRequest, error: nil) 56 | let managedResults = queryResults! as ManagedTodoItem[] 57 | completionBlock(managedResults) 58 | } 59 | } 60 | 61 | func newTodoItem() -> ManagedTodoItem { 62 | let entityDescription = NSEntityDescription.entityForName("TodoItem", inManagedObjectContext: managedObjectContext) 63 | let newEntry = NSManagedObject(entity: entityDescription, insertIntoManagedObjectContext: managedObjectContext) as ManagedTodoItem 64 | 65 | return newEntry 66 | } 67 | 68 | func save() { 69 | managedObjectContext?.save(nil) 70 | } 71 | } -------------------------------------------------------------------------------- /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("didTapAddButton")) 38 | 39 | navigationItem.rightBarButtonItem = addItem 40 | } 41 | 42 | func didTapAddButton () { 43 | eventHandler?.addNewEntry() 44 | } 45 | 46 | func showNoContentMessage() { 47 | view = noContentView 48 | } 49 | 50 | func showUpcomingDisplayData(data: UpcomingDisplayData) { 51 | view = strongTableView 52 | 53 | dataProperty = data 54 | reloadEntries() 55 | } 56 | 57 | func reloadEntries() { 58 | tableView.reloadData() 59 | } 60 | 61 | override func numberOfSectionsInTableView(tableView: UITableView!) -> Int { 62 | var numberOfSections = dataProperty?.sections.count 63 | 64 | if dataProperty?.sections.count == nil { 65 | numberOfSections = 0 66 | } 67 | 68 | return numberOfSections! 69 | } 70 | 71 | override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int { 72 | let upcomingSection = dataProperty?.sections[section] 73 | return upcomingSection!.items.count 74 | } 75 | 76 | override func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String! { 77 | let upcomingSection = dataProperty?.sections[section] 78 | return upcomingSection!.name 79 | } 80 | 81 | override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! { 82 | let upcomingSection = dataProperty?.sections[indexPath.section] 83 | let upcomingItem = upcomingSection!.items[indexPath.row] 84 | 85 | let cell = tableView.dequeueReusableCellWithIdentifier(ListEntryCellIdentifier, forIndexPath: indexPath) as UITableViewCell 86 | 87 | cell.textLabel.text = upcomingItem.title; 88 | cell.detailTextLabel.text = upcomingItem.dueDate; 89 | cell.imageView.image = UIImage(named: upcomingSection!.imageName) 90 | cell.selectionStyle = UITableViewCellSelectionStyle.None; 91 | 92 | return cell 93 | } 94 | } -------------------------------------------------------------------------------- /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 = NSCalendar() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | calendar = NSCalendar.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 = NSCalendar() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | calendar = NSCalendar.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 = NSDateFormatter() 13 | var sections : Dictionary = Dictionary() 14 | 15 | init() { 16 | dayFormatter.dateFormat = NSDateFormatter.dateFormatFromTemplate("EEEE", options: 0, locale: NSLocale.autoupdatingCurrentLocale()) 17 | } 18 | 19 | func addUpcomingItems(upcomingItems: UpcomingItem[]) { 20 | for upcomingItem in upcomingItems { 21 | addUpcomingItem(upcomingItem) 22 | } 23 | } 24 | 25 | func addUpcomingItem(upcomingItem: UpcomingItem) { 26 | var 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, dateRelation: upcomingItem.dateRelation) 43 | let displayItem = UpcomingDisplayItem(title: upcomingItem.title, dueDate: day) 44 | return displayItem 45 | } 46 | 47 | func formattedDay(date: NSDate, dateRelation: NearTermDateRelation) -> String { 48 | if dateRelation == NearTermDateRelation.Today { 49 | return "" 50 | } 51 | 52 | return dayFormatter.stringFromDate(date) 53 | } 54 | 55 | func collectedDisplayData() -> UpcomingDisplayData { 56 | let collectedSections : UpcomingDisplaySection[] = sortedUpcomingDisplaySections() 57 | return UpcomingDisplayData(sections: collectedSections) 58 | } 59 | 60 | func displaySectionForDateRelation(dateRelation: NearTermDateRelation) -> UpcomingDisplaySection { 61 | let sectionTitle = sectionTitleForDateRelation(dateRelation) 62 | let imageName = sectionImageNameForDateRelation(dateRelation) 63 | let items = sections[dateRelation] 64 | 65 | return UpcomingDisplaySection(name: sectionTitle, imageName: imageName, items: items) 66 | } 67 | 68 | func sortedUpcomingDisplaySections() -> UpcomingDisplaySection[] { 69 | let keys = sortedNearTermDateRelations() 70 | var displaySections : UpcomingDisplaySection[] = [] 71 | 72 | for dateRelation in keys { 73 | var itemArray = sections[dateRelation] 74 | 75 | if itemArray { 76 | var displaySection = displaySectionForDateRelation(dateRelation) 77 | displaySections.insert(displaySection, atIndex: displaySections.endIndex) 78 | } 79 | } 80 | 81 | return displaySections 82 | } 83 | 84 | func sortedNearTermDateRelations() -> NearTermDateRelation[] { 85 | var array : NearTermDateRelation[] = [] 86 | array.insert(NearTermDateRelation.Today, atIndex: 0) 87 | array.insert(NearTermDateRelation.Tomorrow, atIndex: 1) 88 | array.insert(NearTermDateRelation.LaterThisWeek, atIndex: 2) 89 | array.insert(NearTermDateRelation.NextWeek, atIndex: 3) 90 | return array 91 | } 92 | 93 | func sectionTitleForDateRelation(dateRelation: NearTermDateRelation) -> String { 94 | switch dateRelation { 95 | case .Today: 96 | return "Today" 97 | case .Tomorrow: 98 | return "Tomorrow" 99 | case .LaterThisWeek: 100 | return "Later This Week" 101 | case .NextWeek: 102 | return "Next Week" 103 | case .OutOfRange: 104 | return "Unknown" 105 | } 106 | } 107 | 108 | func sectionImageNameForDateRelation(dateRelation: NearTermDateRelation) -> String { 109 | switch dateRelation { 110 | case .Today: 111 | return "check" 112 | case .Tomorrow: 113 | return "alarm" 114 | case .LaterThisWeek: 115 | return "circle" 116 | case .NextWeek: 117 | return "calendar" 118 | case .OutOfRange: 119 | return "paper" 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /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 NSCalendar { 12 | class func gregorianCalendar() -> NSCalendar { 13 | return NSCalendar(calendarIdentifier: NSGregorianCalendar) 14 | } 15 | 16 | func dateWithYear(year: Int, month: Int, day: Int) -> NSDate { 17 | let components = NSDateComponents() 18 | components.year = year 19 | components.month = month 20 | components.day = day 21 | components.hour = 12 22 | return dateFromComponents(components) 23 | } 24 | 25 | func dateForTomorrowRelativeToToday(today: NSDate) -> NSDate { 26 | let tomorrowComponents = NSDateComponents() 27 | tomorrowComponents.day = 1 28 | return dateByAddingComponents(tomorrowComponents, toDate: today, options: nil) 29 | } 30 | 31 | func dateForEndOfWeekWithDate(date: NSDate) -> NSDate { 32 | let daysRemainingThisWeek = daysRemainingInWeekWithDate(date) 33 | let remainingDaysComponent = NSDateComponents() 34 | remainingDaysComponent.day = daysRemainingThisWeek 35 | return dateByAddingComponents(remainingDaysComponent, toDate: date, options: nil) 36 | } 37 | 38 | func dateForBeginningOfDay(date: NSDate) -> NSDate { 39 | let newComponent = components((NSCalendarUnit.YearCalendarUnit | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay), fromDate: date) 40 | let newDate = dateFromComponents(newComponent) 41 | return newDate 42 | } 43 | 44 | func dateForEndOfDay(date: NSDate) -> NSDate { 45 | let components = NSDateComponents() 46 | components.day = 1 47 | let toDate = dateForBeginningOfDay(date) 48 | let nextDay = dateByAddingComponents(components, toDate: toDate, options: nil) 49 | let endDay = nextDay.dateByAddingTimeInterval(-1) 50 | return nextDay 51 | } 52 | 53 | func daysRemainingInWeekWithDate(date: NSDate) -> Int { 54 | let weekdayComponent = components(NSCalendarUnit.WeekdayCalendarUnit, fromDate: date) 55 | let daysRange = rangeOfUnit(NSCalendarUnit.WeekdayCalendarUnit, inUnit: NSCalendarUnit.WeekCalendarUnit, forDate: date) 56 | let daysPerWeek = daysRange.length 57 | let daysRemaining = daysPerWeek - weekdayComponent.weekday 58 | return daysRemaining 59 | } 60 | 61 | func dateForEndOfFollowingWeekWithDate(date: NSDate) -> NSDate { 62 | let endOfWeek = dateForEndOfWeekWithDate(date) 63 | let nextWeekComponent = NSDateComponents() 64 | nextWeekComponent.setWeek(1) 65 | let followingWeekDate = dateByAddingComponents(nextWeekComponent, toDate: endOfWeek, options: nil) 66 | return followingWeekDate 67 | } 68 | 69 | func isDate(date: NSDate, beforeYearMonthDay: NSDate) -> Bool { 70 | let comparison = compareYearMonthDay(date, toYearMonthDay: beforeYearMonthDay) 71 | let result = comparison == NSComparisonResult.OrderedAscending 72 | return result 73 | } 74 | 75 | func isDate(date: NSDate, equalToYearMonthDay: NSDate) -> Bool { 76 | let comparison = compareYearMonthDay(date, toYearMonthDay: equalToYearMonthDay) 77 | let result = comparison == NSComparisonResult.OrderedSame 78 | return result 79 | } 80 | 81 | func isDate(date: NSDate, duringSameWeekAsDate: NSDate) -> Bool { 82 | let dateComponents = components(NSCalendarUnit.WeekCalendarUnit, fromDate: date) 83 | let duringSameWeekComponents = components(NSCalendarUnit.WeekCalendarUnit, fromDate: duringSameWeekAsDate) 84 | let result = dateComponents.week() == duringSameWeekComponents.week() 85 | return result 86 | } 87 | 88 | func isDate(date: NSDate, duringWeekAfterDate: NSDate) -> Bool { 89 | let nextWeek = dateForEndOfFollowingWeekWithDate(duringWeekAfterDate) 90 | let dateComponents = components(NSCalendarUnit.WeekCalendarUnit, fromDate: date) 91 | let nextWeekComponents = components(NSCalendarUnit.WeekCalendarUnit, fromDate: nextWeek) 92 | let result = dateComponents.week() == nextWeekComponents.week() 93 | return result 94 | } 95 | 96 | func compareYearMonthDay(date: NSDate, toYearMonthDay: NSDate) -> NSComparisonResult { 97 | let dateComponents = yearMonthDayComponentsFromDate(date) 98 | let yearMonthDayComponents = yearMonthDayComponentsFromDate(toYearMonthDay) 99 | 100 | var result = compareInteger(dateComponents.year, right: yearMonthDayComponents.year) 101 | 102 | if result == NSComparisonResult.OrderedSame { 103 | result = compareInteger(dateComponents.month, right: yearMonthDayComponents.month) 104 | 105 | if result == NSComparisonResult.OrderedSame { 106 | result = compareInteger(dateComponents.day, right: yearMonthDayComponents.day) 107 | } 108 | } 109 | 110 | return result 111 | } 112 | 113 | func yearMonthDayComponentsFromDate(date: NSDate) -> NSDateComponents { 114 | let newComponents = components((NSCalendarUnit.YearCalendarUnit | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay), fromDate: date) 115 | return newComponents 116 | } 117 | 118 | func compareInteger(left: Int, right: Int) -> NSComparisonResult { 119 | var result = NSComparisonResult.OrderedDescending 120 | 121 | if left == right { 122 | result = NSComparisonResult.OrderedSame 123 | } else if left < right { 124 | result = NSComparisonResult.OrderedAscending 125 | } else { 126 | result = NSComparisonResult.OrderedDescending 127 | } 128 | 129 | return result 130 | } 131 | 132 | func nearTermRelationForDate(date: NSDate, relativeToToday: NSDate) -> NearTermDateRelation { 133 | var relation = NearTermDateRelation.OutOfRange 134 | 135 | let dateForTomorrow = dateForTomorrowRelativeToToday(relativeToToday) 136 | 137 | let isDateBeforeYearMonthDay = isDate(date, beforeYearMonthDay: relativeToToday) 138 | let isDateEqualToYearMonthDay = isDate(date, equalToYearMonthDay: relativeToToday) 139 | let isDateEqualToYearMonthDayRelativeToTomorrow = isDate(date, equalToYearMonthDay: dateForTomorrow) 140 | let isDateDuringSameWeekAsDate = isDate(date, duringSameWeekAsDate: relativeToToday) 141 | let isDateDuringSameWeekAfterDate = isDate(date, duringWeekAfterDate: relativeToToday) 142 | 143 | if isDateBeforeYearMonthDay { 144 | relation = NearTermDateRelation.OutOfRange 145 | } else if isDateEqualToYearMonthDay { 146 | relation = NearTermDateRelation.Today 147 | } else if isDateEqualToYearMonthDayRelativeToTomorrow { 148 | let isRelativeDateDuringSameWeek = isDate(relativeToToday, duringSameWeekAsDate: date) 149 | 150 | if isRelativeDateDuringSameWeek { 151 | relation = NearTermDateRelation.Tomorrow 152 | } else { 153 | relation = NearTermDateRelation.NextWeek 154 | } 155 | } else if isDateDuringSameWeekAsDate { 156 | relation = NearTermDateRelation.LaterThisWeek 157 | } else if isDateDuringSameWeekAfterDate { 158 | relation = NearTermDateRelation.NextWeek 159 | } 160 | 161 | return relation 162 | } 163 | } -------------------------------------------------------------------------------- /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 | 67 | 74 | 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 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /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 | 261FA19219414A720029F589 /* Model */, 288 | ); 289 | path = "Application Logic"; 290 | sourceTree = ""; 291 | }; 292 | 261FA18E19414A720029F589 /* Interactor */ = { 293 | isa = PBXGroup; 294 | children = ( 295 | 261FA18F19414A720029F589 /* AddInteractor.swift */, 296 | ); 297 | path = Interactor; 298 | sourceTree = ""; 299 | }; 300 | 261FA19019414A720029F589 /* Manager */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | 261FA19119414A720029F589 /* AddDataManager.swift */, 304 | ); 305 | path = Manager; 306 | sourceTree = ""; 307 | }; 308 | 261FA19219414A720029F589 /* Model */ = { 309 | isa = PBXGroup; 310 | children = ( 311 | ); 312 | path = Model; 313 | sourceTree = ""; 314 | }; 315 | 261FA19319414A720029F589 /* Module Interface */ = { 316 | isa = PBXGroup; 317 | children = ( 318 | 261FA19419414A720029F589 /* AddModuleDelegateInterface.swift */, 319 | 261FA19519414A720029F589 /* AddModuleInterface.swift */, 320 | ); 321 | path = "Module Interface"; 322 | sourceTree = ""; 323 | }; 324 | 261FA19619414A720029F589 /* User Interface */ = { 325 | isa = PBXGroup; 326 | children = ( 327 | 261FA19719414A720029F589 /* Presenter */, 328 | 261FA19919414A720029F589 /* Transition */, 329 | 261FA19C19414A720029F589 /* View */, 330 | 261FA19F19414A720029F589 /* Wireframe */, 331 | ); 332 | path = "User Interface"; 333 | sourceTree = ""; 334 | }; 335 | 261FA19719414A720029F589 /* Presenter */ = { 336 | isa = PBXGroup; 337 | children = ( 338 | 261FA19819414A720029F589 /* AddPresenter.swift */, 339 | ); 340 | path = Presenter; 341 | sourceTree = ""; 342 | }; 343 | 261FA19919414A720029F589 /* Transition */ = { 344 | isa = PBXGroup; 345 | children = ( 346 | 261FA19A19414A720029F589 /* AddDismissalTransition.swift */, 347 | 261FA19B19414A720029F589 /* AddPresentationTransition.swift */, 348 | ); 349 | path = Transition; 350 | sourceTree = ""; 351 | }; 352 | 261FA19C19414A720029F589 /* View */ = { 353 | isa = PBXGroup; 354 | children = ( 355 | 261FA19D19414A720029F589 /* AddViewController.swift */, 356 | 261FA19E19414A720029F589 /* AddViewInterface.swift */, 357 | ); 358 | path = View; 359 | sourceTree = ""; 360 | }; 361 | 261FA19F19414A720029F589 /* Wireframe */ = { 362 | isa = PBXGroup; 363 | children = ( 364 | 261FA1A019414A720029F589 /* AddWireframe.swift */, 365 | ); 366 | path = Wireframe; 367 | sourceTree = ""; 368 | }; 369 | 261FA1A119414A720029F589 /* List */ = { 370 | isa = PBXGroup; 371 | children = ( 372 | 261FA1A219414A720029F589 /* Application Logic */, 373 | 261FA1AA19414A720029F589 /* Module Interface */, 374 | 261FA1AC19414A720029F589 /* User Interface */, 375 | ); 376 | path = List; 377 | sourceTree = ""; 378 | }; 379 | 261FA1A219414A720029F589 /* Application Logic */ = { 380 | isa = PBXGroup; 381 | children = ( 382 | 261FA1A319414A720029F589 /* Interactor */, 383 | 261FA1A719414A720029F589 /* Manager */, 384 | 261FA1A919414A720029F589 /* Model */, 385 | ); 386 | path = "Application Logic"; 387 | sourceTree = ""; 388 | }; 389 | 261FA1A319414A720029F589 /* Interactor */ = { 390 | isa = PBXGroup; 391 | children = ( 392 | 261FA1A419414A720029F589 /* ListInteractor.swift */, 393 | 261FA1A519414A720029F589 /* ListInteractorIO.swift */, 394 | 261FA1A619414A720029F589 /* UpcomingItem.swift */, 395 | ); 396 | path = Interactor; 397 | sourceTree = ""; 398 | }; 399 | 261FA1A719414A720029F589 /* Manager */ = { 400 | isa = PBXGroup; 401 | children = ( 402 | 261FA1A819414A720029F589 /* ListDataManager.swift */, 403 | ); 404 | path = Manager; 405 | sourceTree = ""; 406 | }; 407 | 261FA1A919414A720029F589 /* Model */ = { 408 | isa = PBXGroup; 409 | children = ( 410 | ); 411 | path = Model; 412 | sourceTree = ""; 413 | }; 414 | 261FA1AA19414A720029F589 /* Module Interface */ = { 415 | isa = PBXGroup; 416 | children = ( 417 | 261FA1AB19414A720029F589 /* ListModuleInterface.swift */, 418 | ); 419 | path = "Module Interface"; 420 | sourceTree = ""; 421 | }; 422 | 261FA1AC19414A720029F589 /* User Interface */ = { 423 | isa = PBXGroup; 424 | children = ( 425 | 261FA1AD19414A720029F589 /* Presenter */, 426 | 261FA1B319414A720029F589 /* View */, 427 | 261FA1B619414A720029F589 /* Wireframe */, 428 | ); 429 | path = "User Interface"; 430 | sourceTree = ""; 431 | }; 432 | 261FA1AD19414A720029F589 /* Presenter */ = { 433 | isa = PBXGroup; 434 | children = ( 435 | 261FA1AE19414A720029F589 /* ListPresenter.swift */, 436 | 261FA1AF19414A720029F589 /* UpcomingDisplayData.swift */, 437 | 261FA1B019414A720029F589 /* UpcomingDisplayDataCollection.swift */, 438 | 261FA1B119414A720029F589 /* UpcomingDisplayItem.swift */, 439 | 261FA1B219414A720029F589 /* UpcomingDisplaySection.swift */, 440 | ); 441 | path = Presenter; 442 | sourceTree = ""; 443 | }; 444 | 261FA1B319414A720029F589 /* View */ = { 445 | isa = PBXGroup; 446 | children = ( 447 | 261FA1B419414A720029F589 /* ListViewController.swift */, 448 | 261FA1B519414A720029F589 /* ListViewInterface.swift */, 449 | ); 450 | path = View; 451 | sourceTree = ""; 452 | }; 453 | 261FA1B619414A720029F589 /* Wireframe */ = { 454 | isa = PBXGroup; 455 | children = ( 456 | 261FA1B719414A720029F589 /* ListWireframe.swift */, 457 | ); 458 | path = Wireframe; 459 | sourceTree = ""; 460 | }; 461 | 261FA1DD19414AC20029F589 /* Resources */ = { 462 | isa = PBXGroup; 463 | children = ( 464 | 261FA1EA1942320E0029F589 /* Main.storyboard */, 465 | 261FA1ED194232180029F589 /* Images.xcassets */, 466 | ); 467 | name = Resources; 468 | path = "../../../../Documents/Repos/VIPER-TODO-SWIFT/viper-todo-swift/VIPER TODO/Resources"; 469 | sourceTree = ""; 470 | }; 471 | 261FA1DE19414AC60029F589 /* Supporting Files */ = { 472 | isa = PBXGroup; 473 | children = ( 474 | 261FA1E819414E600029F589 /* Info.plist */, 475 | ); 476 | name = "Supporting Files"; 477 | sourceTree = ""; 478 | }; 479 | /* End PBXGroup section */ 480 | 481 | /* Begin PBXNativeTarget section */ 482 | 261FA14D1941497E0029F589 /* VIPER-SWIFT */ = { 483 | isa = PBXNativeTarget; 484 | buildConfigurationList = 261FA16A1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFT" */; 485 | buildPhases = ( 486 | 261FA14A1941497E0029F589 /* Sources */, 487 | 261FA14B1941497E0029F589 /* Frameworks */, 488 | 261FA14C1941497E0029F589 /* Resources */, 489 | ); 490 | buildRules = ( 491 | ); 492 | dependencies = ( 493 | ); 494 | name = "VIPER-SWIFT"; 495 | productName = "VIPER-SWIFT"; 496 | productReference = 261FA14E1941497E0029F589 /* VIPER-SWIFT.app */; 497 | productType = "com.apple.product-type.application"; 498 | }; 499 | 261FA15F1941497E0029F589 /* VIPER-SWIFTTests */ = { 500 | isa = PBXNativeTarget; 501 | buildConfigurationList = 261FA16D1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFTTests" */; 502 | buildPhases = ( 503 | 261FA15C1941497E0029F589 /* Sources */, 504 | 261FA15D1941497E0029F589 /* Frameworks */, 505 | 261FA15E1941497E0029F589 /* Resources */, 506 | ); 507 | buildRules = ( 508 | ); 509 | dependencies = ( 510 | 261FA1621941497E0029F589 /* PBXTargetDependency */, 511 | ); 512 | name = "VIPER-SWIFTTests"; 513 | productName = "VIPER-SWIFTTests"; 514 | productReference = 261FA1601941497E0029F589 /* VIPER-SWIFTTests.xctest */; 515 | productType = "com.apple.product-type.bundle.unit-test"; 516 | }; 517 | /* End PBXNativeTarget section */ 518 | 519 | /* Begin PBXProject section */ 520 | 261FA1461941497E0029F589 /* Project object */ = { 521 | isa = PBXProject; 522 | attributes = { 523 | LastUpgradeCheck = 0600; 524 | ORGANIZATIONNAME = "Conrad Stoll"; 525 | TargetAttributes = { 526 | 261FA14D1941497E0029F589 = { 527 | CreatedOnToolsVersion = 6.0; 528 | }; 529 | 261FA15F1941497E0029F589 = { 530 | CreatedOnToolsVersion = 6.0; 531 | TestTargetID = 261FA14D1941497E0029F589; 532 | }; 533 | }; 534 | }; 535 | buildConfigurationList = 261FA1491941497E0029F589 /* Build configuration list for PBXProject "VIPER-SWIFT" */; 536 | compatibilityVersion = "Xcode 3.2"; 537 | developmentRegion = English; 538 | hasScannedForEncodings = 0; 539 | knownRegions = ( 540 | en, 541 | Base, 542 | ); 543 | mainGroup = 261FA1451941497E0029F589; 544 | productRefGroup = 261FA14F1941497E0029F589 /* Products */; 545 | projectDirPath = ""; 546 | projectRoot = ""; 547 | targets = ( 548 | 261FA14D1941497E0029F589 /* VIPER-SWIFT */, 549 | 261FA15F1941497E0029F589 /* VIPER-SWIFTTests */, 550 | ); 551 | }; 552 | /* End PBXProject section */ 553 | 554 | /* Begin PBXResourcesBuildPhase section */ 555 | 261FA14C1941497E0029F589 /* Resources */ = { 556 | isa = PBXResourcesBuildPhase; 557 | buildActionMask = 2147483647; 558 | files = ( 559 | 261FA1EE194232180029F589 /* Images.xcassets in Resources */, 560 | 261FA1EC1942320E0029F589 /* Main.storyboard in Resources */, 561 | ); 562 | runOnlyForDeploymentPostprocessing = 0; 563 | }; 564 | 261FA15E1941497E0029F589 /* Resources */ = { 565 | isa = PBXResourcesBuildPhase; 566 | buildActionMask = 2147483647; 567 | files = ( 568 | 26D96FA91942932A00F117F5 /* Main.storyboard in Resources */, 569 | 26D96FAA1942932A00F117F5 /* Images.xcassets in Resources */, 570 | ); 571 | runOnlyForDeploymentPostprocessing = 0; 572 | }; 573 | /* End PBXResourcesBuildPhase section */ 574 | 575 | /* Begin PBXSourcesBuildPhase section */ 576 | 261FA14A1941497E0029F589 /* Sources */ = { 577 | isa = PBXSourcesBuildPhase; 578 | buildActionMask = 2147483647; 579 | files = ( 580 | 261FA1C719414A720029F589 /* AddDataManager.swift in Sources */, 581 | 261FA1D119414A720029F589 /* ListInteractorIO.swift in Sources */, 582 | 261FA1D719414A720029F589 /* UpcomingDisplayDataCollection.swift in Sources */, 583 | 261FA1D219414A720029F589 /* UpcomingItem.swift in Sources */, 584 | 261FA1D519414A720029F589 /* ListPresenter.swift in Sources */, 585 | 261FA1D419414A720029F589 /* ListModuleInterface.swift in Sources */, 586 | 261FA1B919414A720029F589 /* AppDependencies.swift in Sources */, 587 | 261FA1D619414A720029F589 /* UpcomingDisplayData.swift in Sources */, 588 | 261FA1C619414A720029F589 /* AddInteractor.swift in Sources */, 589 | 261FA1D919414A720029F589 /* UpcomingDisplaySection.swift in Sources */, 590 | 261FA1C819414A720029F589 /* AddModuleDelegateInterface.swift in Sources */, 591 | 261FA1BF19414A720029F589 /* DeviceClock.swift in Sources */, 592 | 261FA1DA19414A720029F589 /* ListViewController.swift in Sources */, 593 | 261FA1BE19414A720029F589 /* Clock.swift in Sources */, 594 | 261FA1C519414A720029F589 /* RootWireframe.swift in Sources */, 595 | 261FA1C419414A720029F589 /* TODO.xcdatamodeld in Sources */, 596 | 261FA1CD19414A720029F589 /* AddViewController.swift in Sources */, 597 | 261FA1C119414A720029F589 /* TodoItem.swift in Sources */, 598 | 26A605EB1942A1EE0036C71F /* AppDelegate.swift in Sources */, 599 | 261FA1CB19414A720029F589 /* AddDismissalTransition.swift in Sources */, 600 | 261FA1C219414A720029F589 /* CoreDataStore.swift in Sources */, 601 | 261FA1CC19414A720029F589 /* AddPresentationTransition.swift in Sources */, 602 | 261FA1C919414A720029F589 /* AddModuleInterface.swift in Sources */, 603 | 261FA1D319414A720029F589 /* ListDataManager.swift in Sources */, 604 | 261FA1CA19414A720029F589 /* AddPresenter.swift in Sources */, 605 | 261FA1D019414A720029F589 /* ListInteractor.swift in Sources */, 606 | 261FA1C019414A720029F589 /* NearTermDateRelation.swift in Sources */, 607 | 261FA1E719414CBE0029F589 /* ManagedTodoItem.swift in Sources */, 608 | 261FA1D819414A720029F589 /* UpcomingDisplayItem.swift in Sources */, 609 | 261FA1BC19414A720029F589 /* NSCalendar+CalendarAdditions.swift in Sources */, 610 | 261FA1DB19414A720029F589 /* ListViewInterface.swift in Sources */, 611 | 261FA1DC19414A720029F589 /* ListWireframe.swift in Sources */, 612 | 261FA1CE19414A720029F589 /* AddViewInterface.swift in Sources */, 613 | 261FA1CF19414A720029F589 /* AddWireframe.swift in Sources */, 614 | ); 615 | runOnlyForDeploymentPostprocessing = 0; 616 | }; 617 | 261FA15C1941497E0029F589 /* Sources */ = { 618 | isa = PBXSourcesBuildPhase; 619 | buildActionMask = 2147483647; 620 | files = ( 621 | 26D96FB219429B1A00F117F5 /* ListDataManager.swift in Sources */, 622 | 26D96FAF19429AD000F117F5 /* ManagedTodoItem.swift in Sources */, 623 | 26D96F9D19428C7E00F117F5 /* NSCalendar+CalendarAdditions.swift in Sources */, 624 | 26D96FAC1942967900F117F5 /* RelativeDateTests.swift in Sources */, 625 | 26D96FAE19429A9000F117F5 /* DataTests.swift in Sources */, 626 | 26D96FA71942913B00F117F5 /* ListModuleInterface.swift in Sources */, 627 | 26D96FA41942912A00F117F5 /* UpcomingDisplaySection.swift in Sources */, 628 | 26D96FA31942912A00F117F5 /* UpcomingDisplayItem.swift in Sources */, 629 | 26D96F9C19428C3600F117F5 /* CalendarTests.swift in Sources */, 630 | 26D96FA61942913600F117F5 /* ListViewInterface.swift in Sources */, 631 | 26D96FB019429AD000F117F5 /* CoreDataStore.swift in Sources */, 632 | 26D96FA11942912A00F117F5 /* UpcomingDisplayData.swift in Sources */, 633 | 26D96FA81942915D00F117F5 /* UpcomingItem.swift in Sources */, 634 | 26D96F9E19428D8100F117F5 /* NearTermDateRelation.swift in Sources */, 635 | 26D96FA51942912A00F117F5 /* ListViewController.swift in Sources */, 636 | 26D96FB119429B1A00F117F5 /* TodoItem.swift in Sources */, 637 | 26D96FA21942912A00F117F5 /* UpcomingDisplayDataCollection.swift in Sources */, 638 | ); 639 | runOnlyForDeploymentPostprocessing = 0; 640 | }; 641 | /* End PBXSourcesBuildPhase section */ 642 | 643 | /* Begin PBXTargetDependency section */ 644 | 261FA1621941497E0029F589 /* PBXTargetDependency */ = { 645 | isa = PBXTargetDependency; 646 | target = 261FA14D1941497E0029F589 /* VIPER-SWIFT */; 647 | targetProxy = 261FA1611941497E0029F589 /* PBXContainerItemProxy */; 648 | }; 649 | /* End PBXTargetDependency section */ 650 | 651 | /* Begin PBXVariantGroup section */ 652 | 261FA1EA1942320E0029F589 /* Main.storyboard */ = { 653 | isa = PBXVariantGroup; 654 | children = ( 655 | 261FA1EB1942320E0029F589 /* Base */, 656 | ); 657 | name = Main.storyboard; 658 | sourceTree = ""; 659 | }; 660 | /* End PBXVariantGroup section */ 661 | 662 | /* Begin XCBuildConfiguration section */ 663 | 261FA1681941497E0029F589 /* Debug */ = { 664 | isa = XCBuildConfiguration; 665 | buildSettings = { 666 | ALWAYS_SEARCH_USER_PATHS = NO; 667 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 668 | CLANG_CXX_LIBRARY = "libc++"; 669 | CLANG_ENABLE_MODULES = YES; 670 | CLANG_ENABLE_OBJC_ARC = YES; 671 | CLANG_WARN_BOOL_CONVERSION = YES; 672 | CLANG_WARN_CONSTANT_CONVERSION = YES; 673 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 674 | CLANG_WARN_EMPTY_BODY = YES; 675 | CLANG_WARN_ENUM_CONVERSION = YES; 676 | CLANG_WARN_INT_CONVERSION = YES; 677 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 678 | CLANG_WARN_UNREACHABLE_CODE = YES; 679 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 680 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 681 | COPY_PHASE_STRIP = NO; 682 | ENABLE_STRICT_OBJC_MSGSEND = YES; 683 | GCC_C_LANGUAGE_STANDARD = gnu99; 684 | GCC_DYNAMIC_NO_PIC = NO; 685 | GCC_OPTIMIZATION_LEVEL = 0; 686 | GCC_PREPROCESSOR_DEFINITIONS = ( 687 | "DEBUG=1", 688 | "$(inherited)", 689 | ); 690 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 691 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 692 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 693 | GCC_WARN_UNDECLARED_SELECTOR = YES; 694 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 695 | GCC_WARN_UNUSED_FUNCTION = YES; 696 | GCC_WARN_UNUSED_VARIABLE = YES; 697 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 698 | METAL_ENABLE_DEBUG_INFO = YES; 699 | ONLY_ACTIVE_ARCH = YES; 700 | SDKROOT = iphoneos; 701 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 702 | }; 703 | name = Debug; 704 | }; 705 | 261FA1691941497E0029F589 /* Release */ = { 706 | isa = XCBuildConfiguration; 707 | buildSettings = { 708 | ALWAYS_SEARCH_USER_PATHS = NO; 709 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 710 | CLANG_CXX_LIBRARY = "libc++"; 711 | CLANG_ENABLE_MODULES = YES; 712 | CLANG_ENABLE_OBJC_ARC = YES; 713 | CLANG_WARN_BOOL_CONVERSION = 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_INT_CONVERSION = YES; 719 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 720 | CLANG_WARN_UNREACHABLE_CODE = YES; 721 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 722 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 723 | COPY_PHASE_STRIP = YES; 724 | ENABLE_NS_ASSERTIONS = NO; 725 | ENABLE_STRICT_OBJC_MSGSEND = YES; 726 | GCC_C_LANGUAGE_STANDARD = gnu99; 727 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 728 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 729 | GCC_WARN_UNDECLARED_SELECTOR = YES; 730 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 731 | GCC_WARN_UNUSED_FUNCTION = YES; 732 | GCC_WARN_UNUSED_VARIABLE = YES; 733 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 734 | METAL_ENABLE_DEBUG_INFO = NO; 735 | SDKROOT = iphoneos; 736 | VALIDATE_PRODUCT = YES; 737 | }; 738 | name = Release; 739 | }; 740 | 261FA16B1941497E0029F589 /* Debug */ = { 741 | isa = XCBuildConfiguration; 742 | buildSettings = { 743 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 744 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 745 | INFOPLIST_FILE = "VIPER-SWIFT/Info.plist"; 746 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 747 | PRODUCT_NAME = "$(TARGET_NAME)"; 748 | }; 749 | name = Debug; 750 | }; 751 | 261FA16C1941497E0029F589 /* Release */ = { 752 | isa = XCBuildConfiguration; 753 | buildSettings = { 754 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 755 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 756 | INFOPLIST_FILE = "VIPER-SWIFT/Info.plist"; 757 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 758 | PRODUCT_NAME = "$(TARGET_NAME)"; 759 | }; 760 | name = Release; 761 | }; 762 | 261FA16E1941497E0029F589 /* Debug */ = { 763 | isa = XCBuildConfiguration; 764 | buildSettings = { 765 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/VIPER-SWIFT.app/VIPER-SWIFT"; 766 | FRAMEWORK_SEARCH_PATHS = ( 767 | "$(SDKROOT)/Developer/Library/Frameworks", 768 | "$(inherited)", 769 | ); 770 | GCC_PREPROCESSOR_DEFINITIONS = ( 771 | "DEBUG=1", 772 | "$(inherited)", 773 | ); 774 | INFOPLIST_FILE = "VIPER-SWIFTTests/Info.plist"; 775 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 776 | METAL_ENABLE_DEBUG_INFO = YES; 777 | PRODUCT_NAME = "$(TARGET_NAME)"; 778 | TEST_HOST = "$(BUNDLE_LOADER)"; 779 | }; 780 | name = Debug; 781 | }; 782 | 261FA16F1941497E0029F589 /* Release */ = { 783 | isa = XCBuildConfiguration; 784 | buildSettings = { 785 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/VIPER-SWIFT.app/VIPER-SWIFT"; 786 | FRAMEWORK_SEARCH_PATHS = ( 787 | "$(SDKROOT)/Developer/Library/Frameworks", 788 | "$(inherited)", 789 | ); 790 | INFOPLIST_FILE = "VIPER-SWIFTTests/Info.plist"; 791 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 792 | METAL_ENABLE_DEBUG_INFO = NO; 793 | PRODUCT_NAME = "$(TARGET_NAME)"; 794 | TEST_HOST = "$(BUNDLE_LOADER)"; 795 | }; 796 | name = Release; 797 | }; 798 | /* End XCBuildConfiguration section */ 799 | 800 | /* Begin XCConfigurationList section */ 801 | 261FA1491941497E0029F589 /* Build configuration list for PBXProject "VIPER-SWIFT" */ = { 802 | isa = XCConfigurationList; 803 | buildConfigurations = ( 804 | 261FA1681941497E0029F589 /* Debug */, 805 | 261FA1691941497E0029F589 /* Release */, 806 | ); 807 | defaultConfigurationIsVisible = 0; 808 | defaultConfigurationName = Release; 809 | }; 810 | 261FA16A1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFT" */ = { 811 | isa = XCConfigurationList; 812 | buildConfigurations = ( 813 | 261FA16B1941497E0029F589 /* Debug */, 814 | 261FA16C1941497E0029F589 /* Release */, 815 | ); 816 | defaultConfigurationIsVisible = 0; 817 | defaultConfigurationName = Release; 818 | }; 819 | 261FA16D1941497E0029F589 /* Build configuration list for PBXNativeTarget "VIPER-SWIFTTests" */ = { 820 | isa = XCConfigurationList; 821 | buildConfigurations = ( 822 | 261FA16E1941497E0029F589 /* Debug */, 823 | 261FA16F1941497E0029F589 /* Release */, 824 | ); 825 | defaultConfigurationIsVisible = 0; 826 | defaultConfigurationName = Release; 827 | }; 828 | /* End XCConfigurationList section */ 829 | 830 | /* Begin XCVersionGroup section */ 831 | 261FA18719414A720029F589 /* TODO.xcdatamodeld */ = { 832 | isa = XCVersionGroup; 833 | children = ( 834 | 261FA18819414A720029F589 /* TODO.xcdatamodel */, 835 | ); 836 | currentVersion = 261FA18819414A720029F589 /* TODO.xcdatamodel */; 837 | path = TODO.xcdatamodeld; 838 | sourceTree = ""; 839 | versionGroupType = wrapper.xcdatamodel; 840 | }; 841 | /* End XCVersionGroup section */ 842 | }; 843 | rootObject = 261FA1461941497E0029F589 /* Project object */; 844 | } 845 | --------------------------------------------------------------------------------