├── Chapter06 ├── .DS_Store ├── ToDo │ ├── .DS_Store │ ├── Token.swift │ ├── ViewController.swift │ ├── Controller │ │ ├── ItemCell.swift │ │ ├── DetailViewController.swift │ │ ├── ItemListViewController.swift │ │ ├── InputViewController.swift │ │ └── ItemListDataProvider.swift │ ├── Info.plist │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Model │ │ ├── Location.swift │ │ ├── ToDoItem.swift │ │ └── ItemManager.swift │ ├── APIClient.swift │ └── AppDelegate.swift ├── ToDo.xcodeproj │ ├── xcuserdata │ │ └── dom.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ └── dom.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── ToDoTests │ ├── Info.plist │ ├── StoryboardTests.swift │ ├── Controller │ │ ├── DetailViewControllerTests.swift │ │ └── ItemCellTests.swift │ └── Model │ │ └── ItemManagerTests.swift └── ToDoUITests │ ├── Info.plist │ └── ToDoUITests.swift ├── Chapter03 ├── ToDo │ ├── .DS_Store │ ├── ToDo.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── dom.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── dom.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── ToDo.xcscheme │ ├── ToDo │ │ ├── ViewController.swift │ │ ├── ItemManager.swift │ │ ├── Location.swift │ │ ├── ToDoItem.swift │ │ ├── Info.plist │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ └── AppDelegate.swift │ └── ToDoTests │ │ ├── Info.plist │ │ ├── ItemManagerTests.swift │ │ ├── ToDoItemTests.swift │ │ └── LocationTests.swift └── __MACOSX │ └── ToDo │ ├── ._.DS_Store │ ├── ._ToDo.xcodeproj │ ├── ToDo │ ├── ._Location.swift │ ├── ._ToDoItem.swift │ ├── ._AppDelegate.swift │ ├── ._ItemManager.swift │ ├── ._ViewController.swift │ ├── Base.lproj │ │ ├── ._Main.storyboard │ │ └── ._LaunchScreen.storyboard │ └── Assets.xcassets │ │ └── AppIcon.appiconset │ │ └── ._Contents.json │ └── ToDoTests │ ├── ._LocationTests.swift │ ├── ._ToDoItemTests.swift │ └── ._ItemManagerTests.swift ├── Chapter04 ├── __MACOSX │ └── ToDo_chapter04 │ │ ├── ._.DS_Store │ │ ├── ._ToDo.xcodeproj │ │ ├── ToDo │ │ ├── ._ItemCell.swift │ │ ├── ._AppDelegate.swift │ │ ├── ._ViewController.swift │ │ ├── Model │ │ │ ├── ._Location.swift │ │ │ ├── ._ToDoItem.swift │ │ │ └── ._ItemManager.swift │ │ ├── ._InputViewController.swift │ │ ├── ._DetailViewController.swift │ │ ├── ._ItemListDataProvider.swift │ │ ├── Base.lproj │ │ │ ├── ._Main.storyboard │ │ │ └── ._LaunchScreen.storyboard │ │ ├── ._ItemListViewController.swift │ │ └── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ └── ._Contents.json │ │ └── ToDoTests │ │ ├── ._ItemCellTests.swift │ │ ├── Model │ │ ├── ._LocationTests.swift │ │ ├── ._ToDoItemTests.swift │ │ └── ._ItemManagerTests.swift │ │ ├── ._InputViewControllerTests.swift │ │ ├── ._DetailViewControllerTests.swift │ │ ├── ._ItemListDataProviderTests.swift │ │ └── ._ItemListViewControllerTest.swift └── ToDo_chapter04 │ ├── .DS_Store │ ├── ToDo.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── dom.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── dom.xcuserdatad │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── ToDo.xcscheme │ ├── ToDo │ ├── ItemListViewController.swift │ ├── ViewController.swift │ ├── Model │ │ ├── Location.swift │ │ ├── ItemManager.swift │ │ └── ToDoItem.swift │ ├── ItemCell.swift │ ├── Info.plist │ ├── DetailViewController.swift │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── InputViewController.swift │ ├── AppDelegate.swift │ └── ItemListDataProvider.swift │ └── ToDoTests │ ├── Info.plist │ ├── ItemListViewControllerTest.swift │ ├── Model │ ├── ItemManagerTests.swift │ ├── ToDoItemTests.swift │ └── LocationTests.swift │ ├── DetailViewControllerTests.swift │ ├── ItemCellTests.swift │ └── InputViewControllerTests.swift ├── Chapter05 ├── __MACOSX │ └── ToDo_chapter05 │ │ ├── ._.DS_Store │ │ ├── ._ToDo.xcodeproj │ │ ├── ToDo │ │ ├── ._Token.swift │ │ ├── ._APIClient.swift │ │ ├── ._AppDelegate.swift │ │ ├── ._ViewController.swift │ │ ├── Model │ │ │ ├── ._Location.swift │ │ │ ├── ._ToDoItem.swift │ │ │ └── ._ItemManager.swift │ │ ├── Controller │ │ │ ├── ._ItemCell.swift │ │ │ ├── ._DetailViewController.swift │ │ │ ├── ._InputViewController.swift │ │ │ ├── ._ItemListDataProvider.swift │ │ │ └── ._ItemListViewController.swift │ │ ├── Base.lproj │ │ │ ├── ._Main.storyboard │ │ │ └── ._LaunchScreen.storyboard │ │ └── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ └── ._Contents.json │ │ └── ToDoTests │ │ ├── ._APIClientTests.swift │ │ ├── Model │ │ ├── ._LocationTests.swift │ │ ├── ._ToDoItemTests.swift │ │ └── ._ItemManagerTests.swift │ │ └── Controller │ │ ├── ._ItemCellTests.swift │ │ ├── ._DetailViewControllerTests.swift │ │ ├── ._InputViewControllerTests.swift │ │ ├── ._ItemListDataProviderTests.swift │ │ └── ._ItemListViewControllerTest.swift └── ToDo_chapter05 │ ├── .DS_Store │ ├── ToDo.xcodeproj │ ├── xcuserdata │ │ └── dom.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ └── dom.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ ├── ToDo │ ├── Token.swift │ ├── ViewController.swift │ ├── Controller │ │ ├── ItemListViewController.swift │ │ ├── ItemCell.swift │ │ ├── DetailViewController.swift │ │ ├── InputViewController.swift │ │ └── ItemListDataProvider.swift │ ├── Model │ │ ├── Location.swift │ │ ├── ItemManager.swift │ │ └── ToDoItem.swift │ ├── Info.plist │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── APIClient.swift │ └── AppDelegate.swift │ └── ToDoTests │ ├── Info.plist │ ├── Controller │ ├── ItemListViewControllerTest.swift │ ├── DetailViewControllerTests.swift │ └── ItemCellTests.swift │ └── Model │ ├── ItemManagerTests.swift │ ├── ToDoItemTests.swift │ └── LocationTests.swift ├── Software and hardware list.pdf ├── Chapter01 ├── FirstDemo │ ├── FirstDemo.xcodeproj │ │ ├── xcuserdata │ │ │ └── dom.xcuserdatad │ │ │ │ ├── xcdebugger │ │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ └── dom.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ ├── FirstDemoTests │ │ ├── Info.plist │ │ └── FirstDemoTests.swift │ └── FirstDemo │ │ ├── ViewController.swift │ │ ├── Info.plist │ │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── AppDelegate.swift └── __MACOSX │ └── FirstDemo │ ├── FirstDemo │ ├── ._AppDelegate.swift │ ├── ._ViewController.swift │ ├── Base.lproj │ │ ├── ._Main.storyboard │ │ └── ._LaunchScreen.storyboard │ └── Assets.xcassets │ │ └── AppIcon.appiconset │ │ └── ._Contents.json │ └── FirstDemoTests │ └── ._FirstDemoTests.swift ├── LICENSE └── README.md /Chapter06/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter06/.DS_Store -------------------------------------------------------------------------------- /Chapter03/ToDo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/ToDo/.DS_Store -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/._.DS_Store: -------------------------------------------------------------------------------- 1 | Mac OS X  2Fx ATTRxx -------------------------------------------------------------------------------- /Chapter06/ToDo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter06/ToDo/.DS_Store -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/._.DS_Store: -------------------------------------------------------------------------------- 1 | Mac OS X  2Fx ATTRxx -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/._.DS_Store: -------------------------------------------------------------------------------- 1 | Mac OS X  2Fx ATTRxx -------------------------------------------------------------------------------- /Software and hardware list.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Software and hardware list.pdf -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/ToDo_chapter04/.DS_Store -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/ToDo_chapter05/.DS_Store -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/._ToDo.xcodeproj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/._ToDo.xcodeproj -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/._Location.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/._Location.swift -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/._ToDoItem.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/._ToDoItem.swift -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/._AppDelegate.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/._AppDelegate.swift -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/._ItemManager.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/._ItemManager.swift -------------------------------------------------------------------------------- /Chapter06/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/._ViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/._ViewController.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/._ToDo.xcodeproj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/._ToDo.xcodeproj -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/._ToDo.xcodeproj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/._ToDo.xcodeproj -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/._Token.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/._Token.swift -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo.xcodeproj/xcuserdata/dom.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDoTests/._LocationTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDoTests/._LocationTests.swift -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDoTests/._ToDoItemTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDoTests/._ToDoItemTests.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ItemCell.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ItemCell.swift -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/._APIClient.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/._APIClient.swift -------------------------------------------------------------------------------- /Chapter01/__MACOSX/FirstDemo/FirstDemo/._AppDelegate.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/__MACOSX/FirstDemo/FirstDemo/._AppDelegate.swift -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/Base.lproj/._Main.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/Base.lproj/._Main.storyboard -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDoTests/._ItemManagerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDoTests/._ItemManagerTests.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._AppDelegate.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._AppDelegate.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/._AppDelegate.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/._AppDelegate.swift -------------------------------------------------------------------------------- /Chapter01/__MACOSX/FirstDemo/FirstDemo/._ViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/__MACOSX/FirstDemo/FirstDemo/._ViewController.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ViewController.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/Model/._Location.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/Model/._Location.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/Model/._ToDoItem.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/Model/._ToDoItem.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/._ViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/._ViewController.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Model/._Location.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Model/._Location.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Model/._ToDoItem.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Model/._ToDoItem.swift -------------------------------------------------------------------------------- /Chapter01/__MACOSX/FirstDemo/FirstDemoTests/._FirstDemoTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/__MACOSX/FirstDemo/FirstDemoTests/._FirstDemoTests.swift -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/Base.lproj/._LaunchScreen.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/Base.lproj/._LaunchScreen.storyboard -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._InputViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._InputViewController.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/Model/._ItemManager.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/Model/._ItemManager.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._ItemCellTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._ItemCellTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._ItemCell.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._ItemCell.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Model/._ItemManager.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Model/._ItemManager.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/._APIClientTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/._APIClientTests.swift -------------------------------------------------------------------------------- /Chapter01/__MACOSX/FirstDemo/FirstDemo/Base.lproj/._Main.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/__MACOSX/FirstDemo/FirstDemo/Base.lproj/._Main.storyboard -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._DetailViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._DetailViewController.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ItemListDataProvider.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ItemListDataProvider.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/Base.lproj/._Main.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/Base.lproj/._Main.storyboard -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Base.lproj/._Main.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Base.lproj/._Main.storyboard -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ItemListViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/._ItemListViewController.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/Model/._LocationTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/Model/._LocationTests.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/Model/._ToDoItemTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/Model/._ToDoItemTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Model/._LocationTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Model/._LocationTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Model/._ToDoItemTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Model/._ToDoItemTests.swift -------------------------------------------------------------------------------- /Chapter06/ToDo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter01/__MACOSX/FirstDemo/FirstDemo/Base.lproj/._LaunchScreen.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/__MACOSX/FirstDemo/FirstDemo/Base.lproj/._LaunchScreen.storyboard -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/Base.lproj/._LaunchScreen.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/Base.lproj/._LaunchScreen.storyboard -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._InputViewControllerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._InputViewControllerTests.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/Model/._ItemManagerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/Model/._ItemManagerTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Base.lproj/._LaunchScreen.storyboard: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Base.lproj/._LaunchScreen.storyboard -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._ItemCellTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._ItemCellTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Model/._ItemManagerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Model/._ItemManagerTests.swift -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._DetailViewControllerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._DetailViewControllerTests.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._ItemListDataProviderTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._ItemListDataProviderTests.swift -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._ItemListViewControllerTest.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDoTests/._ItemListViewControllerTest.swift -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._DetailViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._DetailViewController.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._InputViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._InputViewController.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._ItemListDataProvider.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._ItemListDataProvider.swift -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter03/__MACOSX/ToDo/ToDo/Assets.xcassets/AppIcon.appiconset/._Contents.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/__MACOSX/ToDo/ToDo/Assets.xcassets/AppIcon.appiconset/._Contents.json -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._ItemListViewController.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Controller/._ItemListViewController.swift -------------------------------------------------------------------------------- /Chapter06/ToDo/Token.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Token.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 09.09.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Token { 12 | let id: String 13 | } 14 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Token.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Token.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 09.09.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Token { 12 | let id: String 13 | } 14 | -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._DetailViewControllerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._DetailViewControllerTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._InputViewControllerTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._InputViewControllerTests.swift -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._ItemListDataProviderTests.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._ItemListDataProviderTests.swift -------------------------------------------------------------------------------- /Chapter01/__MACOSX/FirstDemo/FirstDemo/Assets.xcassets/AppIcon.appiconset/._Contents.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/__MACOSX/FirstDemo/FirstDemo/Assets.xcassets/AppIcon.appiconset/._Contents.json -------------------------------------------------------------------------------- /Chapter04/__MACOSX/ToDo_chapter04/ToDo/Assets.xcassets/AppIcon.appiconset/._Contents.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/__MACOSX/ToDo_chapter04/ToDo/Assets.xcassets/AppIcon.appiconset/._Contents.json -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDo/Assets.xcassets/AppIcon.appiconset/._Contents.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDo/Assets.xcassets/AppIcon.appiconset/._Contents.json -------------------------------------------------------------------------------- /Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._ItemListViewControllerTest.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/__MACOSX/ToDo_chapter05/ToDoTests/Controller/._ItemListViewControllerTest.swift -------------------------------------------------------------------------------- /Chapter06/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter06/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter03/ToDo/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter01/FirstDemo/FirstDemo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter04/ToDo_chapter04/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Test-Driven-iOS-Development-with-Swift-4-Third-Edition/HEAD/Chapter05/ToDo_chapter05/ToDo.xcodeproj/project.xcworkspace/xcuserdata/dom.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Chapter06/ToDo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/ItemListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemListViewController: UIViewController { 12 | 13 | @IBOutlet var tableView: UITableView! 14 | @IBOutlet var dataProvider: (UITableViewDataSource & UITableViewDelegate)! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | tableView.dataSource = dataProvider 20 | tableView.delegate = dataProvider 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Controller/ItemListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemListViewController: UIViewController { 12 | 13 | @IBOutlet var tableView: UITableView! 14 | @IBOutlet var dataProvider: (UITableViewDataSource & UITableViewDelegate)! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | tableView.dataSource = dataProvider 20 | tableView.delegate = dataProvider 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter06/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ToDo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F2C942C51F3F21A700EF5FE8 16 | 17 | primary 18 | 19 | 20 | F2C942D91F3F21A700EF5FE8 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ToDo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F2C942C51F3F21A700EF5FE8 16 | 17 | primary 18 | 19 | 20 | F2C942D91F3F21A700EF5FE8 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ToDo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F2C942C51F3F21A700EF5FE8 16 | 17 | primary 18 | 19 | 20 | F2C942D91F3F21A700EF5FE8 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ToDo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F2C942C51F3F21A700EF5FE8 16 | 17 | primary 18 | 19 | 20 | F2C942D91F3F21A700EF5FE8 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | FirstDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F28989781F3CFBB600A4C351 16 | 17 | primary 18 | 19 | 20 | F289898C1F3CFBB700A4C351 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter06/ToDoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter06/ToDoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/ItemManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManager.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ItemManager { 12 | var toDoCount: Int { return toDoItems.count } 13 | var doneCount: Int { return doneItems.count } 14 | private var toDoItems: [ToDoItem] = [] 15 | private var doneItems: [ToDoItem] = [] 16 | 17 | func add(_ item: ToDoItem) { 18 | if !toDoItems.contains(item) { 19 | toDoItems.append(item) 20 | } 21 | } 22 | 23 | func item(at index: Int) -> ToDoItem { 24 | return toDoItems[index] 25 | } 26 | 27 | func checkItem(at index: Int) { 28 | let item = toDoItems.remove(at: index) 29 | doneItems.append(item) 30 | } 31 | 32 | func doneItem(at index: Int) -> ToDoItem { 33 | return doneItems[index] 34 | } 35 | 36 | func removeAll() { 37 | toDoItems.removeAll() 38 | doneItems.removeAll() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/Location.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreLocation 11 | 12 | struct Location: Equatable { 13 | let name: String 14 | let coordinate: CLLocationCoordinate2D? 15 | 16 | init(name: String, 17 | coordinate: CLLocationCoordinate2D? = nil) { 18 | 19 | self.name = name 20 | self.coordinate = coordinate 21 | } 22 | 23 | public static func ==(lhs: Location, 24 | rhs: Location) -> Bool { 25 | 26 | if lhs.coordinate?.latitude != 27 | rhs.coordinate?.latitude { 28 | 29 | return false 30 | } 31 | 32 | if lhs.coordinate?.longitude != 33 | rhs.coordinate?.longitude { 34 | 35 | 36 | return false 37 | } 38 | 39 | if lhs.name != rhs.name { 40 | return false 41 | } 42 | 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/Model/Location.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreLocation 11 | 12 | struct Location: Equatable { 13 | let name: String 14 | let coordinate: CLLocationCoordinate2D? 15 | 16 | init(name: String, 17 | coordinate: CLLocationCoordinate2D? = nil) { 18 | 19 | self.name = name 20 | self.coordinate = coordinate 21 | } 22 | 23 | public static func ==(lhs: Location, 24 | rhs: Location) -> Bool { 25 | 26 | if lhs.coordinate?.latitude != 27 | rhs.coordinate?.latitude { 28 | 29 | return false 30 | } 31 | 32 | if lhs.coordinate?.longitude != 33 | rhs.coordinate?.longitude { 34 | 35 | 36 | return false 37 | } 38 | 39 | if lhs.name != rhs.name { 40 | return false 41 | } 42 | 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Model/Location.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreLocation 11 | 12 | struct Location: Equatable { 13 | let name: String 14 | let coordinate: CLLocationCoordinate2D? 15 | 16 | init(name: String, 17 | coordinate: CLLocationCoordinate2D? = nil) { 18 | 19 | self.name = name 20 | self.coordinate = coordinate 21 | } 22 | 23 | public static func ==(lhs: Location, 24 | rhs: Location) -> Bool { 25 | 26 | if lhs.coordinate?.latitude != 27 | rhs.coordinate?.latitude { 28 | 29 | return false 30 | } 31 | 32 | if lhs.coordinate?.longitude != 33 | rhs.coordinate?.longitude { 34 | 35 | 36 | return false 37 | } 38 | 39 | if lhs.name != rhs.name { 40 | return false 41 | } 42 | 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter06/ToDoTests/StoryboardTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 03.10.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class StoryboardTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func test_InitialViewController_IsItemListViewController() { 25 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 26 | 27 | let navigationController = 28 | storyboard.instantiateInitialViewController() 29 | as! UINavigationController 30 | let rootViewController = navigationController.viewControllers[0] 31 | 32 | XCTAssertTrue(rootViewController is ItemListViewController) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/Model/ItemManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManager.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ItemManager { 12 | var toDoCount: Int { return toDoItems.count } 13 | var doneCount: Int { return doneItems.count } 14 | private var toDoItems: [ToDoItem] = [] 15 | private var doneItems: [ToDoItem] = [] 16 | 17 | func add(_ item: ToDoItem) { 18 | if !toDoItems.contains(item) { 19 | toDoItems.append(item) 20 | } 21 | } 22 | 23 | func item(at index: Int) -> ToDoItem { 24 | return toDoItems[index] 25 | } 26 | 27 | func checkItem(at index: Int) { 28 | let item = toDoItems.remove(at: index) 29 | doneItems.append(item) 30 | } 31 | 32 | func doneItem(at index: Int) -> ToDoItem { 33 | return doneItems[index] 34 | } 35 | 36 | func removeAll() { 37 | toDoItems.removeAll() 38 | doneItems.removeAll() 39 | } 40 | 41 | func uncheckItem(at index: Int) { 42 | let item = doneItems.remove(at: index) 43 | toDoItems.append(item) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Model/ItemManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManager.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ItemManager { 12 | var toDoCount: Int { return toDoItems.count } 13 | var doneCount: Int { return doneItems.count } 14 | private var toDoItems: [ToDoItem] = [] 15 | private var doneItems: [ToDoItem] = [] 16 | 17 | func add(_ item: ToDoItem) { 18 | if !toDoItems.contains(item) { 19 | toDoItems.append(item) 20 | } 21 | } 22 | 23 | func item(at index: Int) -> ToDoItem { 24 | return toDoItems[index] 25 | } 26 | 27 | func checkItem(at index: Int) { 28 | let item = toDoItems.remove(at: index) 29 | doneItems.append(item) 30 | } 31 | 32 | func doneItem(at index: Int) -> ToDoItem { 33 | return doneItems[index] 34 | } 35 | 36 | func removeAll() { 37 | toDoItems.removeAll() 38 | doneItems.removeAll() 39 | } 40 | 41 | func uncheckItem(at index: Int) { 42 | let item = doneItems.remove(at: index) 43 | toDoItems.append(item) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/ToDoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItem.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ToDoItem: Equatable { 12 | let title: String 13 | let itemDescription: String? 14 | let timestamp: Double? 15 | let location: Location? 16 | 17 | init(title: String, 18 | itemDescription: String? = nil, 19 | timestamp: Double? = nil, 20 | location: Location? = nil) { 21 | 22 | self.title = title 23 | self.itemDescription = itemDescription 24 | self.timestamp = timestamp 25 | self.location = location 26 | } 27 | 28 | public static func ==(lhs: ToDoItem, 29 | rhs: ToDoItem) -> Bool { 30 | 31 | 32 | if lhs.location != rhs.location { 33 | return false 34 | } 35 | 36 | if lhs.timestamp != rhs.timestamp { 37 | return false 38 | } 39 | 40 | if lhs.itemDescription != rhs.itemDescription { 41 | return false 42 | } 43 | 44 | if lhs.title != rhs.title { 45 | return false 46 | } 47 | 48 | return true 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/Model/ToDoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItem.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ToDoItem: Equatable { 12 | let title: String 13 | let itemDescription: String? 14 | let timestamp: Double? 15 | let location: Location? 16 | 17 | init(title: String, 18 | itemDescription: String? = nil, 19 | timestamp: Double? = nil, 20 | location: Location? = nil) { 21 | 22 | self.title = title 23 | self.itemDescription = itemDescription 24 | self.timestamp = timestamp 25 | self.location = location 26 | } 27 | 28 | public static func ==(lhs: ToDoItem, 29 | rhs: ToDoItem) -> Bool { 30 | 31 | 32 | if lhs.location != rhs.location { 33 | return false 34 | } 35 | 36 | if lhs.timestamp != rhs.timestamp { 37 | return false 38 | } 39 | 40 | if lhs.itemDescription != rhs.itemDescription { 41 | return false 42 | } 43 | 44 | if lhs.title != rhs.title { 45 | return false 46 | } 47 | 48 | return true 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Model/ToDoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItem.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ToDoItem: Equatable { 12 | let title: String 13 | let itemDescription: String? 14 | let timestamp: Double? 15 | let location: Location? 16 | 17 | init(title: String, 18 | itemDescription: String? = nil, 19 | timestamp: Double? = nil, 20 | location: Location? = nil) { 21 | 22 | self.title = title 23 | self.itemDescription = itemDescription 24 | self.timestamp = timestamp 25 | self.location = location 26 | } 27 | 28 | public static func ==(lhs: ToDoItem, 29 | rhs: ToDoItem) -> Bool { 30 | 31 | 32 | if lhs.location != rhs.location { 33 | return false 34 | } 35 | 36 | if lhs.timestamp != rhs.timestamp { 37 | return false 38 | } 39 | 40 | if lhs.itemDescription != rhs.itemDescription { 41 | return false 42 | } 43 | 44 | if lhs.title != rhs.title { 45 | return false 46 | } 47 | 48 | return true 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // FirstDemo 4 | // 5 | // Created by dasdom on 10.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | func numberOfVowels(in string: String) -> Int { 24 | let vowels: [Character] = ["a", "e", "i", "o", "u", 25 | "A", "E", "I", "O", "U"] 26 | 27 | return string.characters.reduce(0) { 28 | $0 + (vowels.contains($1) ? 1 : 0) 29 | } 30 | } 31 | 32 | func makeHeadline(from string: String) -> String { 33 | let words = string.components(separatedBy: " ") 34 | 35 | 36 | let headlineWords = words.map { (word) -> String in 37 | var mutableWord = word 38 | let first = mutableWord.remove(at: mutableWord.startIndex) 39 | 40 | return String(first).uppercased() + mutableWord 41 | } 42 | 43 | 44 | return headlineWords.joined(separator: " ") 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/ItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCell.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemCell: UITableViewCell { 12 | 13 | @IBOutlet var titleLabel: UILabel! 14 | @IBOutlet var locationLabel: UILabel! 15 | @IBOutlet var dateLabel: UILabel! 16 | 17 | lazy var dateFormatter: DateFormatter = { 18 | let dateFormatter = DateFormatter() 19 | dateFormatter.dateFormat = "MM/dd/yyyy" 20 | return dateFormatter 21 | }() 22 | 23 | func configCell(with item: ToDoItem, 24 | checked: Bool = false) { 25 | 26 | if checked { 27 | let attributedString = NSAttributedString( 28 | string: item.title, 29 | attributes: [NSAttributedStringKey.strikethroughStyle: 30 | NSUnderlineStyle.styleSingle.rawValue]) 31 | 32 | 33 | titleLabel.attributedText = attributedString 34 | locationLabel.text = nil 35 | dateLabel.text = nil 36 | } else { 37 | 38 | titleLabel.text = item.title 39 | locationLabel.text = item.location?.name ?? "" 40 | 41 | if let timestamp = item.timestamp { 42 | let date = Date(timeIntervalSince1970: timestamp) 43 | 44 | 45 | dateLabel.text = dateFormatter.string(from: date) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Controller/ItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCell.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemCell: UITableViewCell { 12 | 13 | @IBOutlet var titleLabel: UILabel! 14 | @IBOutlet var locationLabel: UILabel! 15 | @IBOutlet var dateLabel: UILabel! 16 | 17 | lazy var dateFormatter: DateFormatter = { 18 | let dateFormatter = DateFormatter() 19 | dateFormatter.dateFormat = "MM/dd/yyyy" 20 | return dateFormatter 21 | }() 22 | 23 | func configCell(with item: ToDoItem, 24 | checked: Bool = false) { 25 | 26 | if checked { 27 | let attributedString = NSAttributedString( 28 | string: item.title, 29 | attributes: [NSAttributedStringKey.strikethroughStyle: 30 | NSUnderlineStyle.styleSingle.rawValue]) 31 | 32 | 33 | titleLabel.attributedText = attributedString 34 | locationLabel.text = nil 35 | dateLabel.text = nil 36 | } else { 37 | 38 | titleLabel.text = item.title 39 | locationLabel.text = item.location?.name ?? "" 40 | 41 | if let timestamp = item.timestamp { 42 | let date = Date(timeIntervalSince1970: timestamp) 43 | 44 | 45 | dateLabel.text = dateFormatter.string(from: date) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Controller/ItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCell.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemCell: UITableViewCell { 12 | 13 | @IBOutlet var titleLabel: UILabel! 14 | @IBOutlet var locationLabel: UILabel! 15 | @IBOutlet var dateLabel: UILabel! 16 | 17 | lazy var dateFormatter: DateFormatter = { 18 | let dateFormatter = DateFormatter() 19 | dateFormatter.dateFormat = "MM/dd/yyyy" 20 | return dateFormatter 21 | }() 22 | 23 | func configCell(with item: ToDoItem, 24 | checked: Bool = false) { 25 | 26 | if checked { 27 | let attributedString = NSAttributedString( 28 | string: item.title, 29 | attributes: [NSAttributedStringKey.strikethroughStyle: 30 | NSUnderlineStyle.styleSingle.rawValue]) 31 | 32 | 33 | titleLabel.attributedText = attributedString 34 | locationLabel.text = nil 35 | dateLabel.text = nil 36 | } else { 37 | 38 | titleLabel.text = item.title 39 | locationLabel.text = item.location?.name ?? "" 40 | 41 | if let timestamp = item.timestamp { 42 | let date = Date(timeIntervalSince1970: timestamp) 43 | 44 | 45 | dateLabel.text = dateFormatter.string(from: date) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemoTests/FirstDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstDemoTests.swift 3 | // FirstDemoTests 4 | // 5 | // Created by dasdom on 10.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import FirstDemo 11 | 12 | class FirstDemoTests: XCTestCase { 13 | 14 | var viewController: ViewController! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | viewController = ViewController() 19 | } 20 | 21 | override func tearDown() { 22 | viewController = nil 23 | 24 | super.tearDown() 25 | } 26 | 27 | func test_NumberOfVowels_WhenPassedDominik_ReturnsThree() { 28 | 29 | let string = "Dominik" 30 | 31 | let numberOfVowels = viewController.numberOfVowels(in: string) 32 | 33 | XCTAssertEqual(numberOfVowels, 3, 34 | "should find 3 vowels in Dominik") 35 | } 36 | 37 | func test_MakeHeadline_ReturnsStringWithEachWordStartCapital() { 38 | let input = "this is A test headline" 39 | let expectedOutput = "This Is A Test Headline" 40 | 41 | 42 | let headline = viewController.makeHeadline(from: input) 43 | 44 | 45 | XCTAssertEqual(headline, expectedOutput) 46 | } 47 | 48 | func test_MakeHeadline_ReturnsStringWithEachWordStartCapital2() { 49 | let input = "Here is another Example" 50 | let expectedOutput = "Here iS Another Example" 51 | 52 | 53 | let headline = viewController.makeHeadline(from: input) 54 | 55 | 56 | XCTAssertEqual(headline, expectedOutput) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/ItemListViewControllerTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewControllerTest.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ItemListViewControllerTest: XCTestCase { 13 | 14 | var sut: ItemListViewController! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | let storyboard = UIStoryboard(name: "Main", 20 | bundle: nil) 21 | let viewController = 22 | storyboard.instantiateViewController( 23 | withIdentifier: "ItemListViewController") 24 | sut = viewController 25 | as! ItemListViewController 26 | 27 | 28 | sut.loadViewIfNeeded() 29 | } 30 | 31 | override func tearDown() { 32 | // Put teardown code here. This method is called after the invocation of each test method in the class. 33 | super.tearDown() 34 | } 35 | 36 | func test_TableView_AfterViewDidLoad_IsNotNil() { 37 | XCTAssertNotNil(sut.tableView) 38 | } 39 | 40 | func test_LoadingView_SetsTableViewDataSource() { 41 | XCTAssertTrue(sut.tableView.dataSource is ItemListDataProvider) 42 | } 43 | 44 | func test_LoadingView_SetsTableViewDelegate() { 45 | XCTAssertTrue(sut.tableView.delegate is ItemListDataProvider) 46 | } 47 | 48 | func test_LoadingView_DataSourceEqualDelegate() { 49 | XCTAssertEqual( 50 | sut.tableView.dataSource as? ItemListDataProvider, 51 | sut.tableView.delegate as? ItemListDataProvider) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Controller/ItemListViewControllerTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewControllerTest.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ItemListViewControllerTest: XCTestCase { 13 | 14 | var sut: ItemListViewController! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | let storyboard = UIStoryboard(name: "Main", 20 | bundle: nil) 21 | let viewController = 22 | storyboard.instantiateViewController( 23 | withIdentifier: "ItemListViewController") 24 | sut = viewController 25 | as! ItemListViewController 26 | 27 | 28 | sut.loadViewIfNeeded() 29 | } 30 | 31 | override func tearDown() { 32 | // Put teardown code here. This method is called after the invocation of each test method in the class. 33 | super.tearDown() 34 | } 35 | 36 | func test_TableView_AfterViewDidLoad_IsNotNil() { 37 | XCTAssertNotNil(sut.tableView) 38 | } 39 | 40 | func test_LoadingView_SetsTableViewDataSource() { 41 | XCTAssertTrue(sut.tableView.dataSource is ItemListDataProvider) 42 | } 43 | 44 | func test_LoadingView_SetsTableViewDelegate() { 45 | XCTAssertTrue(sut.tableView.delegate is ItemListDataProvider) 46 | } 47 | 48 | func test_LoadingView_DataSourceEqualDelegate() { 49 | XCTAssertEqual( 50 | sut.tableView.dataSource as? ItemListDataProvider, 51 | sut.tableView.delegate as? ItemListDataProvider) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Controller/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | class DetailViewController: UIViewController { 13 | 14 | @IBOutlet var titleLabel: UILabel! 15 | @IBOutlet var dateLabel: UILabel! 16 | @IBOutlet var locationLabel: UILabel! 17 | @IBOutlet var descriptionLabel: UILabel! 18 | @IBOutlet var mapView: MKMapView! 19 | 20 | var itemInfo: (ItemManager, Int)? 21 | 22 | let dateFormatter: DateFormatter = { 23 | let dateFormatter = DateFormatter() 24 | dateFormatter.dateFormat = "MM/dd/yyyy" 25 | return dateFormatter 26 | }() 27 | 28 | override func viewWillAppear(_ animated: Bool) { 29 | super.viewWillAppear(animated) 30 | 31 | guard let itemInfo = itemInfo else { return } 32 | let item = itemInfo.0.item(at: itemInfo.1) 33 | 34 | titleLabel.text = item.title 35 | locationLabel.text = item.location?.name 36 | descriptionLabel.text = item.itemDescription 37 | 38 | if let timestamp = item.timestamp { 39 | let date = Date(timeIntervalSince1970: timestamp) 40 | dateLabel.text = dateFormatter.string(from: date) 41 | } 42 | 43 | if let coordinate = item.location?.coordinate { 44 | let region = MKCoordinateRegionMakeWithDistance(coordinate, 45 | 100, 100) 46 | mapView.region = region 47 | } 48 | } 49 | 50 | func checkItem() { 51 | if let itemInfo = itemInfo { 52 | itemInfo.0.checkItem(at: itemInfo.1) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | class DetailViewController: UIViewController { 13 | 14 | @IBOutlet var titleLabel: UILabel! 15 | @IBOutlet var dateLabel: UILabel! 16 | @IBOutlet var locationLabel: UILabel! 17 | @IBOutlet var descriptionLabel: UILabel! 18 | @IBOutlet var mapView: MKMapView! 19 | 20 | var itemInfo: (ItemManager, Int)? 21 | 22 | let dateFormatter: DateFormatter = { 23 | let dateFormatter = DateFormatter() 24 | dateFormatter.dateFormat = "MM/dd/yyyy" 25 | return dateFormatter 26 | }() 27 | 28 | override func viewWillAppear(_ animated: Bool) { 29 | super.viewWillAppear(animated) 30 | 31 | guard let itemInfo = itemInfo else { return } 32 | let item = itemInfo.0.item(at: itemInfo.1) 33 | 34 | titleLabel.text = item.title 35 | locationLabel.text = item.location?.name 36 | descriptionLabel.text = item.itemDescription 37 | 38 | if let timestamp = item.timestamp { 39 | let date = Date(timeIntervalSince1970: timestamp) 40 | dateLabel.text = dateFormatter.string(from: date) 41 | } 42 | 43 | if let coordinate = item.location?.coordinate { 44 | let region = MKCoordinateRegionMakeWithDistance(coordinate, 45 | 100, 100) 46 | mapView.region = region 47 | } 48 | } 49 | 50 | func checkItem() { 51 | if let itemInfo = itemInfo { 52 | itemInfo.0.checkItem(at: itemInfo.1) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Controller/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | class DetailViewController: UIViewController { 13 | 14 | @IBOutlet var titleLabel: UILabel! 15 | @IBOutlet var dateLabel: UILabel! 16 | @IBOutlet var locationLabel: UILabel! 17 | @IBOutlet var descriptionLabel: UILabel! 18 | @IBOutlet var mapView: MKMapView! 19 | 20 | var itemInfo: (ItemManager, Int)? 21 | 22 | let dateFormatter: DateFormatter = { 23 | let dateFormatter = DateFormatter() 24 | dateFormatter.dateFormat = "MM/dd/yyyy" 25 | return dateFormatter 26 | }() 27 | 28 | override func viewWillAppear(_ animated: Bool) { 29 | super.viewWillAppear(animated) 30 | 31 | guard let itemInfo = itemInfo else { return } 32 | let item = itemInfo.0.item(at: itemInfo.1) 33 | 34 | titleLabel.text = item.title 35 | locationLabel.text = item.location?.name 36 | descriptionLabel.text = item.itemDescription 37 | 38 | if let timestamp = item.timestamp { 39 | let date = Date(timeIntervalSince1970: timestamp) 40 | dateLabel.text = dateFormatter.string(from: date) 41 | } 42 | 43 | if let coordinate = item.location?.coordinate { 44 | let region = MKCoordinateRegionMakeWithDistance(coordinate, 45 | 100, 100) 46 | mapView.region = region 47 | } 48 | } 49 | 50 | func checkItem() { 51 | if let itemInfo = itemInfo { 52 | itemInfo.0.checkItem(at: itemInfo.1) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo/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 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Chapter06/ToDo/Model/Location.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreLocation 11 | 12 | struct Location: Equatable { 13 | let name: String 14 | let coordinate: CLLocationCoordinate2D? 15 | 16 | private let nameKey = "nameKey" 17 | private let latitudeKey = "latitudeKey" 18 | private let longitudeKey = "longitudeKey" 19 | 20 | var plistDict: [String:Any] { 21 | var dict = [String:Any]() 22 | 23 | dict[nameKey] = name 24 | 25 | if let coordinate = coordinate { 26 | dict[latitudeKey] = coordinate.latitude 27 | dict[longitudeKey] = coordinate.longitude 28 | } 29 | return dict 30 | } 31 | 32 | init(name: String, 33 | coordinate: CLLocationCoordinate2D? = nil) { 34 | 35 | self.name = name 36 | self.coordinate = coordinate 37 | } 38 | 39 | init?(dict: [String:Any]) { 40 | guard let name = dict[nameKey] as? String else 41 | { return nil } 42 | 43 | let coordinate: CLLocationCoordinate2D? 44 | if let latitude = dict[latitudeKey] as? Double, 45 | let longitude = dict[longitudeKey] as? Double { 46 | coordinate = CLLocationCoordinate2DMake(latitude, longitude) 47 | } else { 48 | coordinate = nil 49 | } 50 | 51 | self.name = name 52 | self.coordinate = coordinate 53 | } 54 | 55 | public static func ==(lhs: Location, 56 | rhs: Location) -> Bool { 57 | 58 | if lhs.coordinate?.latitude != 59 | rhs.coordinate?.latitude { 60 | 61 | return false 62 | } 63 | 64 | if lhs.coordinate?.longitude != 65 | rhs.coordinate?.longitude { 66 | 67 | 68 | return false 69 | } 70 | 71 | if lhs.name != rhs.name { 72 | return false 73 | } 74 | 75 | return true 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/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 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Controller/ItemListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ItemListViewController: UIViewController { 12 | 13 | @IBOutlet var tableView: UITableView! 14 | @IBOutlet var dataProvider: (UITableViewDataSource & UITableViewDelegate & ItemManagerSettable)! 15 | let itemManager = ItemManager() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | tableView.dataSource = dataProvider 21 | tableView.delegate = dataProvider 22 | 23 | dataProvider.itemManager = itemManager 24 | 25 | NotificationCenter.default.addObserver( 26 | self, 27 | selector: #selector(showDetails(sender:)), 28 | name: NSNotification.Name("ItemSelectedNotification"), 29 | object: nil) 30 | } 31 | 32 | override func viewWillAppear(_ animated: Bool) { 33 | super.viewWillAppear(animated) 34 | 35 | tableView.reloadData() 36 | } 37 | 38 | @IBAction func addItem(_ sender: UIBarButtonItem) { 39 | 40 | if let nextViewController = 41 | storyboard?.instantiateViewController( 42 | withIdentifier: "InputViewController") 43 | as? InputViewController { 44 | 45 | nextViewController.itemManager = itemManager 46 | 47 | present(nextViewController, animated: true, completion: nil) 48 | } 49 | } 50 | 51 | @objc func showDetails(sender: NSNotification) { 52 | guard let index = sender.userInfo?["index"] as? Int else 53 | { fatalError() } 54 | 55 | 56 | if let nextViewController = storyboard?.instantiateViewController( 57 | withIdentifier: "DetailViewController") as? DetailViewController { 58 | 59 | 60 | nextViewController.itemInfo = (itemManager, index) 61 | navigationController?.pushViewController(nextViewController, 62 | animated: true) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/InputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreLocation 11 | 12 | class InputViewController: UIViewController { 13 | 14 | @IBOutlet var titleTextField: UITextField! 15 | @IBOutlet var dateTextField: UITextField! 16 | @IBOutlet var locationTextField: UITextField! 17 | @IBOutlet var addressTextField: UITextField! 18 | @IBOutlet var descriptionTextField: UITextField! 19 | @IBOutlet var saveButton: UIButton! 20 | 21 | lazy var geocoder = CLGeocoder() 22 | var itemManager: ItemManager? 23 | 24 | let dateFormatter: DateFormatter = { 25 | let dateFormatter = DateFormatter() 26 | dateFormatter.dateFormat = "MM/dd/yyyy" 27 | return dateFormatter 28 | }() 29 | 30 | @IBAction func save() { 31 | guard let titleString = titleTextField.text, 32 | titleString.characters.count > 0 else { return } 33 | let date: Date? 34 | if let dateText = self.dateTextField.text, 35 | dateText.characters.count > 0 { 36 | date = dateFormatter.date(from: dateText) 37 | } else { 38 | date = nil 39 | } 40 | let descriptionString = descriptionTextField.text 41 | if let locationName = locationTextField.text, 42 | locationName.characters.count > 0 { 43 | if let address = addressTextField.text, 44 | address.characters.count > 0 { 45 | 46 | 47 | geocoder.geocodeAddressString(address) { 48 | [unowned self] (placeMarks, error) -> Void in 49 | 50 | 51 | let placeMark = placeMarks?.first 52 | 53 | 54 | let item = ToDoItem( 55 | title: titleString, 56 | itemDescription: descriptionString, 57 | timestamp: date?.timeIntervalSince1970, 58 | location: Location( 59 | name: locationName, 60 | coordinate: placeMark?.location?.coordinate)) 61 | 62 | 63 | self.itemManager?.add(item) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Controller/InputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreLocation 11 | 12 | class InputViewController: UIViewController { 13 | 14 | @IBOutlet var titleTextField: UITextField! 15 | @IBOutlet var dateTextField: UITextField! 16 | @IBOutlet var locationTextField: UITextField! 17 | @IBOutlet var addressTextField: UITextField! 18 | @IBOutlet var descriptionTextField: UITextField! 19 | @IBOutlet var saveButton: UIButton! 20 | 21 | lazy var geocoder = CLGeocoder() 22 | var itemManager: ItemManager? 23 | 24 | let dateFormatter: DateFormatter = { 25 | let dateFormatter = DateFormatter() 26 | dateFormatter.dateFormat = "MM/dd/yyyy" 27 | return dateFormatter 28 | }() 29 | 30 | @IBAction func save() { 31 | guard let titleString = titleTextField.text, 32 | titleString.characters.count > 0 else { return } 33 | let date: Date? 34 | if let dateText = self.dateTextField.text, 35 | dateText.characters.count > 0 { 36 | date = dateFormatter.date(from: dateText) 37 | } else { 38 | date = nil 39 | } 40 | let descriptionString = descriptionTextField.text 41 | if let locationName = locationTextField.text, 42 | locationName.characters.count > 0 { 43 | if let address = addressTextField.text, 44 | address.characters.count > 0 { 45 | 46 | 47 | geocoder.geocodeAddressString(address) { 48 | [unowned self] (placeMarks, error) -> Void in 49 | 50 | 51 | let placeMark = placeMarks?.first 52 | 53 | 54 | let item = ToDoItem( 55 | title: titleString, 56 | itemDescription: descriptionString, 57 | timestamp: date?.timeIntervalSince1970, 58 | location: Location( 59 | name: locationName, 60 | coordinate: placeMark?.location?.coordinate)) 61 | 62 | 63 | self.itemManager?.add(item) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Chapter06/ToDo/APIClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIClient.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 09.09.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class APIClient { 12 | 13 | lazy var session: SessionProtocol = URLSession.shared 14 | 15 | func loginUser(withName username: String, 16 | password: String, 17 | completion: @escaping (Token?, Error?) -> Void) { 18 | 19 | let query = "username=\(username.percentEncoded)&password=\(password.percentEncoded)" 20 | guard let url = URL(string: 21 | "https://awesometodos.com/login?\(query)") else { 22 | fatalError() 23 | } 24 | 25 | 26 | session.dataTask(with: url) { (data, response, error) in 27 | 28 | guard error == nil else { return completion(nil, error) } 29 | 30 | guard let data = data else { 31 | completion(nil, WebserviceError.DataEmptyError) 32 | return 33 | } 34 | 35 | do { 36 | let dict = try JSONSerialization.jsonObject( 37 | with: data, 38 | options: []) as? [String:String] 39 | 40 | 41 | let token: Token? 42 | if let tokenString = dict?["token"] { 43 | token = Token(id: tokenString) 44 | } else { 45 | token = nil 46 | } 47 | completion(token, nil) 48 | } catch { 49 | completion(nil, error) 50 | } 51 | }.resume() 52 | } 53 | } 54 | 55 | extension String { 56 | 57 | var percentEncoded: String { 58 | 59 | let allowedCharacters = CharacterSet( 60 | charactersIn: 61 | "/%&=?$#+-~@<>|\\*,.()[]{}^!").inverted 62 | 63 | guard let encoded = self.addingPercentEncoding( 64 | withAllowedCharacters: allowedCharacters) else { fatalError() } 65 | 66 | return encoded 67 | } 68 | } 69 | 70 | protocol SessionProtocol { 71 | func dataTask( 72 | with url: URL, 73 | completionHandler: @escaping 74 | (Data?, URLResponse?, Error?) -> Void) 75 | -> URLSessionDataTask 76 | } 77 | 78 | extension URLSession: SessionProtocol {} 79 | 80 | enum WebserviceError : Error { 81 | case DataEmptyError, ResponseError 82 | } 83 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/APIClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIClient.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 09.09.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class APIClient { 12 | 13 | lazy var session: SessionProtocol = URLSession.shared 14 | 15 | func loginUser(withName username: String, 16 | password: String, 17 | completion: @escaping (Token?, Error?) -> Void) { 18 | 19 | let query = "username=\(username.percentEncoded)&password=\(password.percentEncoded)" 20 | guard let url = URL(string: 21 | "https://awesometodos.com/login?\(query)") else { 22 | fatalError() 23 | } 24 | 25 | 26 | session.dataTask(with: url) { (data, response, error) in 27 | 28 | guard error == nil else { return completion(nil, error) } 29 | 30 | guard let data = data else { 31 | completion(nil, WebserviceError.DataEmptyError) 32 | return 33 | } 34 | 35 | do { 36 | let dict = try JSONSerialization.jsonObject( 37 | with: data, 38 | options: []) as? [String:String] 39 | 40 | 41 | let token: Token? 42 | if let tokenString = dict?["token"] { 43 | token = Token(id: tokenString) 44 | } else { 45 | token = nil 46 | } 47 | completion(token, nil) 48 | } catch { 49 | completion(nil, error) 50 | } 51 | }.resume() 52 | } 53 | } 54 | 55 | extension String { 56 | 57 | var percentEncoded: String { 58 | 59 | let allowedCharacters = CharacterSet( 60 | charactersIn: 61 | "/%&=?$#+-~@<>|\\*,.()[]{}^!").inverted 62 | 63 | guard let encoded = self.addingPercentEncoding( 64 | withAllowedCharacters: allowedCharacters) else { fatalError() } 65 | 66 | return encoded 67 | } 68 | } 69 | 70 | protocol SessionProtocol { 71 | func dataTask( 72 | with url: URL, 73 | completionHandler: @escaping 74 | (Data?, URLResponse?, Error?) -> Void) 75 | -> URLSessionDataTask 76 | } 77 | 78 | extension URLSession: SessionProtocol {} 79 | 80 | enum WebserviceError : Error { 81 | case DataEmptyError, ResponseError 82 | } 83 | -------------------------------------------------------------------------------- /Chapter06/ToDo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Model/ToDoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItem.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ToDoItem: Equatable { 12 | let title: String 13 | let itemDescription: String? 14 | let timestamp: Double? 15 | let location: Location? 16 | 17 | private let titleKey = "titleKey" 18 | private let itemDescriptionKey = "itemDescriptionKey" 19 | private let timestampKey = "timestampKey" 20 | private let locationKey = "locationKey" 21 | 22 | var plistDict: [String:Any] { 23 | var dict = [String:Any]() 24 | dict[titleKey] = title 25 | if let itemDescription = itemDescription { 26 | dict[itemDescriptionKey] = itemDescription 27 | } 28 | if let timestamp = timestamp { 29 | dict[timestampKey] = timestamp 30 | } 31 | if let location = location { 32 | let locationDict = location.plistDict 33 | dict[locationKey] = locationDict 34 | } 35 | return dict 36 | } 37 | 38 | init(title: String, 39 | itemDescription: String? = nil, 40 | timestamp: Double? = nil, 41 | location: Location? = nil) { 42 | 43 | self.title = title 44 | self.itemDescription = itemDescription 45 | self.timestamp = timestamp 46 | self.location = location 47 | } 48 | 49 | init?(dict: [String:Any]) { 50 | guard let title = dict[titleKey] as? String else 51 | { return nil } 52 | 53 | self.title = title 54 | 55 | self.itemDescription = dict[itemDescriptionKey] as? String 56 | self.timestamp = dict[timestampKey] as? Double 57 | if let locationDict = dict[locationKey] as? [String:Any] { 58 | self.location = Location(dict: locationDict) 59 | } else { 60 | self.location = nil 61 | } 62 | } 63 | 64 | public static func ==(lhs: ToDoItem, 65 | rhs: ToDoItem) -> Bool { 66 | 67 | 68 | if lhs.location != rhs.location { 69 | return false 70 | } 71 | 72 | if lhs.timestamp != rhs.timestamp { 73 | return false 74 | } 75 | 76 | if lhs.itemDescription != rhs.itemDescription { 77 | return false 78 | } 79 | 80 | if lhs.title != rhs.title { 81 | return false 82 | } 83 | 84 | return true 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /Chapter01/FirstDemo/FirstDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FirstDemo 4 | // 5 | // Created by dasdom on 10.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Chapter06/ToDoUITests/ToDoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoUITests.swift 3 | // ToDoUITests 4 | // 5 | // Created by dasdom on 07.10.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ToDoUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | 33 | let app = XCUIApplication() 34 | app.navigationBars["ToDo.ItemListView"].buttons["Add"].tap() 35 | 36 | let titleTextField = app.textFields["Title"] 37 | titleTextField.tap() 38 | titleTextField.typeText("Meeting") 39 | 40 | let dateTextField = app.textFields["Date"] 41 | dateTextField.tap() 42 | dateTextField.tap() 43 | dateTextField.typeText("02/22/2018") 44 | 45 | let locationTextField = app.textFields["Location"] 46 | locationTextField.tap() 47 | locationTextField.tap() 48 | locationTextField.typeText("Office") 49 | 50 | let addressTextField = app.textFields["Address"] 51 | addressTextField.tap() 52 | addressTextField.tap() 53 | addressTextField.typeText("Infinite Loop 1, Cupertino") 54 | 55 | let descriptionTextField = app.textFields["Description"] 56 | descriptionTextField.tap() 57 | descriptionTextField.tap() 58 | descriptionTextField.typeText("Bring iPad") 59 | app.buttons["Save"].tap() 60 | 61 | XCTAssertTrue(app.tables.staticTexts["Meeting"].exists) 62 | XCTAssertTrue(app.tables.staticTexts["02/22/2018"].exists) 63 | XCTAssertTrue(app.tables.staticTexts["Office"].exists) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Test-Driven iOS Development with Swift 4 - Third Edition 2 | This is the code repository for [Test-Driven iOS Development with Swift 4 - Third Edition](https://www.packtpub.com/application-development/test-driven-ios-development-swift-4-third-edition?utm_source=GitHub&utm_medium=repo&utm_campaign=9781788475709), published by [Packt](https://www.packtpub.com). It contains all the supporting project files necessary to work through the book from start to finish. 3 | 4 | ## About the Book 5 | 6 | Test-driven development (TDD) is a proven way to find software bugs early. Writing tests before you code improves the structure and maintainability of your apps. Using TDD, in combination with Swift 4's improved syntax, means there is no longer any excuse for writing bad code. 7 | 8 | This book will help you understand the process of TDD and how to apply it to your apps written in Swift. 9 | 10 | Through practical, real-world examples, you’ll learn how to implement TDD in context. You will begin with an overview of the TDD workflow and then delve into unit-testing concepts and code cycles. 11 | 12 | Finally, the book will guide you through the next steps to becoming a testing expert by discussing integration tests, Behavior Driven Development (BDD), open source testing frameworks, and UI Tests (introduced in Xcode 9). 13 | 14 | ## Instructions and Navigations 15 | All of the code is organized into folders. Each folder starts with a number followed by the application name. For example, Chapter01. 16 | 17 | 18 | 19 | The code will look like the following: 20 | ``` 21 | 22 | func test_NumberOfVowels_WhenPassedDominik_ReturnsThree() { 23 | let viewController = ViewController() 24 | let string = "Dominik" 25 | let numberOfVowels = viewController.numberOfVowels(in: string) 26 | XCTAssertEqual(numberOfVowels, 3, "should find 3 vowels in Dominik") 27 | } 28 | 29 | ``` 30 | 31 | ## Related Products 32 | * [Developing iOS 11 Applications Using Swift 4 [Video]](https://www.packtpub.com/application-development/developing-ios-11-applications-using-swift-4-video?utm_source=GitHub&utm_medium=repo&utm_campaign=9781788393546) 33 | 34 | * [Test-Driven iOS Development with Swift](https://www.packtpub.com/application-development/test-driven-ios-development-swift?utm_source=GitHub&utm_medium=repo&utm_campaign=9781785880735) 35 | 36 | * [Learning iOS 8 Game Development Using Swift](https://www.packtpub.com/game-development/learning-ios-8-game-development-using-swift?utm_source=GitHub&utm_medium=repo&utm_campaign=9781784393557) 37 | ### Suggestions and Feedback 38 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSe5qwunkGf6PUvzPirPDtuy1Du5Rlzew23UBp2S-P3wB-GcwQ/viewform) if you have any feedback or suggestions. 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDoTests/ItemManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManagerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ItemManagerTests: XCTestCase { 13 | 14 | var sut: ItemManager! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | sut = ItemManager() 20 | } 21 | 22 | override func tearDown() { 23 | 24 | sut = nil 25 | 26 | super.tearDown() 27 | } 28 | 29 | func test_ToDoCount_Initially_IsZero() { 30 | 31 | XCTAssertEqual(sut.toDoCount, 0) 32 | } 33 | 34 | func test_DoneCount_Initially_IsZero() { 35 | 36 | XCTAssertEqual(sut.doneCount, 0) 37 | } 38 | 39 | func test_AddItem_IncreasesToDoCountToOne() { 40 | sut.add(ToDoItem(title: "")) 41 | 42 | XCTAssertEqual(sut.toDoCount, 1) 43 | } 44 | 45 | func test_ItemAt_ReturnsAddedItem() { 46 | let item = ToDoItem(title: "Foo") 47 | sut.add(item) 48 | 49 | let returnedItem = sut.item(at: 0) 50 | 51 | XCTAssertEqual(returnedItem.title, item.title) 52 | } 53 | 54 | func test_CheckItemAt_ChangesCounts() { 55 | sut.add(ToDoItem(title: "")) 56 | 57 | sut.checkItem(at: 0) 58 | 59 | XCTAssertEqual(sut.toDoCount, 0) 60 | XCTAssertEqual(sut.doneCount, 1) 61 | } 62 | 63 | func test_CheckItemAt_RemovesItFromToDoItems() { 64 | let first = ToDoItem(title: "First") 65 | let second = ToDoItem(title: "Second") 66 | sut.add(first) 67 | sut.add(second) 68 | 69 | sut.checkItem(at: 0) 70 | 71 | XCTAssertEqual(sut.item(at: 0).title, 72 | "Second") 73 | } 74 | 75 | func test_DoneItemAt_ReturnsCheckedItem() { 76 | let item = ToDoItem(title: "Foo") 77 | sut.add(item) 78 | 79 | 80 | sut.checkItem(at: 0) 81 | let returnedItem = sut.doneItem(at: 0) 82 | 83 | XCTAssertEqual(returnedItem.title, item.title) 84 | } 85 | 86 | func test_RemoveAll_ResultsInCountsBeZero() { 87 | 88 | sut.add(ToDoItem(title: "Foo")) 89 | sut.add(ToDoItem(title: "Bar")) 90 | sut.checkItem(at: 0) 91 | 92 | 93 | XCTAssertEqual(sut.toDoCount, 1) 94 | XCTAssertEqual(sut.doneCount, 1) 95 | 96 | 97 | sut.removeAll() 98 | 99 | XCTAssertEqual(sut.toDoCount, 0) 100 | XCTAssertEqual(sut.doneCount, 0) 101 | } 102 | 103 | func test_Add_WhenItemIsAlreadyAdded_DoesNotIncreaseCount() { 104 | 105 | 106 | sut.add(ToDoItem(title: "Foo")) 107 | sut.add(ToDoItem(title: "Foo")) 108 | 109 | 110 | XCTAssertEqual(sut.toDoCount, 1) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/Model/ItemManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManagerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ItemManagerTests: XCTestCase { 13 | 14 | var sut: ItemManager! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | sut = ItemManager() 20 | } 21 | 22 | override func tearDown() { 23 | 24 | sut = nil 25 | 26 | super.tearDown() 27 | } 28 | 29 | func test_ToDoCount_Initially_IsZero() { 30 | 31 | XCTAssertEqual(sut.toDoCount, 0) 32 | } 33 | 34 | func test_DoneCount_Initially_IsZero() { 35 | 36 | XCTAssertEqual(sut.doneCount, 0) 37 | } 38 | 39 | func test_AddItem_IncreasesToDoCountToOne() { 40 | sut.add(ToDoItem(title: "")) 41 | 42 | XCTAssertEqual(sut.toDoCount, 1) 43 | } 44 | 45 | func test_ItemAt_ReturnsAddedItem() { 46 | let item = ToDoItem(title: "Foo") 47 | sut.add(item) 48 | 49 | let returnedItem = sut.item(at: 0) 50 | 51 | XCTAssertEqual(returnedItem.title, item.title) 52 | } 53 | 54 | func test_CheckItemAt_ChangesCounts() { 55 | sut.add(ToDoItem(title: "")) 56 | 57 | sut.checkItem(at: 0) 58 | 59 | XCTAssertEqual(sut.toDoCount, 0) 60 | XCTAssertEqual(sut.doneCount, 1) 61 | } 62 | 63 | func test_CheckItemAt_RemovesItFromToDoItems() { 64 | let first = ToDoItem(title: "First") 65 | let second = ToDoItem(title: "Second") 66 | sut.add(first) 67 | sut.add(second) 68 | 69 | sut.checkItem(at: 0) 70 | 71 | XCTAssertEqual(sut.item(at: 0).title, 72 | "Second") 73 | } 74 | 75 | func test_DoneItemAt_ReturnsCheckedItem() { 76 | let item = ToDoItem(title: "Foo") 77 | sut.add(item) 78 | 79 | 80 | sut.checkItem(at: 0) 81 | let returnedItem = sut.doneItem(at: 0) 82 | 83 | XCTAssertEqual(returnedItem.title, item.title) 84 | } 85 | 86 | func test_RemoveAll_ResultsInCountsBeZero() { 87 | 88 | sut.add(ToDoItem(title: "Foo")) 89 | sut.add(ToDoItem(title: "Bar")) 90 | sut.checkItem(at: 0) 91 | 92 | 93 | XCTAssertEqual(sut.toDoCount, 1) 94 | XCTAssertEqual(sut.doneCount, 1) 95 | 96 | 97 | sut.removeAll() 98 | 99 | XCTAssertEqual(sut.toDoCount, 0) 100 | XCTAssertEqual(sut.doneCount, 0) 101 | } 102 | 103 | func test_Add_WhenItemIsAlreadyAdded_DoesNotIncreaseCount() { 104 | 105 | 106 | sut.add(ToDoItem(title: "Foo")) 107 | sut.add(ToDoItem(title: "Foo")) 108 | 109 | 110 | XCTAssertEqual(sut.toDoCount, 1) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Model/ItemManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManagerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ItemManagerTests: XCTestCase { 13 | 14 | var sut: ItemManager! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | sut = ItemManager() 20 | } 21 | 22 | override func tearDown() { 23 | 24 | sut = nil 25 | 26 | super.tearDown() 27 | } 28 | 29 | func test_ToDoCount_Initially_IsZero() { 30 | 31 | XCTAssertEqual(sut.toDoCount, 0) 32 | } 33 | 34 | func test_DoneCount_Initially_IsZero() { 35 | 36 | XCTAssertEqual(sut.doneCount, 0) 37 | } 38 | 39 | func test_AddItem_IncreasesToDoCountToOne() { 40 | sut.add(ToDoItem(title: "")) 41 | 42 | XCTAssertEqual(sut.toDoCount, 1) 43 | } 44 | 45 | func test_ItemAt_ReturnsAddedItem() { 46 | let item = ToDoItem(title: "Foo") 47 | sut.add(item) 48 | 49 | let returnedItem = sut.item(at: 0) 50 | 51 | XCTAssertEqual(returnedItem.title, item.title) 52 | } 53 | 54 | func test_CheckItemAt_ChangesCounts() { 55 | sut.add(ToDoItem(title: "")) 56 | 57 | sut.checkItem(at: 0) 58 | 59 | XCTAssertEqual(sut.toDoCount, 0) 60 | XCTAssertEqual(sut.doneCount, 1) 61 | } 62 | 63 | func test_CheckItemAt_RemovesItFromToDoItems() { 64 | let first = ToDoItem(title: "First") 65 | let second = ToDoItem(title: "Second") 66 | sut.add(first) 67 | sut.add(second) 68 | 69 | sut.checkItem(at: 0) 70 | 71 | XCTAssertEqual(sut.item(at: 0).title, 72 | "Second") 73 | } 74 | 75 | func test_DoneItemAt_ReturnsCheckedItem() { 76 | let item = ToDoItem(title: "Foo") 77 | sut.add(item) 78 | 79 | 80 | sut.checkItem(at: 0) 81 | let returnedItem = sut.doneItem(at: 0) 82 | 83 | XCTAssertEqual(returnedItem.title, item.title) 84 | } 85 | 86 | func test_RemoveAll_ResultsInCountsBeZero() { 87 | 88 | sut.add(ToDoItem(title: "Foo")) 89 | sut.add(ToDoItem(title: "Bar")) 90 | sut.checkItem(at: 0) 91 | 92 | 93 | XCTAssertEqual(sut.toDoCount, 1) 94 | XCTAssertEqual(sut.doneCount, 1) 95 | 96 | 97 | sut.removeAll() 98 | 99 | XCTAssertEqual(sut.toDoCount, 0) 100 | XCTAssertEqual(sut.doneCount, 0) 101 | } 102 | 103 | func test_Add_WhenItemIsAlreadyAdded_DoesNotIncreaseCount() { 104 | 105 | 106 | sut.add(ToDoItem(title: "Foo")) 107 | sut.add(ToDoItem(title: "Foo")) 108 | 109 | 110 | XCTAssertEqual(sut.toDoCount, 1) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Controller/InputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputViewController.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreLocation 11 | 12 | class InputViewController: UIViewController { 13 | 14 | @IBOutlet var titleTextField: UITextField! 15 | @IBOutlet var dateTextField: UITextField! 16 | @IBOutlet var locationTextField: UITextField! 17 | @IBOutlet var addressTextField: UITextField! 18 | @IBOutlet var descriptionTextField: UITextField! 19 | @IBOutlet var saveButton: UIButton! 20 | 21 | lazy var geocoder = CLGeocoder() 22 | var itemManager: ItemManager? 23 | 24 | let dateFormatter: DateFormatter = { 25 | let dateFormatter = DateFormatter() 26 | dateFormatter.dateFormat = "MM/dd/yyyy" 27 | return dateFormatter 28 | }() 29 | 30 | @IBAction func save() { 31 | guard let titleString = titleTextField.text, 32 | titleString.characters.count > 0 else { return } 33 | let date: Date? 34 | if let dateText = self.dateTextField.text, 35 | dateText.characters.count > 0 { 36 | date = dateFormatter.date(from: dateText) 37 | } else { 38 | date = nil 39 | } 40 | let descriptionString = descriptionTextField.text 41 | if let locationName = locationTextField.text, 42 | locationName.characters.count > 0 { 43 | if let address = addressTextField.text, 44 | address.characters.count > 0 { 45 | 46 | 47 | geocoder.geocodeAddressString(address) { 48 | [unowned self] (placeMarks, error) -> Void in 49 | 50 | 51 | let placeMark = placeMarks?.first 52 | 53 | 54 | let item = ToDoItem( 55 | title: titleString, 56 | itemDescription: descriptionString, 57 | timestamp: date?.timeIntervalSince1970, 58 | location: Location( 59 | name: locationName, 60 | coordinate: placeMark?.location?.coordinate)) 61 | 62 | DispatchQueue.main.async(execute: { 63 | self.itemManager?.add(item) 64 | self.dismiss(animated: true) 65 | }) 66 | } 67 | } else { 68 | let item = ToDoItem(title: titleString, 69 | itemDescription: descriptionString, 70 | timestamp: date?.timeIntervalSince1970, 71 | location: nil) 72 | self.itemManager?.add(item) 73 | dismiss(animated: true) 74 | } 75 | } else { 76 | let item = ToDoItem( 77 | title: titleString, 78 | itemDescription: descriptionString, 79 | timestamp: date?.timeIntervalSince1970) 80 | 81 | self.itemManager?.add(item) 82 | dismiss(animated: true) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Model/ItemManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManager.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ItemManager: NSObject { 12 | var toDoCount: Int { return toDoItems.count } 13 | var doneCount: Int { return doneItems.count } 14 | private var toDoItems: [ToDoItem] = [] 15 | private var doneItems: [ToDoItem] = [] 16 | 17 | var toDoPathURL: URL { 18 | let fileURLs = FileManager.default.urls( 19 | for: .documentDirectory, in: .userDomainMask) 20 | 21 | 22 | guard let documentURL = fileURLs.first else { 23 | print("Something went wrong. Documents url could not be found") 24 | fatalError() 25 | } 26 | 27 | 28 | return documentURL.appendingPathComponent("toDoItems.plist") 29 | } 30 | 31 | override init() { 32 | super.init() 33 | 34 | NotificationCenter.default.addObserver( 35 | self, 36 | selector: #selector(save), 37 | name: .UIApplicationWillResignActive, 38 | object: nil) 39 | 40 | if let nsToDoItems = NSArray(contentsOf: toDoPathURL) { 41 | 42 | for dict in nsToDoItems { 43 | if let toDoItem = ToDoItem(dict: dict as! [String:Any]) { 44 | toDoItems.append(toDoItem) 45 | } 46 | } 47 | } 48 | } 49 | 50 | deinit { 51 | NotificationCenter.default.removeObserver(self) 52 | save() 53 | } 54 | 55 | func add(_ item: ToDoItem) { 56 | if !toDoItems.contains(item) { 57 | toDoItems.append(item) 58 | } 59 | } 60 | 61 | func item(at index: Int) -> ToDoItem { 62 | return toDoItems[index] 63 | } 64 | 65 | func checkItem(at index: Int) { 66 | let item = toDoItems.remove(at: index) 67 | doneItems.append(item) 68 | } 69 | 70 | func doneItem(at index: Int) -> ToDoItem { 71 | return doneItems[index] 72 | } 73 | 74 | func removeAll() { 75 | toDoItems.removeAll() 76 | doneItems.removeAll() 77 | } 78 | 79 | func uncheckItem(at index: Int) { 80 | let item = doneItems.remove(at: index) 81 | toDoItems.append(item) 82 | } 83 | 84 | @objc func save() { 85 | let nsToDoItems = toDoItems.map { $0.plistDict } 86 | 87 | guard nsToDoItems.count > 0 else { 88 | try? FileManager.default.removeItem(at: toDoPathURL) 89 | return 90 | } 91 | do { 92 | let plistData = try PropertyListSerialization.data( 93 | fromPropertyList: nsToDoItems, 94 | format: PropertyListSerialization.PropertyListFormat.xml, 95 | options: PropertyListSerialization.WriteOptions(0) 96 | ) 97 | try plistData.write(to: toDoPathURL, 98 | options: Data.WritingOptions.atomic) 99 | } catch { 100 | print(error) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Chapter06/ToDoTests/Controller/DetailViewControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewControllerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 27.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class DetailViewControllerTests: XCTestCase { 14 | 15 | var sut: DetailViewController! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | 20 | let storyboard = UIStoryboard(name: "Main", 21 | bundle: nil) 22 | sut = storyboard 23 | .instantiateViewController( 24 | withIdentifier: "DetailViewController") 25 | as! DetailViewController 26 | sut.loadViewIfNeeded() 27 | } 28 | 29 | override func tearDown() { 30 | sut.itemInfo?.0.removeAll() 31 | 32 | super.tearDown() 33 | } 34 | 35 | func test_HasTitleLabel() { 36 | let titleLabelIsSubView = sut.titleLabel?.isDescendant(of: sut.view) ?? false 37 | XCTAssertTrue(titleLabelIsSubView) 38 | } 39 | 40 | func test_HasMapView() { 41 | let mapViewIsSubView = 42 | sut.mapView?.isDescendant( 43 | of: sut.view) ?? false 44 | XCTAssertTrue(mapViewIsSubView) 45 | } 46 | 47 | func test_SettingItemInfo_SetsTextsToLabels() { 48 | let coordinate = 49 | CLLocationCoordinate2DMake( 50 | 51.2277, 6.7735) 51 | 52 | let location = Location(name: "Foo", coordinate: coordinate) 53 | let dateFormatter = DateFormatter() 54 | dateFormatter.dateFormat = "MM/dd/yyyy" 55 | let date = dateFormatter.date(from: "08/27/2017") 56 | let timestamp = date?.timeIntervalSince1970 57 | 58 | let item = ToDoItem(title: "Bar", 59 | itemDescription: "Baz", 60 | timestamp: timestamp, 61 | location: location) 62 | 63 | 64 | let itemManager = ItemManager() 65 | itemManager.add(item) 66 | 67 | sut.itemInfo = (itemManager, 0) 68 | 69 | sut.beginAppearanceTransition(true, animated: true) 70 | sut.endAppearanceTransition() 71 | XCTAssertEqual(sut.titleLabel.text, "Bar") 72 | XCTAssertEqual(sut.dateLabel.text, "08/27/2017") 73 | XCTAssertEqual(sut.locationLabel.text, "Foo") 74 | XCTAssertEqual(sut.descriptionLabel.text, "Baz") 75 | XCTAssertEqual(sut.mapView.centerCoordinate.latitude, 76 | coordinate.latitude, 77 | accuracy: 0.001) 78 | XCTAssertEqual(sut.mapView.centerCoordinate.longitude, 79 | coordinate.longitude, 80 | accuracy: 0.001) 81 | } 82 | 83 | func test_CheckItem_ChecksItemInItemManager() { 84 | let itemManager = ItemManager() 85 | itemManager.add(ToDoItem(title: "Foo")) 86 | 87 | sut.itemInfo = (itemManager, 0) 88 | sut.checkItem() 89 | 90 | XCTAssertEqual(itemManager.toDoCount, 0) 91 | XCTAssertEqual(itemManager.doneCount, 1) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/DetailViewControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewControllerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 27.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class DetailViewControllerTests: XCTestCase { 14 | 15 | var sut: DetailViewController! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | 20 | let storyboard = UIStoryboard(name: "Main", 21 | bundle: nil) 22 | sut = storyboard 23 | .instantiateViewController( 24 | withIdentifier: "DetailViewController") 25 | as! DetailViewController 26 | sut.loadViewIfNeeded() 27 | } 28 | 29 | override func tearDown() { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | super.tearDown() 32 | } 33 | 34 | func test_HasTitleLabel() { 35 | let titleLabelIsSubView = sut.titleLabel?.isDescendant(of: sut.view) ?? false 36 | XCTAssertTrue(titleLabelIsSubView) 37 | } 38 | 39 | func test_HasMapView() { 40 | let mapViewIsSubView = 41 | sut.mapView?.isDescendant( 42 | of: sut.view) ?? false 43 | XCTAssertTrue(mapViewIsSubView) 44 | } 45 | 46 | func test_SettingItemInfo_SetsTextsToLabels() { 47 | let coordinate = 48 | CLLocationCoordinate2DMake( 49 | 51.2277, 6.7735) 50 | 51 | let location = Location(name: "Foo", coordinate: coordinate) 52 | let dateFormatter = DateFormatter() 53 | dateFormatter.dateFormat = "MM/dd/yyyy" 54 | let date = dateFormatter.date(from: "08/27/2017") 55 | let timestamp = date?.timeIntervalSince1970 56 | 57 | let item = ToDoItem(title: "Bar", 58 | itemDescription: "Baz", 59 | timestamp: timestamp, 60 | location: location) 61 | 62 | 63 | let itemManager = ItemManager() 64 | itemManager.add(item) 65 | 66 | sut.itemInfo = (itemManager, 0) 67 | 68 | sut.beginAppearanceTransition(true, animated: true) 69 | sut.endAppearanceTransition() 70 | XCTAssertEqual(sut.titleLabel.text, "Bar") 71 | XCTAssertEqual(sut.dateLabel.text, "08/27/2017") 72 | XCTAssertEqual(sut.locationLabel.text, "Foo") 73 | XCTAssertEqual(sut.descriptionLabel.text, "Baz") 74 | XCTAssertEqual(sut.mapView.centerCoordinate.latitude, 75 | coordinate.latitude, 76 | accuracy: 0.001) 77 | XCTAssertEqual(sut.mapView.centerCoordinate.longitude, 78 | coordinate.longitude, 79 | accuracy: 0.001) 80 | } 81 | 82 | func test_CheckItem_ChecksItemInItemManager() { 83 | let itemManager = ItemManager() 84 | itemManager.add(ToDoItem(title: "Foo")) 85 | 86 | sut.itemInfo = (itemManager, 0) 87 | sut.checkItem() 88 | 89 | XCTAssertEqual(itemManager.toDoCount, 0) 90 | XCTAssertEqual(itemManager.doneCount, 1) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Controller/DetailViewControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewControllerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 27.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class DetailViewControllerTests: XCTestCase { 14 | 15 | var sut: DetailViewController! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | 20 | let storyboard = UIStoryboard(name: "Main", 21 | bundle: nil) 22 | sut = storyboard 23 | .instantiateViewController( 24 | withIdentifier: "DetailViewController") 25 | as! DetailViewController 26 | sut.loadViewIfNeeded() 27 | } 28 | 29 | override func tearDown() { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | super.tearDown() 32 | } 33 | 34 | func test_HasTitleLabel() { 35 | let titleLabelIsSubView = sut.titleLabel?.isDescendant(of: sut.view) ?? false 36 | XCTAssertTrue(titleLabelIsSubView) 37 | } 38 | 39 | func test_HasMapView() { 40 | let mapViewIsSubView = 41 | sut.mapView?.isDescendant( 42 | of: sut.view) ?? false 43 | XCTAssertTrue(mapViewIsSubView) 44 | } 45 | 46 | func test_SettingItemInfo_SetsTextsToLabels() { 47 | let coordinate = 48 | CLLocationCoordinate2DMake( 49 | 51.2277, 6.7735) 50 | 51 | let location = Location(name: "Foo", coordinate: coordinate) 52 | let dateFormatter = DateFormatter() 53 | dateFormatter.dateFormat = "MM/dd/yyyy" 54 | let date = dateFormatter.date(from: "08/27/2017") 55 | let timestamp = date?.timeIntervalSince1970 56 | 57 | let item = ToDoItem(title: "Bar", 58 | itemDescription: "Baz", 59 | timestamp: timestamp, 60 | location: location) 61 | 62 | 63 | let itemManager = ItemManager() 64 | itemManager.add(item) 65 | 66 | sut.itemInfo = (itemManager, 0) 67 | 68 | sut.beginAppearanceTransition(true, animated: true) 69 | sut.endAppearanceTransition() 70 | XCTAssertEqual(sut.titleLabel.text, "Bar") 71 | XCTAssertEqual(sut.dateLabel.text, "08/27/2017") 72 | XCTAssertEqual(sut.locationLabel.text, "Foo") 73 | XCTAssertEqual(sut.descriptionLabel.text, "Baz") 74 | XCTAssertEqual(sut.mapView.centerCoordinate.latitude, 75 | coordinate.latitude, 76 | accuracy: 0.001) 77 | XCTAssertEqual(sut.mapView.centerCoordinate.longitude, 78 | coordinate.longitude, 79 | accuracy: 0.001) 80 | } 81 | 82 | func test_CheckItem_ChecksItemInItemManager() { 83 | let itemManager = ItemManager() 84 | itemManager.add(ToDoItem(title: "Foo")) 85 | 86 | sut.itemInfo = (itemManager, 0) 87 | sut.checkItem() 88 | 89 | XCTAssertEqual(itemManager.toDoCount, 0) 90 | XCTAssertEqual(itemManager.doneCount, 1) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo/ItemListDataProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListDataProvider.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum Section: Int { 12 | case toDo 13 | case done 14 | } 15 | 16 | class ItemListDataProvider: NSObject, UITableViewDataSource, UITableViewDelegate { 17 | 18 | var itemManager: ItemManager? 19 | 20 | func numberOfSections( 21 | in tableView: UITableView) -> Int { 22 | 23 | return 2 24 | } 25 | 26 | func tableView(_ tableView: UITableView, 27 | numberOfRowsInSection section: Int) -> Int { 28 | 29 | 30 | guard let itemManager = 31 | itemManager else { return 0 } 32 | guard let itemSection = 33 | Section(rawValue: section) else { 34 | fatalError() 35 | } 36 | 37 | 38 | let numberOfRows: Int 39 | 40 | 41 | switch itemSection { 42 | case .toDo: 43 | numberOfRows = itemManager.toDoCount 44 | case .done: 45 | numberOfRows = itemManager.doneCount 46 | } 47 | return numberOfRows 48 | } 49 | 50 | func tableView(_ tableView: UITableView, 51 | cellForRowAt indexPath: IndexPath) -> UITableViewCell { 52 | 53 | let cell = tableView.dequeueReusableCell( 54 | withIdentifier: "ItemCell", 55 | for: indexPath) as! ItemCell 56 | 57 | guard let itemManager = itemManager else { fatalError() } 58 | guard let section = Section(rawValue: indexPath.section) else 59 | { 60 | fatalError() 61 | } 62 | 63 | let item: ToDoItem 64 | switch section { 65 | case .toDo: 66 | item = itemManager.item(at: indexPath.row) 67 | case .done: 68 | item = itemManager.doneItem(at: indexPath.row) 69 | } 70 | 71 | cell.configCell(with: item) 72 | 73 | return cell 74 | } 75 | 76 | func tableView( 77 | _ tableView: UITableView, 78 | titleForDeleteConfirmationButtonForRowAt indexPath: 79 | IndexPath) -> String? { 80 | 81 | guard let section = Section(rawValue: indexPath.section) else 82 | { 83 | fatalError() 84 | } 85 | 86 | let buttonTitle: String 87 | switch section { 88 | case .toDo: 89 | buttonTitle = "Check" 90 | case .done: 91 | buttonTitle = "Uncheck" 92 | } 93 | 94 | return buttonTitle 95 | } 96 | 97 | func tableView(_ tableView: UITableView, 98 | commit editingStyle: UITableViewCellEditingStyle, 99 | forRowAt indexPath: IndexPath) { 100 | 101 | 102 | guard let itemManager = itemManager else { fatalError() } 103 | guard let section = Section(rawValue: indexPath.section) else 104 | { 105 | fatalError() 106 | } 107 | 108 | 109 | switch section { 110 | case .toDo: 111 | itemManager.checkItem(at: indexPath.row) 112 | case .done: 113 | itemManager.uncheckItem(at: indexPath.row) 114 | } 115 | tableView.reloadData() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDo/Controller/ItemListDataProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListDataProvider.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum Section: Int { 12 | case toDo 13 | case done 14 | } 15 | 16 | class ItemListDataProvider: NSObject, UITableViewDataSource, UITableViewDelegate { 17 | 18 | var itemManager: ItemManager? 19 | 20 | func numberOfSections( 21 | in tableView: UITableView) -> Int { 22 | 23 | return 2 24 | } 25 | 26 | func tableView(_ tableView: UITableView, 27 | numberOfRowsInSection section: Int) -> Int { 28 | 29 | 30 | guard let itemManager = 31 | itemManager else { return 0 } 32 | guard let itemSection = 33 | Section(rawValue: section) else { 34 | fatalError() 35 | } 36 | 37 | 38 | let numberOfRows: Int 39 | 40 | 41 | switch itemSection { 42 | case .toDo: 43 | numberOfRows = itemManager.toDoCount 44 | case .done: 45 | numberOfRows = itemManager.doneCount 46 | } 47 | return numberOfRows 48 | } 49 | 50 | func tableView(_ tableView: UITableView, 51 | cellForRowAt indexPath: IndexPath) -> UITableViewCell { 52 | 53 | let cell = tableView.dequeueReusableCell( 54 | withIdentifier: "ItemCell", 55 | for: indexPath) as! ItemCell 56 | 57 | guard let itemManager = itemManager else { fatalError() } 58 | guard let section = Section(rawValue: indexPath.section) else 59 | { 60 | fatalError() 61 | } 62 | 63 | let item: ToDoItem 64 | switch section { 65 | case .toDo: 66 | item = itemManager.item(at: indexPath.row) 67 | case .done: 68 | item = itemManager.doneItem(at: indexPath.row) 69 | } 70 | 71 | cell.configCell(with: item) 72 | 73 | return cell 74 | } 75 | 76 | func tableView( 77 | _ tableView: UITableView, 78 | titleForDeleteConfirmationButtonForRowAt indexPath: 79 | IndexPath) -> String? { 80 | 81 | guard let section = Section(rawValue: indexPath.section) else 82 | { 83 | fatalError() 84 | } 85 | 86 | let buttonTitle: String 87 | switch section { 88 | case .toDo: 89 | buttonTitle = "Check" 90 | case .done: 91 | buttonTitle = "Uncheck" 92 | } 93 | 94 | return buttonTitle 95 | } 96 | 97 | func tableView(_ tableView: UITableView, 98 | commit editingStyle: UITableViewCellEditingStyle, 99 | forRowAt indexPath: IndexPath) { 100 | 101 | 102 | guard let itemManager = itemManager else { fatalError() } 103 | guard let section = Section(rawValue: indexPath.section) else 104 | { 105 | fatalError() 106 | } 107 | 108 | 109 | switch section { 110 | case .toDo: 111 | itemManager.checkItem(at: indexPath.row) 112 | case .done: 113 | itemManager.uncheckItem(at: indexPath.row) 114 | } 115 | tableView.reloadData() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Chapter06/ToDoTests/Model/ItemManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemManagerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ItemManagerTests: XCTestCase { 13 | 14 | var sut: ItemManager! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | sut = ItemManager() 20 | } 21 | 22 | override func tearDown() { 23 | 24 | sut.removeAll() 25 | sut = nil 26 | 27 | super.tearDown() 28 | } 29 | 30 | func test_ToDoCount_Initially_IsZero() { 31 | 32 | XCTAssertEqual(sut.toDoCount, 0) 33 | } 34 | 35 | func test_DoneCount_Initially_IsZero() { 36 | 37 | XCTAssertEqual(sut.doneCount, 0) 38 | } 39 | 40 | func test_AddItem_IncreasesToDoCountToOne() { 41 | sut.add(ToDoItem(title: "")) 42 | 43 | XCTAssertEqual(sut.toDoCount, 1) 44 | } 45 | 46 | func test_ItemAt_ReturnsAddedItem() { 47 | let item = ToDoItem(title: "Foo") 48 | sut.add(item) 49 | 50 | let returnedItem = sut.item(at: 0) 51 | 52 | XCTAssertEqual(returnedItem.title, item.title) 53 | } 54 | 55 | func test_CheckItemAt_ChangesCounts() { 56 | sut.add(ToDoItem(title: "")) 57 | 58 | sut.checkItem(at: 0) 59 | 60 | XCTAssertEqual(sut.toDoCount, 0) 61 | XCTAssertEqual(sut.doneCount, 1) 62 | } 63 | 64 | func test_CheckItemAt_RemovesItFromToDoItems() { 65 | let first = ToDoItem(title: "First") 66 | let second = ToDoItem(title: "Second") 67 | sut.add(first) 68 | sut.add(second) 69 | 70 | sut.checkItem(at: 0) 71 | 72 | XCTAssertEqual(sut.item(at: 0).title, 73 | "Second") 74 | } 75 | 76 | func test_DoneItemAt_ReturnsCheckedItem() { 77 | let item = ToDoItem(title: "Foo") 78 | sut.add(item) 79 | 80 | 81 | sut.checkItem(at: 0) 82 | let returnedItem = sut.doneItem(at: 0) 83 | 84 | XCTAssertEqual(returnedItem.title, item.title) 85 | } 86 | 87 | func test_RemoveAll_ResultsInCountsBeZero() { 88 | 89 | sut.add(ToDoItem(title: "Foo")) 90 | sut.add(ToDoItem(title: "Bar")) 91 | sut.checkItem(at: 0) 92 | 93 | 94 | XCTAssertEqual(sut.toDoCount, 1) 95 | XCTAssertEqual(sut.doneCount, 1) 96 | 97 | 98 | sut.removeAll() 99 | 100 | XCTAssertEqual(sut.toDoCount, 0) 101 | XCTAssertEqual(sut.doneCount, 0) 102 | } 103 | 104 | func test_Add_WhenItemIsAlreadyAdded_DoesNotIncreaseCount() { 105 | 106 | 107 | sut.add(ToDoItem(title: "Foo")) 108 | sut.add(ToDoItem(title: "Foo")) 109 | 110 | 111 | XCTAssertEqual(sut.toDoCount, 1) 112 | } 113 | 114 | func test_ToDoItemsGetSerialized() { 115 | var itemManager: ItemManager? = ItemManager() 116 | 117 | 118 | let firstItem = ToDoItem(title: "First") 119 | itemManager!.add(firstItem) 120 | 121 | 122 | let secondItem = ToDoItem(title: "Second") 123 | itemManager!.add(secondItem) 124 | 125 | 126 | NotificationCenter.default.post( 127 | name: .UIApplicationWillResignActive, 128 | object: nil) 129 | 130 | 131 | itemManager = nil 132 | 133 | 134 | XCTAssertNil(itemManager) 135 | 136 | 137 | itemManager = ItemManager() 138 | XCTAssertEqual(itemManager?.toDoCount, 2) 139 | XCTAssertEqual(itemManager?.item(at: 0), firstItem) 140 | XCTAssertEqual(itemManager?.item(at: 1), secondItem) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDoTests/ToDoItemTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItemTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ToDoItemTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func test_Init_WhenGivenTitle_SetsTitle() { 25 | let item = ToDoItem(title: "Foo") 26 | 27 | XCTAssertEqual(item.title, "Foo", "should set title") 28 | } 29 | 30 | func test_Init_WhenGivenDescription_SetsDescripiton() { 31 | 32 | let item = ToDoItem(title: "", 33 | itemDescription: "Bar") 34 | 35 | XCTAssertEqual(item.itemDescription, 36 | "Bar", 37 | "should set itemDescription") 38 | } 39 | 40 | func test_Init_SetsTimestamp() { 41 | 42 | let item = ToDoItem(title: "", 43 | timestamp: 0.0) 44 | 45 | XCTAssertEqual(item.timestamp, 0.0) 46 | } 47 | 48 | func test_Init_SetsLocation() { 49 | 50 | let location = Location(name: "Foo") 51 | 52 | let item = ToDoItem(title: "", 53 | location: location) 54 | 55 | XCTAssertEqual(item.location?.name, 56 | location.name) 57 | } 58 | 59 | func test_EqualItems_AreEqual() { 60 | let first = ToDoItem(title: "Foo") 61 | let second = ToDoItem(title: "Foo") 62 | 63 | 64 | XCTAssertEqual(first, second) 65 | } 66 | 67 | func test_Items_WhenLocationDiffers_AreNotEqual() { 68 | 69 | 70 | var first = ToDoItem(title: "", 71 | location: Location(name: "Foo")) 72 | var second = ToDoItem(title: "", 73 | location: Location(name: "Bar")) 74 | 75 | 76 | XCTAssertNotEqual(first, second) 77 | 78 | 79 | first = ToDoItem(title: "", 80 | location: nil) 81 | second = ToDoItem(title: "", 82 | location: Location(name: "Foo")) 83 | 84 | 85 | XCTAssertNotEqual(first, second) 86 | } 87 | 88 | func test_Items_WhenOneLocationIsNil_AreNotEqual() { 89 | 90 | 91 | let first = ToDoItem(title: "", 92 | location: Location(name: "Foo")) 93 | let second = ToDoItem(title: "", 94 | location: nil) 95 | 96 | 97 | XCTAssertNotEqual(first, second) 98 | } 99 | 100 | func test_Items_WhenTimestampsDiffer_AreNotEqual() { 101 | 102 | 103 | let first = ToDoItem(title: "Foo", 104 | timestamp: 1.0) 105 | let second = ToDoItem(title: "Foo", 106 | timestamp: 0.0) 107 | 108 | 109 | XCTAssertNotEqual(first, second) 110 | } 111 | 112 | func test_Items_WhenDescriptionsDiffer_AreNotEqual() { 113 | 114 | 115 | let first = ToDoItem(title: "Foo", 116 | itemDescription: "Bar") 117 | let second = ToDoItem(title: "Foo", 118 | itemDescription: "Baz") 119 | 120 | 121 | XCTAssertNotEqual(first, second) 122 | } 123 | 124 | func test_Items_WhenTitlesDiffer_AreNotEqual() { 125 | 126 | 127 | let first = ToDoItem(title: "Foo") 128 | let second = ToDoItem(title: "Bar") 129 | 130 | 131 | XCTAssertNotEqual(first, second) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Chapter06/ToDo/Controller/ItemListDataProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListDataProvider.swift 3 | // ToDo 4 | // 5 | // Created by dasdom on 25.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum Section: Int { 12 | case toDo 13 | case done 14 | } 15 | 16 | class ItemListDataProvider: NSObject, UITableViewDataSource, UITableViewDelegate, ItemManagerSettable { 17 | 18 | var itemManager: ItemManager? 19 | 20 | func numberOfSections( 21 | in tableView: UITableView) -> Int { 22 | 23 | return 2 24 | } 25 | 26 | func tableView(_ tableView: UITableView, 27 | numberOfRowsInSection section: Int) -> Int { 28 | 29 | 30 | guard let itemManager = 31 | itemManager else { return 0 } 32 | guard let itemSection = 33 | Section(rawValue: section) else { 34 | fatalError() 35 | } 36 | 37 | 38 | let numberOfRows: Int 39 | 40 | 41 | switch itemSection { 42 | case .toDo: 43 | numberOfRows = itemManager.toDoCount 44 | case .done: 45 | numberOfRows = itemManager.doneCount 46 | } 47 | return numberOfRows 48 | } 49 | 50 | func tableView(_ tableView: UITableView, 51 | cellForRowAt indexPath: IndexPath) -> UITableViewCell { 52 | 53 | let cell = tableView.dequeueReusableCell( 54 | withIdentifier: "ItemCell", 55 | for: indexPath) as! ItemCell 56 | 57 | guard let itemManager = itemManager else { fatalError() } 58 | guard let section = Section(rawValue: indexPath.section) else 59 | { 60 | fatalError() 61 | } 62 | 63 | let item: ToDoItem 64 | switch section { 65 | case .toDo: 66 | item = itemManager.item(at: indexPath.row) 67 | case .done: 68 | item = itemManager.doneItem(at: indexPath.row) 69 | } 70 | 71 | cell.configCell(with: item) 72 | 73 | return cell 74 | } 75 | 76 | func tableView( 77 | _ tableView: UITableView, 78 | titleForDeleteConfirmationButtonForRowAt indexPath: 79 | IndexPath) -> String? { 80 | 81 | guard let section = Section(rawValue: indexPath.section) else 82 | { 83 | fatalError() 84 | } 85 | 86 | let buttonTitle: String 87 | switch section { 88 | case .toDo: 89 | buttonTitle = "Check" 90 | case .done: 91 | buttonTitle = "Uncheck" 92 | } 93 | 94 | return buttonTitle 95 | } 96 | 97 | func tableView(_ tableView: UITableView, 98 | commit editingStyle: UITableViewCellEditingStyle, 99 | forRowAt indexPath: IndexPath) { 100 | 101 | 102 | guard let itemManager = itemManager else { fatalError() } 103 | guard let section = Section(rawValue: indexPath.section) else 104 | { 105 | fatalError() 106 | } 107 | 108 | 109 | switch section { 110 | case .toDo: 111 | itemManager.checkItem(at: indexPath.row) 112 | case .done: 113 | itemManager.uncheckItem(at: indexPath.row) 114 | } 115 | tableView.reloadData() 116 | } 117 | 118 | func tableView(_ tableView: UITableView, 119 | didSelectRowAt indexPath: IndexPath) { 120 | guard let itemSection = Section(rawValue: indexPath.section) else 121 | { fatalError() } 122 | 123 | 124 | switch itemSection { 125 | case .toDo: 126 | NotificationCenter.default.post( 127 | name: NSNotification.Name("ItemSelectedNotification"), 128 | object: self, 129 | userInfo: ["index": indexPath.row]) 130 | 131 | 132 | default: 133 | break 134 | } 135 | } 136 | } 137 | 138 | @objc protocol ItemManagerSettable { 139 | var itemManager: ItemManager? { get set } 140 | } 141 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/Model/ToDoItemTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItemTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ToDoItemTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func test_Init_WhenGivenTitle_SetsTitle() { 25 | let item = ToDoItem(title: "Foo") 26 | 27 | XCTAssertEqual(item.title, "Foo", "should set title") 28 | } 29 | 30 | func test_Init_WhenGivenDescription_SetsDescripiton() { 31 | 32 | let item = ToDoItem(title: "", 33 | itemDescription: "Bar") 34 | 35 | XCTAssertEqual(item.itemDescription, 36 | "Bar", 37 | "should set itemDescription") 38 | } 39 | 40 | func test_Init_SetsTimestamp() { 41 | 42 | let item = ToDoItem(title: "", 43 | timestamp: 0.0) 44 | 45 | XCTAssertEqual(item.timestamp, 0.0) 46 | } 47 | 48 | func test_Init_SetsLocation() { 49 | 50 | let location = Location(name: "Foo") 51 | 52 | let item = ToDoItem(title: "", 53 | location: location) 54 | 55 | XCTAssertEqual(item.location?.name, 56 | location.name) 57 | } 58 | 59 | func test_EqualItems_AreEqual() { 60 | let first = ToDoItem(title: "Foo") 61 | let second = ToDoItem(title: "Foo") 62 | 63 | 64 | XCTAssertEqual(first, second) 65 | } 66 | 67 | func test_Items_WhenLocationDiffers_AreNotEqual() { 68 | 69 | 70 | var first = ToDoItem(title: "", 71 | location: Location(name: "Foo")) 72 | var second = ToDoItem(title: "", 73 | location: Location(name: "Bar")) 74 | 75 | 76 | XCTAssertNotEqual(first, second) 77 | 78 | 79 | first = ToDoItem(title: "", 80 | location: nil) 81 | second = ToDoItem(title: "", 82 | location: Location(name: "Foo")) 83 | 84 | 85 | XCTAssertNotEqual(first, second) 86 | } 87 | 88 | func test_Items_WhenOneLocationIsNil_AreNotEqual() { 89 | 90 | 91 | let first = ToDoItem(title: "", 92 | location: Location(name: "Foo")) 93 | let second = ToDoItem(title: "", 94 | location: nil) 95 | 96 | 97 | XCTAssertNotEqual(first, second) 98 | } 99 | 100 | func test_Items_WhenTimestampsDiffer_AreNotEqual() { 101 | 102 | 103 | let first = ToDoItem(title: "Foo", 104 | timestamp: 1.0) 105 | let second = ToDoItem(title: "Foo", 106 | timestamp: 0.0) 107 | 108 | 109 | XCTAssertNotEqual(first, second) 110 | } 111 | 112 | func test_Items_WhenDescriptionsDiffer_AreNotEqual() { 113 | 114 | 115 | let first = ToDoItem(title: "Foo", 116 | itemDescription: "Bar") 117 | let second = ToDoItem(title: "Foo", 118 | itemDescription: "Baz") 119 | 120 | 121 | XCTAssertNotEqual(first, second) 122 | } 123 | 124 | func test_Items_WhenTitlesDiffer_AreNotEqual() { 125 | 126 | 127 | let first = ToDoItem(title: "Foo") 128 | let second = ToDoItem(title: "Bar") 129 | 130 | 131 | XCTAssertNotEqual(first, second) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Model/ToDoItemTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToDoItemTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | 12 | class ToDoItemTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func test_Init_WhenGivenTitle_SetsTitle() { 25 | let item = ToDoItem(title: "Foo") 26 | 27 | XCTAssertEqual(item.title, "Foo", "should set title") 28 | } 29 | 30 | func test_Init_WhenGivenDescription_SetsDescripiton() { 31 | 32 | let item = ToDoItem(title: "", 33 | itemDescription: "Bar") 34 | 35 | XCTAssertEqual(item.itemDescription, 36 | "Bar", 37 | "should set itemDescription") 38 | } 39 | 40 | func test_Init_SetsTimestamp() { 41 | 42 | let item = ToDoItem(title: "", 43 | timestamp: 0.0) 44 | 45 | XCTAssertEqual(item.timestamp, 0.0) 46 | } 47 | 48 | func test_Init_SetsLocation() { 49 | 50 | let location = Location(name: "Foo") 51 | 52 | let item = ToDoItem(title: "", 53 | location: location) 54 | 55 | XCTAssertEqual(item.location?.name, 56 | location.name) 57 | } 58 | 59 | func test_EqualItems_AreEqual() { 60 | let first = ToDoItem(title: "Foo") 61 | let second = ToDoItem(title: "Foo") 62 | 63 | 64 | XCTAssertEqual(first, second) 65 | } 66 | 67 | func test_Items_WhenLocationDiffers_AreNotEqual() { 68 | 69 | 70 | var first = ToDoItem(title: "", 71 | location: Location(name: "Foo")) 72 | var second = ToDoItem(title: "", 73 | location: Location(name: "Bar")) 74 | 75 | 76 | XCTAssertNotEqual(first, second) 77 | 78 | 79 | first = ToDoItem(title: "", 80 | location: nil) 81 | second = ToDoItem(title: "", 82 | location: Location(name: "Foo")) 83 | 84 | 85 | XCTAssertNotEqual(first, second) 86 | } 87 | 88 | func test_Items_WhenOneLocationIsNil_AreNotEqual() { 89 | 90 | 91 | let first = ToDoItem(title: "", 92 | location: Location(name: "Foo")) 93 | let second = ToDoItem(title: "", 94 | location: nil) 95 | 96 | 97 | XCTAssertNotEqual(first, second) 98 | } 99 | 100 | func test_Items_WhenTimestampsDiffer_AreNotEqual() { 101 | 102 | 103 | let first = ToDoItem(title: "Foo", 104 | timestamp: 1.0) 105 | let second = ToDoItem(title: "Foo", 106 | timestamp: 0.0) 107 | 108 | 109 | XCTAssertNotEqual(first, second) 110 | } 111 | 112 | func test_Items_WhenDescriptionsDiffer_AreNotEqual() { 113 | 114 | 115 | let first = ToDoItem(title: "Foo", 116 | itemDescription: "Bar") 117 | let second = ToDoItem(title: "Foo", 118 | itemDescription: "Baz") 119 | 120 | 121 | XCTAssertNotEqual(first, second) 122 | } 123 | 124 | func test_Items_WhenTitlesDiffer_AreNotEqual() { 125 | 126 | 127 | let first = ToDoItem(title: "Foo") 128 | let second = ToDoItem(title: "Bar") 129 | 130 | 131 | XCTAssertNotEqual(first, second) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Chapter06/ToDoTests/Controller/ItemCellTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCellTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 27.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | @testable import ToDo 12 | 13 | class ItemCellTests: XCTestCase { 14 | 15 | var tableView: UITableView! 16 | let dataSource = FakeDataSource() 17 | var cell: ItemCell! 18 | 19 | override func setUp() { 20 | super.setUp() 21 | 22 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 23 | let controller = storyboard 24 | .instantiateViewController( 25 | withIdentifier: "ItemListViewController") 26 | as! ItemListViewController 27 | 28 | 29 | controller.loadViewIfNeeded() 30 | 31 | 32 | tableView = controller.tableView 33 | tableView?.dataSource = dataSource 34 | 35 | 36 | cell = tableView?.dequeueReusableCell( 37 | withIdentifier: "ItemCell", 38 | for: IndexPath(row: 0, section: 0)) as! ItemCell 39 | 40 | } 41 | 42 | override func tearDown() { 43 | // Put teardown code here. This method is called after the invocation of each test method in the class. 44 | super.tearDown() 45 | } 46 | 47 | func test_HasNameLabel() { 48 | XCTAssertTrue(cell.titleLabel.isDescendant(of: cell.contentView)) 49 | } 50 | 51 | func test_HasLocationLabel() { 52 | XCTAssertTrue(cell.locationLabel.isDescendant(of: cell.contentView)) 53 | } 54 | 55 | func test_HasDateLabel() { 56 | XCTAssertTrue(cell.dateLabel.isDescendant(of: cell.contentView)) 57 | } 58 | 59 | func test_ConfigCell_SetsTitle() { 60 | cell.configCell(with: ToDoItem(title: "Foo")) 61 | 62 | XCTAssertEqual(cell.titleLabel.text, "Foo") 63 | } 64 | 65 | func test_ConfigCell_SetsDate() { 66 | let dateFormatter = DateFormatter() 67 | dateFormatter.dateFormat = "MM/dd/yyyy" 68 | let date = dateFormatter.date(from: "08/27/2017") 69 | let timestamp = date?.timeIntervalSince1970 70 | 71 | cell.configCell(with: ToDoItem(title: "Foo", 72 | timestamp: timestamp)) 73 | 74 | XCTAssertEqual(cell.dateLabel.text, "08/27/2017") 75 | } 76 | 77 | func test_ConfigCell_SetsLocation() { 78 | let location = Location(name: "Bar") 79 | cell.configCell(with: ToDoItem(title: "Foo", 80 | location: location)) 81 | 82 | XCTAssertEqual(cell.locationLabel.text, "Bar") 83 | } 84 | 85 | func test_Title_WhenItemIsChecked_IsStrokeThrough() { 86 | let location = Location(name: "Bar") 87 | let item = ToDoItem(title: "Foo", 88 | itemDescription: nil, 89 | timestamp: 0, 90 | location: location) 91 | 92 | 93 | cell.configCell(with: item, checked: true) 94 | 95 | 96 | let attributedString = NSAttributedString( 97 | string: "Foo", 98 | attributes: [NSAttributedStringKey.strikethroughStyle: 99 | NSUnderlineStyle.styleSingle.rawValue]) 100 | 101 | 102 | XCTAssertEqual(cell.titleLabel.attributedText, attributedString) 103 | XCTAssertNil(cell.locationLabel.text) 104 | XCTAssertNil(cell.dateLabel.text) 105 | } 106 | } 107 | 108 | extension ItemCellTests { 109 | class FakeDataSource: NSObject, UITableViewDataSource { 110 | 111 | 112 | func tableView(_ tableView: UITableView, 113 | numberOfRowsInSection section: Int) -> Int { 114 | 115 | 116 | return 1 117 | } 118 | 119 | 120 | 121 | func tableView(_ tableView: UITableView, 122 | cellForRowAt indexPath: IndexPath) 123 | -> UITableViewCell { 124 | 125 | 126 | return UITableViewCell() 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/ItemCellTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCellTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 27.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | @testable import ToDo 12 | 13 | class ItemCellTests: XCTestCase { 14 | 15 | var tableView: UITableView! 16 | let dataSource = FakeDataSource() 17 | var cell: ItemCell! 18 | 19 | override func setUp() { 20 | super.setUp() 21 | 22 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 23 | let controller = storyboard 24 | .instantiateViewController( 25 | withIdentifier: "ItemListViewController") 26 | as! ItemListViewController 27 | 28 | 29 | controller.loadViewIfNeeded() 30 | 31 | 32 | tableView = controller.tableView 33 | tableView?.dataSource = dataSource 34 | 35 | 36 | cell = tableView?.dequeueReusableCell( 37 | withIdentifier: "ItemCell", 38 | for: IndexPath(row: 0, section: 0)) as! ItemCell 39 | 40 | } 41 | 42 | override func tearDown() { 43 | // Put teardown code here. This method is called after the invocation of each test method in the class. 44 | super.tearDown() 45 | } 46 | 47 | func test_HasNameLabel() { 48 | XCTAssertTrue(cell.titleLabel.isDescendant(of: cell.contentView)) 49 | } 50 | 51 | func test_HasLocationLabel() { 52 | XCTAssertTrue(cell.locationLabel.isDescendant(of: cell.contentView)) 53 | } 54 | 55 | func test_HasDateLabel() { 56 | XCTAssertTrue(cell.dateLabel.isDescendant(of: cell.contentView)) 57 | } 58 | 59 | func test_ConfigCell_SetsTitle() { 60 | cell.configCell(with: ToDoItem(title: "Foo")) 61 | 62 | XCTAssertEqual(cell.titleLabel.text, "Foo") 63 | } 64 | 65 | func test_ConfigCell_SetsDate() { 66 | let dateFormatter = DateFormatter() 67 | dateFormatter.dateFormat = "MM/dd/yyyy" 68 | let date = dateFormatter.date(from: "08/27/2017") 69 | let timestamp = date?.timeIntervalSince1970 70 | 71 | cell.configCell(with: ToDoItem(title: "Foo", 72 | timestamp: timestamp)) 73 | 74 | XCTAssertEqual(cell.dateLabel.text, "08/27/2017") 75 | } 76 | 77 | func test_ConfigCell_SetsLocation() { 78 | let location = Location(name: "Bar") 79 | cell.configCell(with: ToDoItem(title: "Foo", 80 | location: location)) 81 | 82 | XCTAssertEqual(cell.locationLabel.text, "Bar") 83 | } 84 | 85 | func test_Title_WhenItemIsChecked_IsStrokeThrough() { 86 | let location = Location(name: "Bar") 87 | let item = ToDoItem(title: "Foo", 88 | itemDescription: nil, 89 | timestamp: 0, 90 | location: location) 91 | 92 | 93 | cell.configCell(with: item, checked: true) 94 | 95 | 96 | let attributedString = NSAttributedString( 97 | string: "Foo", 98 | attributes: [NSAttributedStringKey.strikethroughStyle: 99 | NSUnderlineStyle.styleSingle.rawValue]) 100 | 101 | 102 | XCTAssertEqual(cell.titleLabel.attributedText, attributedString) 103 | XCTAssertNil(cell.locationLabel.text) 104 | XCTAssertNil(cell.dateLabel.text) 105 | } 106 | } 107 | 108 | extension ItemCellTests { 109 | class FakeDataSource: NSObject, UITableViewDataSource { 110 | 111 | 112 | func tableView(_ tableView: UITableView, 113 | numberOfRowsInSection section: Int) -> Int { 114 | 115 | 116 | return 1 117 | } 118 | 119 | 120 | 121 | func tableView(_ tableView: UITableView, 122 | cellForRowAt indexPath: IndexPath) 123 | -> UITableViewCell { 124 | 125 | 126 | return UITableViewCell() 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Controller/ItemCellTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemCellTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 27.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | @testable import ToDo 12 | 13 | class ItemCellTests: XCTestCase { 14 | 15 | var tableView: UITableView! 16 | let dataSource = FakeDataSource() 17 | var cell: ItemCell! 18 | 19 | override func setUp() { 20 | super.setUp() 21 | 22 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 23 | let controller = storyboard 24 | .instantiateViewController( 25 | withIdentifier: "ItemListViewController") 26 | as! ItemListViewController 27 | 28 | 29 | controller.loadViewIfNeeded() 30 | 31 | 32 | tableView = controller.tableView 33 | tableView?.dataSource = dataSource 34 | 35 | 36 | cell = tableView?.dequeueReusableCell( 37 | withIdentifier: "ItemCell", 38 | for: IndexPath(row: 0, section: 0)) as! ItemCell 39 | 40 | } 41 | 42 | override func tearDown() { 43 | // Put teardown code here. This method is called after the invocation of each test method in the class. 44 | super.tearDown() 45 | } 46 | 47 | func test_HasNameLabel() { 48 | XCTAssertTrue(cell.titleLabel.isDescendant(of: cell.contentView)) 49 | } 50 | 51 | func test_HasLocationLabel() { 52 | XCTAssertTrue(cell.locationLabel.isDescendant(of: cell.contentView)) 53 | } 54 | 55 | func test_HasDateLabel() { 56 | XCTAssertTrue(cell.dateLabel.isDescendant(of: cell.contentView)) 57 | } 58 | 59 | func test_ConfigCell_SetsTitle() { 60 | cell.configCell(with: ToDoItem(title: "Foo")) 61 | 62 | XCTAssertEqual(cell.titleLabel.text, "Foo") 63 | } 64 | 65 | func test_ConfigCell_SetsDate() { 66 | let dateFormatter = DateFormatter() 67 | dateFormatter.dateFormat = "MM/dd/yyyy" 68 | let date = dateFormatter.date(from: "08/27/2017") 69 | let timestamp = date?.timeIntervalSince1970 70 | 71 | cell.configCell(with: ToDoItem(title: "Foo", 72 | timestamp: timestamp)) 73 | 74 | XCTAssertEqual(cell.dateLabel.text, "08/27/2017") 75 | } 76 | 77 | func test_ConfigCell_SetsLocation() { 78 | let location = Location(name: "Bar") 79 | cell.configCell(with: ToDoItem(title: "Foo", 80 | location: location)) 81 | 82 | XCTAssertEqual(cell.locationLabel.text, "Bar") 83 | } 84 | 85 | func test_Title_WhenItemIsChecked_IsStrokeThrough() { 86 | let location = Location(name: "Bar") 87 | let item = ToDoItem(title: "Foo", 88 | itemDescription: nil, 89 | timestamp: 0, 90 | location: location) 91 | 92 | 93 | cell.configCell(with: item, checked: true) 94 | 95 | 96 | let attributedString = NSAttributedString( 97 | string: "Foo", 98 | attributes: [NSAttributedStringKey.strikethroughStyle: 99 | NSUnderlineStyle.styleSingle.rawValue]) 100 | 101 | 102 | XCTAssertEqual(cell.titleLabel.attributedText, attributedString) 103 | XCTAssertNil(cell.locationLabel.text) 104 | XCTAssertNil(cell.dateLabel.text) 105 | } 106 | } 107 | 108 | extension ItemCellTests { 109 | class FakeDataSource: NSObject, UITableViewDataSource { 110 | 111 | 112 | func tableView(_ tableView: UITableView, 113 | numberOfRowsInSection section: Int) -> Int { 114 | 115 | 116 | return 1 117 | } 118 | 119 | 120 | 121 | func tableView(_ tableView: UITableView, 122 | cellForRowAt indexPath: IndexPath) 123 | -> UITableViewCell { 124 | 125 | 126 | return UITableViewCell() 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/InputViewControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputViewControllerTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 28.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class InputViewControllerTests: XCTestCase { 14 | 15 | var sut: InputViewController! 16 | var placemark: MockPlacemark! 17 | 18 | override func setUp() { 19 | super.setUp() 20 | 21 | let storyboard = UIStoryboard(name: "Main", 22 | bundle: nil) 23 | sut = storyboard 24 | .instantiateViewController( 25 | withIdentifier: "InputViewController") 26 | as! InputViewController 27 | 28 | 29 | sut.loadViewIfNeeded() 30 | 31 | } 32 | 33 | override func tearDown() { 34 | // Put teardown code here. This method is called after the invocation of each test method in the class. 35 | super.tearDown() 36 | } 37 | 38 | func test_HasTitleTextField() { 39 | let titleTextFieldIsSubView = 40 | sut.titleTextField?.isDescendant( 41 | of: sut.view) ?? false 42 | XCTAssertTrue(titleTextFieldIsSubView) 43 | } 44 | 45 | func test_Save_UsesGeocoderToGetCoordinateFromAddress() { 46 | let dateFormatter = DateFormatter() 47 | dateFormatter.dateFormat = "MM/dd/yyyy" 48 | 49 | 50 | let timestamp = 1456095600.0 51 | let date = Date(timeIntervalSince1970: timestamp) 52 | 53 | 54 | sut.titleTextField.text = "Foo" 55 | sut.dateTextField.text = dateFormatter.string(from: date) 56 | sut.locationTextField.text = "Bar" 57 | sut.addressTextField.text = "Infinite Loop 1, Cupertino" 58 | sut.descriptionTextField.text = "Baz" 59 | 60 | 61 | let mockGeocoder = MockGeocoder() 62 | sut.geocoder = mockGeocoder 63 | 64 | 65 | sut.itemManager = ItemManager() 66 | 67 | 68 | sut.save() 69 | 70 | 71 | placemark = MockPlacemark() 72 | let coordinate = CLLocationCoordinate2DMake(37.3316851, 73 | -122.0300674) 74 | placemark.mockCoordinate = coordinate 75 | mockGeocoder.completionHandler?([placemark], nil) 76 | 77 | 78 | let item = sut.itemManager?.item(at: 0) 79 | 80 | 81 | let testItem = ToDoItem(title: "Foo", 82 | itemDescription: "Baz", 83 | timestamp: timestamp, 84 | location: Location(name: "Bar", 85 | coordinate: coordinate)) 86 | 87 | 88 | XCTAssertEqual(item, testItem) 89 | } 90 | 91 | func test_SaveButtonHasSaveAction() { 92 | let saveButton: UIButton = sut.saveButton 93 | 94 | 95 | guard let actions = saveButton.actions( 96 | forTarget: sut, 97 | forControlEvent: .touchUpInside) else { 98 | XCTFail(); return 99 | } 100 | 101 | 102 | XCTAssertTrue(actions.contains("save")) 103 | } 104 | } 105 | 106 | extension InputViewControllerTests { 107 | class MockGeocoder: CLGeocoder { 108 | 109 | 110 | var completionHandler: CLGeocodeCompletionHandler? 111 | 112 | 113 | override func geocodeAddressString( 114 | _ addressString: String, 115 | completionHandler: @escaping CLGeocodeCompletionHandler) { 116 | 117 | 118 | self.completionHandler = completionHandler 119 | } 120 | } 121 | 122 | class MockPlacemark : CLPlacemark { 123 | 124 | 125 | var mockCoordinate: CLLocationCoordinate2D? 126 | 127 | 128 | override var location: CLLocation? { 129 | guard let coordinate = mockCoordinate else 130 | { return CLLocation() } 131 | 132 | 133 | return CLLocation(latitude: coordinate.latitude, 134 | longitude: coordinate.longitude) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDoTests/LocationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class LocationTests: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | override func tearDown() { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | super.tearDown() 23 | } 24 | 25 | func test_Init_SetsCoordinate() { 26 | 27 | let coordinate = 28 | CLLocationCoordinate2D(latitude: 1, 29 | longitude: 2) 30 | 31 | let location = 32 | Location(name: "", 33 | coordinate: coordinate) 34 | 35 | XCTAssertEqual( 36 | location.coordinate?.latitude, 37 | coordinate.latitude) 38 | XCTAssertEqual( 39 | location.coordinate?.longitude, 40 | coordinate.longitude) 41 | } 42 | 43 | func test_Init_SetsName() { 44 | 45 | let location = Location(name: "Foo") 46 | 47 | XCTAssertEqual(location.name, "Foo") 48 | } 49 | 50 | func test_EqualLocations_AreEqual() { 51 | let first = Location(name: "Foo") 52 | let second = Location(name: "Foo") 53 | 54 | XCTAssertEqual(first, second) 55 | } 56 | 57 | func test_Locations_WhenLatitudeDiffers_AreNotEqual() { 58 | 59 | 60 | assertLocationNotEqualWith(firstName: "Foo", 61 | firstLongLat: (1.0, 0.0), 62 | secondName: "Foo", 63 | secondLongLat: (0.0, 0.0)) 64 | } 65 | 66 | func test_Locations_WhenLongitudeDiffers_AreNotEqual() { 67 | 68 | assertLocationNotEqualWith(firstName: "Foo", 69 | firstLongLat: (0.0, 1.0), 70 | secondName: "Foo", 71 | secondLongLat: (0.0, 0.0)) 72 | } 73 | 74 | func test_Locations_WhenOnlyOneHasCoordinate_AreNotEqual() { 75 | 76 | 77 | assertLocationNotEqualWith(firstName: "Foo", 78 | firstLongLat: (0.0, 0.0), 79 | secondName: "Foo", 80 | secondLongLat: nil) 81 | } 82 | 83 | func test_Locations_WhenNamesDiffer_AreNotEqual() { 84 | 85 | 86 | assertLocationNotEqualWith(firstName: "Foo", 87 | firstLongLat: nil, 88 | secondName: "Bar", 89 | secondLongLat: nil) 90 | } 91 | 92 | func assertLocationNotEqualWith(firstName: String, 93 | firstLongLat: (Double, Double)?, 94 | secondName: String, 95 | secondLongLat: (Double, Double)?, 96 | line: UInt = #line) { 97 | 98 | var firstCoord: CLLocationCoordinate2D? = nil 99 | if let firstLongLat = firstLongLat { 100 | firstCoord = 101 | CLLocationCoordinate2D(latitude: firstLongLat.0, 102 | longitude: firstLongLat.1) 103 | } 104 | let firstLocation = 105 | Location(name: firstName, 106 | coordinate: firstCoord) 107 | 108 | 109 | var secondCoord: CLLocationCoordinate2D? = nil 110 | if let secondLongLat = secondLongLat { 111 | secondCoord = 112 | CLLocationCoordinate2D(latitude: secondLongLat.0, 113 | longitude: secondLongLat.1) 114 | } 115 | let secondLocation = 116 | Location(name: secondName, 117 | coordinate: secondCoord) 118 | 119 | 120 | XCTAssertNotEqual(firstLocation, secondLocation, line: line) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDoTests/Model/LocationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class LocationTests: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | override func tearDown() { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | super.tearDown() 23 | } 24 | 25 | func test_Init_SetsCoordinate() { 26 | 27 | let coordinate = 28 | CLLocationCoordinate2D(latitude: 1, 29 | longitude: 2) 30 | 31 | let location = 32 | Location(name: "", 33 | coordinate: coordinate) 34 | 35 | XCTAssertEqual( 36 | location.coordinate?.latitude, 37 | coordinate.latitude) 38 | XCTAssertEqual( 39 | location.coordinate?.longitude, 40 | coordinate.longitude) 41 | } 42 | 43 | func test_Init_SetsName() { 44 | 45 | let location = Location(name: "Foo") 46 | 47 | XCTAssertEqual(location.name, "Foo") 48 | } 49 | 50 | func test_EqualLocations_AreEqual() { 51 | let first = Location(name: "Foo") 52 | let second = Location(name: "Foo") 53 | 54 | XCTAssertEqual(first, second) 55 | } 56 | 57 | func test_Locations_WhenLatitudeDiffers_AreNotEqual() { 58 | 59 | 60 | assertLocationNotEqualWith(firstName: "Foo", 61 | firstLongLat: (1.0, 0.0), 62 | secondName: "Foo", 63 | secondLongLat: (0.0, 0.0)) 64 | } 65 | 66 | func test_Locations_WhenLongitudeDiffers_AreNotEqual() { 67 | 68 | assertLocationNotEqualWith(firstName: "Foo", 69 | firstLongLat: (0.0, 1.0), 70 | secondName: "Foo", 71 | secondLongLat: (0.0, 0.0)) 72 | } 73 | 74 | func test_Locations_WhenOnlyOneHasCoordinate_AreNotEqual() { 75 | 76 | 77 | assertLocationNotEqualWith(firstName: "Foo", 78 | firstLongLat: (0.0, 0.0), 79 | secondName: "Foo", 80 | secondLongLat: nil) 81 | } 82 | 83 | func test_Locations_WhenNamesDiffer_AreNotEqual() { 84 | 85 | 86 | assertLocationNotEqualWith(firstName: "Foo", 87 | firstLongLat: nil, 88 | secondName: "Bar", 89 | secondLongLat: nil) 90 | } 91 | 92 | func assertLocationNotEqualWith(firstName: String, 93 | firstLongLat: (Double, Double)?, 94 | secondName: String, 95 | secondLongLat: (Double, Double)?, 96 | line: UInt = #line) { 97 | 98 | var firstCoord: CLLocationCoordinate2D? = nil 99 | if let firstLongLat = firstLongLat { 100 | firstCoord = 101 | CLLocationCoordinate2D(latitude: firstLongLat.0, 102 | longitude: firstLongLat.1) 103 | } 104 | let firstLocation = 105 | Location(name: firstName, 106 | coordinate: firstCoord) 107 | 108 | 109 | var secondCoord: CLLocationCoordinate2D? = nil 110 | if let secondLongLat = secondLongLat { 111 | secondCoord = 112 | CLLocationCoordinate2D(latitude: secondLongLat.0, 113 | longitude: secondLongLat.1) 114 | } 115 | let secondLocation = 116 | Location(name: secondName, 117 | coordinate: secondCoord) 118 | 119 | 120 | XCTAssertNotEqual(firstLocation, secondLocation, line: line) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Chapter05/ToDo_chapter05/ToDoTests/Model/LocationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationTests.swift 3 | // ToDoTests 4 | // 5 | // Created by dasdom on 12.08.17. 6 | // Copyright © 2017 dasdom. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ToDo 11 | import CoreLocation 12 | 13 | class LocationTests: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | override func tearDown() { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | super.tearDown() 23 | } 24 | 25 | func test_Init_SetsCoordinate() { 26 | 27 | let coordinate = 28 | CLLocationCoordinate2D(latitude: 1, 29 | longitude: 2) 30 | 31 | let location = 32 | Location(name: "", 33 | coordinate: coordinate) 34 | 35 | XCTAssertEqual( 36 | location.coordinate?.latitude, 37 | coordinate.latitude) 38 | XCTAssertEqual( 39 | location.coordinate?.longitude, 40 | coordinate.longitude) 41 | } 42 | 43 | func test_Init_SetsName() { 44 | 45 | let location = Location(name: "Foo") 46 | 47 | XCTAssertEqual(location.name, "Foo") 48 | } 49 | 50 | func test_EqualLocations_AreEqual() { 51 | let first = Location(name: "Foo") 52 | let second = Location(name: "Foo") 53 | 54 | XCTAssertEqual(first, second) 55 | } 56 | 57 | func test_Locations_WhenLatitudeDiffers_AreNotEqual() { 58 | 59 | 60 | assertLocationNotEqualWith(firstName: "Foo", 61 | firstLongLat: (1.0, 0.0), 62 | secondName: "Foo", 63 | secondLongLat: (0.0, 0.0)) 64 | } 65 | 66 | func test_Locations_WhenLongitudeDiffers_AreNotEqual() { 67 | 68 | assertLocationNotEqualWith(firstName: "Foo", 69 | firstLongLat: (0.0, 1.0), 70 | secondName: "Foo", 71 | secondLongLat: (0.0, 0.0)) 72 | } 73 | 74 | func test_Locations_WhenOnlyOneHasCoordinate_AreNotEqual() { 75 | 76 | 77 | assertLocationNotEqualWith(firstName: "Foo", 78 | firstLongLat: (0.0, 0.0), 79 | secondName: "Foo", 80 | secondLongLat: nil) 81 | } 82 | 83 | func test_Locations_WhenNamesDiffer_AreNotEqual() { 84 | 85 | 86 | assertLocationNotEqualWith(firstName: "Foo", 87 | firstLongLat: nil, 88 | secondName: "Bar", 89 | secondLongLat: nil) 90 | } 91 | 92 | func assertLocationNotEqualWith(firstName: String, 93 | firstLongLat: (Double, Double)?, 94 | secondName: String, 95 | secondLongLat: (Double, Double)?, 96 | line: UInt = #line) { 97 | 98 | var firstCoord: CLLocationCoordinate2D? = nil 99 | if let firstLongLat = firstLongLat { 100 | firstCoord = 101 | CLLocationCoordinate2D(latitude: firstLongLat.0, 102 | longitude: firstLongLat.1) 103 | } 104 | let firstLocation = 105 | Location(name: firstName, 106 | coordinate: firstCoord) 107 | 108 | 109 | var secondCoord: CLLocationCoordinate2D? = nil 110 | if let secondLongLat = secondLongLat { 111 | secondCoord = 112 | CLLocationCoordinate2D(latitude: secondLongLat.0, 113 | longitude: secondLongLat.1) 114 | } 115 | let secondLocation = 116 | Location(name: secondName, 117 | coordinate: secondCoord) 118 | 119 | 120 | XCTAssertNotEqual(firstLocation, secondLocation, line: line) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Chapter03/ToDo/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/ToDo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Chapter04/ToDo_chapter04/ToDo.xcodeproj/xcuserdata/dom.xcuserdatad/xcschemes/ToDo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | --------------------------------------------------------------------------------