├── MVVM Contacts Starter
├── MVVM Contacts Starter
│ ├── ContactViewModel.swift
│ ├── ContactViewModelController.swift
│ ├── Contact.swift
│ ├── ContactsTableViewCell.swift
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── profile_placeholder.imageset
│ │ │ ├── profile_picture_badge@2x.png
│ │ │ ├── profile_picture_badge@3x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Contact+CoreDataProperties.swift
│ ├── AddContactViewController.swift
│ ├── ContactsViewController.swift
│ ├── Array+Insertion.swift
│ ├── MVVM_Contacts.xcdatamodeld
│ │ └── MVVM_Contacts.xcdatamodel
│ │ │ └── contents
│ ├── CoreDataStore.swift
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── ContactLocalDataManager.swift
│ └── AppDelegate.swift
└── MVVM Contacts Starter.xcodeproj
│ ├── project.xcworkspace
│ └── contents.xcworkspacedata
│ └── project.pbxproj
├── img
├── contacts.png
├── add-contact.png
├── mvc-pattern.png
├── mvvm-pattern.png
├── auth0-login-screen.png
└── viper-architecture.png
├── README.md
├── MVVM Contacts Final
├── MVVM Contacts Final
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── profile_placeholder.imageset
│ │ │ ├── profile_picture_badge@2x.png
│ │ │ ├── profile_picture_badge@3x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Contact+CoreDataProperties.swift
│ ├── ContactsTableViewCell.swift
│ ├── ContactViewModel.swift
│ ├── Contact.swift
│ ├── Array+Insertion.swift
│ ├── MVVM_Contacts.xcdatamodeld
│ │ └── MVVM_Contacts.xcdatamodel
│ │ │ └── contents
│ ├── CoreDataStore.swift
│ ├── Info.plist
│ ├── ContactViewModelController.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── AddContactViewController.swift
│ ├── ContactLocalDataManager.swift
│ ├── ContactsViewController.swift
│ └── AppDelegate.swift
└── MVVM Contacts Final.xcodeproj
│ ├── project.xcworkspace
│ └── contents.xcworkspacedata
│ └── project.pbxproj
├── VIPER Contacts Final
├── VIPER Contacts Final
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── profile_placeholder.imageset
│ │ │ ├── profile_picture_badge@2x.png
│ │ │ ├── profile_picture_badge@3x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Contact+CoreDataProperties.swift
│ ├── Array+Insertion.swift
│ ├── AddContact
│ │ ├── Interactor
│ │ │ └── AddContactInteractor.swift
│ │ ├── Presenter
│ │ │ └── AddContactPresenter.swift
│ │ ├── DataManager
│ │ │ └── Local
│ │ │ │ └── AddContactLocalDataManager.swift
│ │ ├── View
│ │ │ └── AddContactView.swift
│ │ ├── WireFrame
│ │ │ └── AddContactWireFrame.swift
│ │ └── Protocols
│ │ │ └── AddContactProtocols.swift
│ ├── ContactList
│ │ ├── Interactor
│ │ │ └── ContactListInteractor.swift
│ │ ├── DataManager
│ │ │ └── Local
│ │ │ │ └── ContactListLocalDataManager.swift
│ │ ├── Presenter
│ │ │ └── ContactListPresenter.swift
│ │ ├── WireFrame
│ │ │ └── ContactListWireFrame.swift
│ │ ├── View
│ │ │ └── ContactListView.swift
│ │ └── Protocols
│ │ │ └── ContactListProtocols.swift
│ ├── VIPER_Contacts_Starter.xcdatamodeld
│ │ └── VIPER_Contacts_Starter.xcdatamodel
│ │ │ └── contents
│ ├── Contact.swift
│ ├── CoreDataStore.swift
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ └── AppDelegate.swift
└── VIPER Contacts Final.xcodeproj
│ └── project.xcworkspace
│ └── contents.xcworkspacedata
├── VIPER Contacts Starter
├── VIPER Contacts Starter
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── profile_placeholder.imageset
│ │ │ ├── profile_picture_badge@2x.png
│ │ │ ├── profile_picture_badge@3x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Contact.swift
│ ├── Contact+CoreDataProperties.swift
│ ├── ContactList
│ │ ├── Interactor
│ │ │ └── ContactListInteractor.swift
│ │ ├── WireFrame
│ │ │ └── ContactListWireFrame.swift
│ │ ├── Presenter
│ │ │ └── ContactListPresenter.swift
│ │ ├── View
│ │ │ └── ContactListView.swift
│ │ ├── DataManager
│ │ │ └── Local
│ │ │ │ └── ContactListLocalDataManager.swift
│ │ └── Protocols
│ │ │ └── ContactListProtocols.swift
│ ├── Array+Insertion.swift
│ ├── AddContact
│ │ ├── Interactor
│ │ │ └── AddContactInteractor.swift
│ │ ├── Presenter
│ │ │ └── AddContactPresenter.swift
│ │ ├── DataManager
│ │ │ └── Local
│ │ │ │ └── AddContactLocalDataManager.swift
│ │ ├── View
│ │ │ └── AddContactView.swift
│ │ ├── WireFrame
│ │ │ └── AddContactWireFrame.swift
│ │ └── Protocols
│ │ │ └── AddContactProtocols.swift
│ ├── VIPER_Contacts_Starter.xcdatamodeld
│ │ └── VIPER_Contacts_Starter.xcdatamodel
│ │ │ └── contents
│ ├── CoreDataStore.swift
│ ├── Info.plist
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ └── AppDelegate.swift
└── VIPER Contacts Starter.xcodeproj
│ └── project.xcworkspace
│ └── contents.xcworkspacedata
├── LICENSE
└── .gitignore
/MVVM Contacts Starter/MVVM Contacts Starter/ContactViewModel.swift:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/ContactViewModelController.swift:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/img/contacts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/img/contacts.png
--------------------------------------------------------------------------------
/img/add-contact.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/img/add-contact.png
--------------------------------------------------------------------------------
/img/mvc-pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/img/mvc-pattern.png
--------------------------------------------------------------------------------
/img/mvvm-pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/img/mvvm-pattern.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mvvm_viper
2 | Blog post written as guest author for the auth0 blog. Contains example code
3 |
--------------------------------------------------------------------------------
/img/auth0-login-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/img/auth0-login-screen.png
--------------------------------------------------------------------------------
/img/viper-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/img/viper-architecture.png
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Contact.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | open class Contact: NSManagedObject {
4 | }
5 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/ContactsTableViewCell.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactsTableViewCell: UITableViewCell {
4 | }
5 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Contact.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | open class Contact: NSManagedObject {}
4 |
5 | public struct ContactViewModel {}
6 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Contact+CoreDataProperties.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | extension Contact {
4 |
5 | @NSManaged var firstName: String?
6 | @NSManaged var lastName: String?
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Contact+CoreDataProperties.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | extension Contact {
4 |
5 | @NSManaged var firstName: String?
6 | @NSManaged var lastName: String?
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Contact+CoreDataProperties.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | extension Contact {
4 |
5 | @NSManaged var firstName: String?
6 | @NSManaged var lastName: String?
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Contact+CoreDataProperties.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | extension Contact {
4 |
5 | @NSManaged var firstName: String?
6 | @NSManaged var lastName: String?
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/MVVM Contacts Starter/MVVM Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/MVVM Contacts Starter/MVVM Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@2x.png
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0-tutorials/mvvm_viper/HEAD/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/profile_placeholder.imageset/profile_picture_badge@3x.png
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/ContactList/Interactor/ContactListInteractor.swift:
--------------------------------------------------------------------------------
1 |
2 | class ContactListInteractor: ContactListInteractorInputProtocol {
3 | weak var presenter: ContactListInteractorOutputProtocol?
4 | var localDatamanager: ContactListLocalDataManagerInputProtocol?
5 |
6 | func retrieveContacts() {}
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/ContactsTableViewCell.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactsTableViewCell: UITableViewCell {
4 | var cellModel: ContactViewModel? {
5 | didSet {
6 | bindViewModel()
7 | }
8 | }
9 |
10 | func bindViewModel() {
11 | textLabel?.text = cellModel?.fullName
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/ContactList/WireFrame/ContactListWireFrame.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactListWireFrame: ContactListWireFrameProtocol {
4 |
5 | class func createContactListModule() -> UIViewController {
6 | return UIViewController()
7 | }
8 |
9 | func presentAddContactScreen(from view: ContactListViewProtocol) {}
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/ContactViewModel.swift:
--------------------------------------------------------------------------------
1 |
2 | public struct ContactViewModel {
3 | var fullName: String
4 | }
5 |
6 | public func <(lhs: ContactViewModel, rhs: ContactViewModel) -> Bool {
7 | return lhs.fullName.lowercased() < rhs.fullName.lowercased()
8 | }
9 |
10 | public func >(lhs: ContactViewModel, rhs: ContactViewModel) -> Bool {
11 | return lhs.fullName.lowercased() > rhs.fullName.lowercased()
12 | }
13 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Contact.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | open class Contact: NSManagedObject {
4 |
5 | var fullName: String {
6 | get {
7 | var name = ""
8 | if let firstName = firstName {
9 | name += firstName
10 | }
11 | if let lastName = lastName {
12 | name += " \(lastName)"
13 | }
14 | return name
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/AddContactViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class AddContactViewController: UIViewController {
4 |
5 | @IBOutlet var firstNameTextField: UITextField!
6 | @IBOutlet var lastNameTextField: UITextField!
7 |
8 | @IBAction func didClickOnDoneButton(_ sender: UIBarButtonItem) {
9 | }
10 |
11 | @IBAction func didClickOnCancelButton(_ sender: UIBarButtonItem) {
12 | dismiss(animated: true, completion: nil)
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/profile_placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "profile_picture_badge@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "profile_picture_badge@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/profile_placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "profile_picture_badge@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "profile_picture_badge@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Assets.xcassets/profile_placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "profile_picture_badge@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "profile_picture_badge@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/profile_placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "profile_picture_badge@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "profile_picture_badge@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/ContactsViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactsViewController: UIViewController {
4 |
5 | @IBOutlet var tableView: UITableView!
6 | }
7 |
8 | extension ContactsViewController: UITableViewDataSource {
9 |
10 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
11 | return UITableViewCell()
12 | }
13 |
14 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
15 | return 0
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Array+Insertion.swift:
--------------------------------------------------------------------------------
1 |
2 | extension Array {
3 | func insertionIndex(of elem: Element, isOrderedBefore: (Element, Element) -> Bool) -> Int {
4 | var lo = 0
5 | var hi = self.count - 1
6 | while lo <= hi {
7 | let mid = (lo + hi)/2
8 | if isOrderedBefore(self[mid], elem) {
9 | lo = mid + 1
10 | } else if isOrderedBefore(elem, self[mid]) {
11 | hi = mid - 1
12 | } else {
13 | return mid
14 | }
15 | }
16 | return lo
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Array+Insertion.swift:
--------------------------------------------------------------------------------
1 |
2 | extension Array {
3 | func insertionIndex(of elem: Element, isOrderedBefore: (Element, Element) -> Bool) -> Int {
4 | var lo = 0
5 | var hi = self.count - 1
6 | while lo <= hi {
7 | let mid = (lo + hi)/2
8 | if isOrderedBefore(self[mid], elem) {
9 | lo = mid + 1
10 | } else if isOrderedBefore(elem, self[mid]) {
11 | hi = mid - 1
12 | } else {
13 | return mid
14 | }
15 | }
16 | return lo
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Array+Insertion.swift:
--------------------------------------------------------------------------------
1 |
2 | extension Array {
3 | func insertionIndex(of elem: Element, isOrderedBefore: (Element, Element) -> Bool) -> Int {
4 | var lo = 0
5 | var hi = self.count - 1
6 | while lo <= hi {
7 | let mid = (lo + hi)/2
8 | if isOrderedBefore(self[mid], elem) {
9 | lo = mid + 1
10 | } else if isOrderedBefore(elem, self[mid]) {
11 | hi = mid - 1
12 | } else {
13 | return mid
14 | }
15 | }
16 | return lo
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Array+Insertion.swift:
--------------------------------------------------------------------------------
1 |
2 | extension Array {
3 | func insertionIndex(of elem: Element, isOrderedBefore: (Element, Element) -> Bool) -> Int {
4 | var lo = 0
5 | var hi = self.count - 1
6 | while lo <= hi {
7 | let mid = (lo + hi)/2
8 | if isOrderedBefore(self[mid], elem) {
9 | lo = mid + 1
10 | } else if isOrderedBefore(elem, self[mid]) {
11 | hi = mid - 1
12 | } else {
13 | return mid
14 | }
15 | }
16 | return lo
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AddContact/Interactor/AddContactInteractor.swift:
--------------------------------------------------------------------------------
1 |
2 | class AddContactInteractor: AddContactInteractorInputProtocol {
3 | weak var presenter: AddContactInteractorOutputProtocol?
4 | var APIDataManager: AddContactAPIDataManagerInputProtocol?
5 | var localDatamanager: AddContactLocalDataManagerInputProtocol?
6 |
7 | func saveNewContact(firstName: String, lastName: String) -> Contact? {
8 | do {
9 | let contact = try localDatamanager?.createContact(firstName: firstName, lastName: lastName)
10 | return contact
11 | } catch {
12 | return nil
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/ContactList/Interactor/ContactListInteractor.swift:
--------------------------------------------------------------------------------
1 |
2 | class ContactListInteractor: ContactListInteractorInputProtocol {
3 | weak var presenter: ContactListInteractorOutputProtocol?
4 | var localDatamanager: ContactListLocalDataManagerInputProtocol?
5 |
6 | func retrieveContacts() {
7 | do {
8 | if let contactList = try localDatamanager?.retrieveContactList() {
9 | presenter?.didRetrieveContacts(contactList)
10 | } else {
11 | presenter?.didRetrieveContacts([])
12 | }
13 |
14 | } catch {
15 | presenter?.didRetrieveContacts([])
16 | }
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/ContactList/Presenter/ContactListPresenter.swift:
--------------------------------------------------------------------------------
1 |
2 | class ContactListPresenter: ContactListPresenterProtocol {
3 | weak var view: ContactListViewProtocol?
4 | var interactor: ContactListInteractorInputProtocol?
5 | var wireFrame: ContactListWireFrameProtocol?
6 |
7 | func viewDidLoad() {}
8 |
9 | func addNewContact(from view: ContactListViewProtocol) {}
10 |
11 | }
12 |
13 | extension ContactListPresenter: ContactListInteractorOutputProtocol {
14 |
15 | func didRetrieveContacts(_ contacts: [Contact]) {}
16 |
17 | }
18 |
19 | extension ContactListPresenter: AddModuleDelegate {
20 |
21 | func didAddContact(_ contact: Contact) {}
22 |
23 | func didCancelAddContact() {}
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/MVVM_Contacts.xcdatamodeld/MVVM_Contacts.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/MVVM_Contacts.xcdatamodeld/MVVM_Contacts.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AddContact/Interactor/AddContactInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 |
7 | class AddContactInteractor: AddContactInteractorInputProtocol {
8 | weak var presenter: AddContactInteractorOutputProtocol?
9 | var APIDataManager: AddContactAPIDataManagerInputProtocol?
10 | var localDatamanager: AddContactLocalDataManagerInputProtocol?
11 |
12 | func saveNewContact(firstName: String, lastName: String) -> Contact? {
13 | do {
14 | let contact = try localDatamanager?.createContact(firstName: firstName, lastName: lastName)
15 | return contact
16 | } catch {
17 | return nil
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/VIPER_Contacts_Starter.xcdatamodeld/VIPER_Contacts_Starter.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/VIPER_Contacts_Starter.xcdatamodeld/VIPER_Contacts_Starter.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Contact.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | open class Contact: NSManagedObject {
4 |
5 | var fullName: String {
6 | get {
7 | var name = ""
8 | if let firstName = firstName {
9 | name += firstName
10 | }
11 | if let lastName = lastName {
12 | name += " " + lastName
13 | }
14 | return name
15 | }
16 | }
17 |
18 | }
19 |
20 | public struct ContactViewModel {
21 | var fullName = ""
22 | }
23 |
24 | public func <(lhs: ContactViewModel, rhs: ContactViewModel) -> Bool {
25 | return lhs.fullName.lowercased() < rhs.fullName.lowercased()
26 | }
27 |
28 | public func >(lhs: ContactViewModel, rhs: ContactViewModel) -> Bool {
29 | return lhs.fullName.lowercased() > rhs.fullName.lowercased()
30 | }
31 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/CoreDataStore.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 | import UIKit
3 |
4 | class CoreDataStore {
5 |
6 | static var persistentStoreCoordinator: NSPersistentStoreCoordinator? {
7 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
8 | return appDelegate.persistentStoreCoordinator
9 | }
10 | return nil
11 | }
12 |
13 | static var managedObjectModel: NSManagedObjectModel? {
14 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
15 | return appDelegate.managedObjectModel
16 | }
17 | return nil
18 | }
19 |
20 | static var managedObjectContext: NSManagedObjectContext? {
21 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
22 | return appDelegate.managedObjectContext
23 | }
24 | return nil
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/ContactList/View/ContactListView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactListView: UIViewController {
4 |
5 | @IBOutlet var tableView: UITableView!
6 | var presenter: ContactListPresenterProtocol?
7 |
8 | @IBAction func didClickOnAddButton(_ sender: UIBarButtonItem) {}
9 |
10 | }
11 |
12 | extension ContactListView: ContactListViewProtocol {
13 |
14 | func reloadInterface(with contacts: [ContactViewModel]) {}
15 |
16 | func didInsertContact(_ contact: ContactViewModel) {}
17 |
18 | }
19 |
20 | extension ContactListView: UITableViewDataSource {
21 |
22 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
23 | return UITableViewCell()
24 | }
25 |
26 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
27 | return 0
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/CoreDataStore.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 | import UIKit
3 |
4 | class CoreDataStore {
5 |
6 | static var persistentStoreCoordinator: NSPersistentStoreCoordinator? {
7 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
8 | return appDelegate.persistentStoreCoordinator
9 | }
10 | return nil
11 | }
12 |
13 | static var managedObjectModel: NSManagedObjectModel? {
14 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
15 | return appDelegate.managedObjectModel
16 | }
17 | return nil
18 | }
19 |
20 | static var managedObjectContext: NSManagedObjectContext? {
21 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
22 | return appDelegate.managedObjectContext
23 | }
24 | return nil
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/CoreDataStore.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 | import UIKit
3 |
4 | class CoreDataStore {
5 |
6 | static var persistentStoreCoordinator: NSPersistentStoreCoordinator? {
7 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
8 | return appDelegate.persistentStoreCoordinator
9 | }
10 | return nil
11 | }
12 |
13 | static var managedObjectModel: NSManagedObjectModel? {
14 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
15 | return appDelegate.managedObjectModel
16 | }
17 | return nil
18 | }
19 |
20 | static var managedObjectContext: NSManagedObjectContext? {
21 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
22 | return appDelegate.managedObjectContext
23 | }
24 | return nil
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/ContactList/DataManager/Local/ContactListLocalDataManager.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | class ContactListLocalDataManager: ContactListLocalDataManagerInputProtocol {
4 |
5 | func retrieveContactList() throws -> [Contact] {
6 | guard let managedOC = CoreDataStore.managedObjectContext else {
7 | throw PersistenceError.managedObjectContextNotFound
8 | }
9 |
10 | let request: NSFetchRequest = NSFetchRequest(entityName: String(describing: Contact.self))
11 | let caseInsensitiveSelector = #selector(NSString.caseInsensitiveCompare(_:))
12 | let sortDescriptorFirstName = NSSortDescriptor(key: "firstName", ascending: true, selector: caseInsensitiveSelector)
13 | let sortDescriptorLastName = NSSortDescriptor(key: "lastName", ascending: true, selector: caseInsensitiveSelector)
14 | request.sortDescriptors = [sortDescriptorFirstName, sortDescriptorLastName]
15 |
16 | return try managedOC.fetch(request)
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/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 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/ContactList/DataManager/Local/ContactListLocalDataManager.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | class ContactListLocalDataManager: ContactListLocalDataManagerInputProtocol {
4 |
5 | func retrieveContactList() throws -> [Contact] {
6 | guard let managedOC = CoreDataStore.managedObjectContext else {
7 | throw PersistenceError.managedObjectContextNotFound
8 | }
9 |
10 | let request: NSFetchRequest = NSFetchRequest(entityName: String(describing: Contact.self))
11 | let caseInsensitiveSelector = #selector(NSString.caseInsensitiveCompare(_:))
12 | let sortDescriptorFirstName = NSSortDescriptor(key: "firstName", ascending: true, selector: caseInsensitiveSelector)
13 | let sortDescriptorLastName = NSSortDescriptor(key: "lastName", ascending: true, selector: caseInsensitiveSelector)
14 | request.sortDescriptors = [sortDescriptorFirstName, sortDescriptorLastName]
15 |
16 | return try managedOC.fetch(request)
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AddContact/Presenter/AddContactPresenter.swift:
--------------------------------------------------------------------------------
1 |
2 | class AddContactPresenter: AddContactPresenterProtocol, AddContactInteractorOutputProtocol {
3 | weak var view: AddContactViewProtocol?
4 | var interactor: AddContactInteractorInputProtocol?
5 | var wireFrame: AddContactWireFrameProtocol?
6 | var delegate: AddModuleDelegate?
7 |
8 | func cancelAddContactAction() {
9 | if let view = view {
10 | wireFrame?.dismissAddContactInterface(from: view) { [weak delegate] in
11 | delegate?.didCancelAddContact()
12 | }
13 | }
14 | }
15 |
16 | func addNewContact(firstName: String, lastName: String) {
17 | let contact = interactor?.saveNewContact(firstName: firstName, lastName: lastName)
18 | if let view = view, let contact = contact {
19 | wireFrame?.dismissAddContactInterface(from: view) { [weak delegate] in
20 | delegate?.didAddContact(contact)
21 | }
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AddContact/DataManager/Local/AddContactLocalDataManager.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | enum PersistenceError: Error {
4 | case managedObjectContextNotFound
5 | case couldNotCreateObject
6 | case objectNotFound
7 | }
8 |
9 | class AddContactLocalDataManager: AddContactLocalDataManagerInputProtocol {
10 |
11 | func createContact(firstName: String, lastName: String) throws -> Contact {
12 | guard let managedOC = CoreDataStore.managedObjectContext else {
13 | throw PersistenceError.managedObjectContextNotFound
14 | }
15 |
16 | if let newContact = NSEntityDescription.entity(forEntityName: "Contact",
17 | in: managedOC) {
18 | let contact = Contact(entity: newContact, insertInto: managedOC)
19 | contact.firstName = firstName
20 | contact.lastName = lastName
21 | try managedOC.save()
22 | return contact
23 | }
24 | throw PersistenceError.couldNotCreateObject
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/CoreDataStore.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoreDataStore.swift
3 | // VIPER Contacts Final
4 | //
5 | // Created by Rafael Sacchi on 8/29/16.
6 | // Copyright © 2016 Rafael Sacchi. All rights reserved.
7 | //
8 |
9 | import CoreData
10 | import UIKit
11 |
12 | class CoreDataStore {
13 |
14 | static var persistentStoreCoordinator: NSPersistentStoreCoordinator? {
15 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
16 | return appDelegate.persistentStoreCoordinator
17 | }
18 | return nil
19 | }
20 |
21 | static var managedObjectModel: NSManagedObjectModel? {
22 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
23 | return appDelegate.managedObjectModel
24 | }
25 | return nil
26 | }
27 |
28 | static var managedObjectContext: NSManagedObjectContext? {
29 | if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
30 | return appDelegate.managedObjectContext
31 | }
32 | return nil
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 auth0-tutorials
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 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AddContact/Presenter/AddContactPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 |
7 | class AddContactPresenter: AddContactPresenterProtocol, AddContactInteractorOutputProtocol {
8 | weak var view: AddContactViewProtocol?
9 | var interactor: AddContactInteractorInputProtocol?
10 | var wireFrame: AddContactWireFrameProtocol?
11 | var delegate: AddModuleDelegate?
12 |
13 | func cancelAddContactAction() {
14 | if let view = view {
15 | wireFrame?.dismissAddContactInterface(from: view) { [weak delegate] in
16 | delegate?.didCancelAddContact()
17 | }
18 | }
19 | }
20 |
21 | func addNewContact(firstName: String, lastName: String) {
22 | let contact = interactor?.saveNewContact(firstName: firstName, lastName: lastName)
23 | if let view = view, let contact = contact {
24 | wireFrame?.dismissAddContactInterface(from: view) { [weak delegate] in
25 | delegate?.didAddContact(contact)
26 | }
27 | }
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/ContactList/Presenter/ContactListPresenter.swift:
--------------------------------------------------------------------------------
1 |
2 | class ContactListPresenter: ContactListPresenterProtocol {
3 | weak var view: ContactListViewProtocol?
4 | var interactor: ContactListInteractorInputProtocol?
5 | var wireFrame: ContactListWireFrameProtocol?
6 |
7 | func viewDidLoad() {
8 | interactor?.retrieveContacts()
9 | }
10 |
11 | func addNewContact(from view: ContactListViewProtocol) {
12 | wireFrame?.presentAddContactScreen(from: view)
13 | }
14 |
15 | }
16 |
17 | extension ContactListPresenter: ContactListInteractorOutputProtocol {
18 |
19 | func didRetrieveContacts(_ contacts: [Contact]) {
20 | view?.reloadInterface(with: contacts.map() {
21 | return ContactViewModel(fullName: $0.fullName)
22 | })
23 | }
24 |
25 | }
26 |
27 | extension ContactListPresenter: AddModuleDelegate {
28 |
29 | func didAddContact(_ contact: Contact) {
30 | let contactViewModel = ContactViewModel(fullName: contact.fullName)
31 | view?.didInsertContact(contactViewModel)
32 | }
33 |
34 | func didCancelAddContact() {}
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AddContact/DataManager/Local/AddContactLocalDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 | import CoreData
7 |
8 | enum PersistenceError: Error {
9 | case managedObjectContextNotFound
10 | case couldNotCreateObject
11 | case objectNotFound
12 | }
13 |
14 | class AddContactLocalDataManager: AddContactLocalDataManagerInputProtocol {
15 |
16 | func createContact(firstName: String, lastName: String) throws -> Contact {
17 | guard let managedOC = CoreDataStore.managedObjectContext else {
18 | throw PersistenceError.managedObjectContextNotFound
19 | }
20 |
21 | if let newContact = NSEntityDescription.entity(forEntityName: "Contact",
22 | in: managedOC) {
23 | let contact = Contact(entity: newContact, insertInto: managedOC)
24 | contact.firstName = firstName
25 | contact.lastName = lastName
26 | try managedOC.save()
27 | return contact
28 | }
29 | throw PersistenceError.couldNotCreateObject
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UIStatusBarStyle
32 | UIStatusBarStyleLightContent
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UIViewControllerBasedStatusBarAppearance
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AddContact/View/AddContactView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class AddContactView: UIViewController, AddContactViewProtocol {
4 | var presenter: AddContactPresenterProtocol?
5 |
6 | @IBOutlet var firstNameTextField: UITextField!
7 | @IBOutlet var lastNameTextField: UITextField!
8 |
9 | override func viewDidLoad() {
10 | super.viewDidLoad()
11 | firstNameTextField.becomeFirstResponder()
12 | }
13 |
14 | @IBAction func didClickOnDoneButton(_ sender: UIBarButtonItem) {
15 | guard
16 | let firstName = firstNameTextField.text,
17 | let lastName = lastNameTextField.text
18 | else {
19 | return
20 | }
21 |
22 | if firstName.isEmpty || lastName.isEmpty {
23 | showEmptyNameAlert()
24 | return
25 | }
26 | presenter?.addNewContact(firstName: firstName, lastName: lastName)
27 | }
28 |
29 | @IBAction func didClickOnCancelButton(_ sender: UIBarButtonItem) {
30 | presenter?.cancelAddContactAction()
31 | }
32 |
33 | fileprivate func showEmptyNameAlert() {
34 | let alertView = UIAlertController(title: "Error",
35 | message: "A contact must have first and last names",
36 | preferredStyle: .alert)
37 | alertView.addAction(UIAlertAction(title: "Ok", style: .destructive, handler: nil))
38 | present(alertView, animated: true, completion: nil)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarStyle
34 | UIStatusBarStyleLightContent
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UIViewControllerBasedStatusBarAppearance
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarStyle
34 | UIStatusBarStyleLightContent
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UIViewControllerBasedStatusBarAppearance
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarStyle
34 | UIStatusBarStyleLightContent
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UIViewControllerBasedStatusBarAppearance
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/ContactViewModelController.swift:
--------------------------------------------------------------------------------
1 |
2 | class ContactViewModelController {
3 |
4 | fileprivate var contactViewModelList: [ContactViewModel] = []
5 | fileprivate var dataManager = ContactLocalDataManager()
6 |
7 | var contactsCount: Int {
8 | return contactViewModelList.count
9 | }
10 |
11 | func retrieveContacts(_ success: (() -> Void)?, failure: (() -> Void)?) {
12 | do {
13 | let contacts = try dataManager.retrieveContactList()
14 | contactViewModelList = contacts.map() { ContactViewModel(fullName: $0.fullName) }
15 | success?()
16 | } catch {
17 | failure?()
18 | }
19 | }
20 |
21 | func viewModel(at index: Int) -> ContactViewModel {
22 | return contactViewModelList[index]
23 | }
24 |
25 | func createContact(firstName: String, lastName: String,
26 | success: ((ContactViewModel, Int) -> Void)?,
27 | failure: (() -> Void)?) {
28 | do {
29 | let contact = try dataManager.createContact(firstName: firstName, lastName: lastName)
30 | let contactViewModel = ContactViewModel(fullName: contact.fullName)
31 | let insertionIndex = contactViewModelList.insertionIndex(of: contactViewModel) { $0 < $1 }
32 | contactViewModelList.insert(contactViewModel, at: insertionIndex)
33 | success?(contactViewModel, insertionIndex)
34 | } catch {
35 | failure?()
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AddContact/View/AddContactView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 | import UIKit
7 |
8 | class AddContactView: UIViewController, AddContactViewProtocol {
9 | var presenter: AddContactPresenterProtocol?
10 |
11 | @IBOutlet var firstNameTextField: UITextField!
12 | @IBOutlet var lastNameTextField: UITextField!
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 | firstNameTextField.becomeFirstResponder()
17 | }
18 |
19 | @IBAction func didClickOnDoneButton(_ sender: UIBarButtonItem) {
20 | guard
21 | let firstName = firstNameTextField.text,
22 | let lastName = lastNameTextField.text
23 | else {
24 | return
25 | }
26 |
27 | if firstName.isEmpty || lastName.isEmpty {
28 | showEmptyNameAlert()
29 | return
30 | }
31 | presenter?.addNewContact(firstName: firstName, lastName: lastName)
32 | }
33 |
34 | @IBAction func didClickOnCancelButton(_ sender: UIBarButtonItem) {
35 | presenter?.cancelAddContactAction()
36 | }
37 |
38 | fileprivate func showEmptyNameAlert() {
39 | let alertView = UIAlertController(title: "Error",
40 | message: "A contact must have first and last names",
41 | preferredStyle: .alert)
42 | alertView.addAction(UIAlertAction(title: "Ok", style: .destructive, handler: nil))
43 | present(alertView, animated: true, completion: nil)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | # Xcode
3 | #
4 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
5 |
6 | ## Build generated
7 | build/
8 | DerivedData/
9 |
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata/
20 |
21 | ## Other
22 | *.moved-aside
23 | *.xcuserstate
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | .build/
40 |
41 | # CocoaPods
42 | #
43 | # We recommend against adding the Pods directory to your .gitignore. However
44 | # you should judge for yourself, the pros and cons are mentioned at:
45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
46 | #
47 | # Pods/
48 |
49 | # Carthage
50 | #
51 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
52 | # Carthage/Checkouts
53 |
54 | Carthage/Build
55 |
56 | # fastlane
57 | #
58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
59 | # screenshots whenever they are needed.
60 | # For more information about the recommended setup visit:
61 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
62 |
63 | fastlane/report.xml
64 | fastlane/Preview.html
65 | fastlane/screenshots
66 | fastlane/test_output
67 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AddContact/WireFrame/AddContactWireFrame.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class AddContactWireFrame: AddContactWireFrameProtocol {
4 |
5 | class func createAddContactModule(with delegate: AddModuleDelegate) -> UIViewController {
6 |
7 | let navController = mainStoryboard.instantiateViewController(withIdentifier: "AddContactsNavigationController")
8 | if let view = navController.childViewControllers.first as? AddContactViewProtocol {
9 | // Generating module components
10 | let presenter: AddContactPresenterProtocol & AddContactInteractorOutputProtocol = AddContactPresenter()
11 | let interactor: AddContactInteractorInputProtocol = AddContactInteractor()
12 | let localDataManager: AddContactLocalDataManagerInputProtocol = AddContactLocalDataManager()
13 | let wireFrame: AddContactWireFrameProtocol = AddContactWireFrame()
14 |
15 | // Connecting
16 | view.presenter = presenter
17 | presenter.view = view
18 | presenter.wireFrame = wireFrame
19 | presenter.interactor = interactor
20 | presenter.delegate = delegate
21 | interactor.presenter = presenter
22 | interactor.localDatamanager = localDataManager
23 |
24 | return navController
25 | }
26 | return UIViewController()
27 | }
28 |
29 | static var mainStoryboard: UIStoryboard {
30 | return UIStoryboard(name: "Main", bundle: Bundle.main)
31 | }
32 |
33 | func dismissAddContactInterface(from view: AddContactViewProtocol, completion: (() -> Void)?) {
34 | if let view = view as? UIViewController {
35 | view.dismiss(animated: true, completion: completion)
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/ContactList/WireFrame/ContactListWireFrame.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactListWireFrame: ContactListWireFrameProtocol {
4 |
5 | class func createContactListModule() -> UIViewController {
6 | let navController = mainStoryboard.instantiateViewController(withIdentifier: "ContactsNavigationController")
7 | if let view = navController.childViewControllers.first as? ContactListView {
8 | let presenter: ContactListPresenterProtocol & ContactListInteractorOutputProtocol = ContactListPresenter()
9 | let interactor: ContactListInteractorInputProtocol = ContactListInteractor()
10 | let localDataManager: ContactListLocalDataManagerInputProtocol = ContactListLocalDataManager()
11 | let wireFrame: ContactListWireFrameProtocol = ContactListWireFrame()
12 |
13 | view.presenter = presenter
14 | presenter.view = view
15 | presenter.wireFrame = wireFrame
16 | presenter.interactor = interactor
17 | interactor.presenter = presenter
18 | interactor.localDatamanager = localDataManager
19 |
20 | return navController
21 | }
22 | return UIViewController()
23 | }
24 |
25 | static var mainStoryboard: UIStoryboard {
26 | return UIStoryboard(name: "Main", bundle: Bundle.main)
27 | }
28 |
29 | func presentAddContactScreen(from view: ContactListViewProtocol) {
30 |
31 | guard let delegate = view.presenter as? AddModuleDelegate else {
32 | return
33 | }
34 |
35 | let addContactsView = AddContactWireFrame.createAddContactModule(with: delegate)
36 | if let sourceView = view as? UIViewController {
37 | sourceView.present(addContactsView, animated: true, completion: nil)
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/ContactLocalDataManager.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 |
3 | enum PersistenceError: Error {
4 | case managedObjectContextNotFound
5 | case couldNotCreateObject
6 | case objectNotFound
7 | }
8 |
9 | class ContactLocalDataManager {
10 |
11 | func createContact(firstName: String, lastName: String) throws -> Contact {
12 | guard let managedOC = CoreDataStore.managedObjectContext else {
13 | throw PersistenceError.managedObjectContextNotFound
14 | }
15 |
16 | if let newContact = NSEntityDescription.entity(forEntityName: String(describing: Contact.self),
17 | in: managedOC) {
18 | let contact = Contact(entity: newContact, insertInto: managedOC)
19 | contact.firstName = firstName
20 | contact.lastName = lastName
21 | try managedOC.save()
22 | return contact
23 | }
24 | throw PersistenceError.couldNotCreateObject
25 | }
26 |
27 | func retrieveContactList() throws -> [Contact] {
28 | guard let managedOC = CoreDataStore.managedObjectContext else {
29 | throw PersistenceError.managedObjectContextNotFound
30 | }
31 |
32 | let request: NSFetchRequest = NSFetchRequest(entityName: String(describing: Contact.self))
33 | let caseInsensitiveSelector = #selector(NSString.caseInsensitiveCompare(_:))
34 | let sortDescriptorFirstName = NSSortDescriptor(key: "firstName", ascending: true, selector: caseInsensitiveSelector)
35 | let sortDescriptorLastName = NSSortDescriptor(key: "lastName", ascending: true, selector: caseInsensitiveSelector)
36 | request.sortDescriptors = [sortDescriptorFirstName, sortDescriptorLastName]
37 |
38 | return try managedOC.fetch(request)
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/ContactList/View/ContactListView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactListView: UIViewController {
4 |
5 | @IBOutlet var tableView: UITableView!
6 | var presenter: ContactListPresenterProtocol?
7 | var contactList: [ContactViewModel] = []
8 |
9 | override func viewDidLoad() {
10 | super.viewDidLoad()
11 | presenter?.viewDidLoad()
12 |
13 | tableView.tableFooterView = UIView()
14 | }
15 |
16 | @IBAction func didClickOnAddButton(_ sender: UIBarButtonItem) {
17 | presenter?.addNewContact(from: self)
18 | }
19 |
20 | }
21 |
22 | extension ContactListView: ContactListViewProtocol {
23 |
24 | func reloadInterface(with contacts: [ContactViewModel]) {
25 | contactList = contacts
26 | tableView.reloadData()
27 | }
28 |
29 | func didInsertContact(_ contact: ContactViewModel) {
30 | let insertionIndex = contactList.insertionIndex(of: contact) { $0 < $1 }
31 | contactList.insert(contact, at: insertionIndex)
32 |
33 | let indexPath = IndexPath(row: insertionIndex, section: 0)
34 | tableView.beginUpdates()
35 | tableView.insertRows(at: [indexPath], with: .right)
36 | tableView.endUpdates()
37 | }
38 |
39 | }
40 |
41 | extension ContactListView: UITableViewDataSource {
42 |
43 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
44 | guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell") else {
45 | return UITableViewCell()
46 | }
47 | cell.textLabel?.text = contactList[(indexPath as NSIndexPath).row].fullName
48 | return cell
49 | }
50 |
51 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
52 | return contactList.count
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AddContact/WireFrame/AddContactWireFrame.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 | import UIKit
7 |
8 | class AddContactWireFrame: AddContactWireFrameProtocol {
9 |
10 | class func createAddContactModule(with delegate: AddModuleDelegate) -> UIViewController {
11 |
12 | let navController = mainStoryboard.instantiateViewController(withIdentifier: "AddContactsNavigationController")
13 | if let view = navController.childViewControllers.first as? AddContactViewProtocol {
14 | // Generating module components
15 | let presenter: AddContactPresenterProtocol & AddContactInteractorOutputProtocol = AddContactPresenter()
16 | let interactor: AddContactInteractorInputProtocol = AddContactInteractor()
17 | let localDataManager: AddContactLocalDataManagerInputProtocol = AddContactLocalDataManager()
18 | let wireFrame: AddContactWireFrameProtocol = AddContactWireFrame()
19 |
20 | // Connecting
21 | view.presenter = presenter
22 | presenter.view = view
23 | presenter.wireFrame = wireFrame
24 | presenter.interactor = interactor
25 | presenter.delegate = delegate
26 | interactor.presenter = presenter
27 | interactor.localDatamanager = localDataManager
28 |
29 | return navController
30 | }
31 | return UIViewController()
32 | }
33 |
34 | static var mainStoryboard: UIStoryboard {
35 | return UIStoryboard(name: "Main", bundle: Bundle.main)
36 | }
37 |
38 | func dismissAddContactInterface(from view: AddContactViewProtocol, completion: (() -> Void)?) {
39 | if let view = view as? UIViewController {
40 | view.dismiss(animated: true, completion: completion)
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/ContactList/Protocols/ContactListProtocols.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | protocol ContactListViewProtocol: class {
4 | var presenter: ContactListPresenterProtocol? { get set }
5 |
6 | // PRESENTER -> VIEW
7 | func didInsertContact(_ contact: ContactViewModel)
8 | func reloadInterface(with contacts: [ContactViewModel])
9 | }
10 |
11 | protocol ContactListWireFrameProtocol: class {
12 | static func createContactListModule() -> UIViewController
13 |
14 | // PRESENTER -> WIREFRAME
15 | func presentAddContactScreen(from view: ContactListViewProtocol)
16 | }
17 |
18 | protocol ContactListPresenterProtocol: class {
19 | var view: ContactListViewProtocol? { get set }
20 | var interactor: ContactListInteractorInputProtocol? { get set }
21 | var wireFrame: ContactListWireFrameProtocol? { get set }
22 |
23 | // VIEW -> PRESENTER
24 | func viewDidLoad()
25 | func addNewContact(from view: ContactListViewProtocol)
26 | }
27 |
28 | protocol ContactListInteractorOutputProtocol: class {
29 | // INTERACTOR -> PRESENTER
30 |
31 | func didRetrieveContacts(_ contacts: [Contact])
32 | }
33 |
34 | protocol ContactListInteractorInputProtocol: class {
35 | var presenter: ContactListInteractorOutputProtocol? { get set }
36 | var localDatamanager: ContactListLocalDataManagerInputProtocol? { get set }
37 | /**
38 | * Add here your methods for communication PRESENTER -> INTERACTOR
39 | */
40 | func retrieveContacts()
41 | }
42 |
43 | protocol ContactListDataManagerInputProtocol: class {
44 | /**
45 | * Add here your methods for communication INTERACTOR -> DATAMANAGER
46 | */
47 | }
48 |
49 | protocol ContactListLocalDataManagerInputProtocol: class {
50 | /**
51 | * Add here your methods for communication INTERACTOR -> LOCALDATAMANAGER
52 | */
53 | func retrieveContactList() throws -> [Contact]
54 | }
55 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/ContactList/Protocols/ContactListProtocols.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | protocol ContactListViewProtocol: class {
4 | var presenter: ContactListPresenterProtocol? { get set }
5 |
6 | // PRESENTER -> VIEW
7 | func didInsertContact(_ contact: ContactViewModel)
8 | func reloadInterface(with contacts: [ContactViewModel])
9 | }
10 |
11 | protocol ContactListWireFrameProtocol: class {
12 | static func createContactListModule() -> UIViewController
13 |
14 | // PRESENTER -> WIREFRAME
15 | func presentAddContactScreen(from view: ContactListViewProtocol)
16 | }
17 |
18 | protocol ContactListPresenterProtocol: class {
19 | var view: ContactListViewProtocol? { get set }
20 | var interactor: ContactListInteractorInputProtocol? { get set }
21 | var wireFrame: ContactListWireFrameProtocol? { get set }
22 |
23 | // VIEW -> PRESENTER
24 | func viewDidLoad()
25 | func addNewContact(from view: ContactListViewProtocol)
26 | }
27 |
28 | protocol ContactListInteractorOutputProtocol: class {
29 | // INTERACTOR -> PRESENTER
30 |
31 | func didRetrieveContacts(_ contacts: [Contact])
32 | }
33 |
34 | protocol ContactListInteractorInputProtocol: class {
35 | var presenter: ContactListInteractorOutputProtocol? { get set }
36 | var localDatamanager: ContactListLocalDataManagerInputProtocol? { get set }
37 | /**
38 | * Add here your methods for communication PRESENTER -> INTERACTOR
39 | */
40 | func retrieveContacts()
41 | }
42 |
43 | protocol ContactListDataManagerInputProtocol: class {
44 | /**
45 | * Add here your methods for communication INTERACTOR -> DATAMANAGER
46 | */
47 | }
48 |
49 | protocol ContactListLocalDataManagerInputProtocol: class {
50 | /**
51 | * Add here your methods for communication INTERACTOR -> LOCALDATAMANAGER
52 | */
53 | func retrieveContactList() throws -> [Contact]
54 | }
55 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/AddContactViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class AddContactViewController: UIViewController {
4 |
5 | @IBOutlet var firstNameTextField: UITextField!
6 | @IBOutlet var lastNameTextField: UITextField!
7 | var contactsViewModelController: ContactViewModelController?
8 | var didAddContact: ((ContactViewModel, Int) -> Void)?
9 |
10 | override func viewDidLoad() {
11 | super.viewDidLoad()
12 | firstNameTextField.becomeFirstResponder()
13 | }
14 |
15 | @IBAction func didClickOnDoneButton(_ sender: UIBarButtonItem) {
16 | guard let firstName = firstNameTextField.text,
17 | let lastName = lastNameTextField.text
18 | else {
19 | return
20 | }
21 |
22 | if firstName.isEmpty || lastName.isEmpty {
23 | showEmptyNameAlert()
24 | return
25 | }
26 |
27 | dismiss(animated: true) { [unowned self] in
28 | self.contactsViewModelController?.createContact(firstName: firstName, lastName: lastName,
29 | success: self.didAddContact, failure: nil)
30 | }
31 |
32 | }
33 |
34 | @IBAction func didClickOnCancelButton(_ sender: UIBarButtonItem) {
35 | dismiss(animated: true, completion: nil)
36 | }
37 |
38 | fileprivate func showEmptyNameAlert() {
39 | showMessage(title: "Error", message: "A contact must have first and last names")
40 | }
41 |
42 | fileprivate func showMessage(title: String, message: String) {
43 | let alertView = UIAlertController(title: title,
44 | message: message,
45 | preferredStyle: .alert)
46 | alertView.addAction(UIAlertAction(title: "Ok", style: .destructive, handler: nil))
47 | present(alertView, animated: true, completion: nil)
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/ContactLocalDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 | import CoreData
7 |
8 | enum PersistenceError: Error {
9 | case managedObjectContextNotFound
10 | case couldNotCreateObject
11 | case objectNotFound
12 | }
13 |
14 | class ContactLocalDataManager {
15 |
16 | func createContact(firstName: String, lastName: String) throws -> Contact {
17 | guard let managedOC = CoreDataStore.managedObjectContext else {
18 | throw PersistenceError.managedObjectContextNotFound
19 | }
20 |
21 | if let newContact = NSEntityDescription.entity(forEntityName: String(describing: Contact.self),
22 | in: managedOC) {
23 | let contact = Contact(entity: newContact, insertInto: managedOC)
24 | contact.firstName = firstName
25 | contact.lastName = lastName
26 | try managedOC.save()
27 | return contact
28 | }
29 | throw PersistenceError.couldNotCreateObject
30 | }
31 |
32 | func retrieveContactList() throws -> [Contact] {
33 | guard let managedOC = CoreDataStore.managedObjectContext else {
34 | throw PersistenceError.managedObjectContextNotFound
35 | }
36 |
37 | let request: NSFetchRequest = NSFetchRequest(entityName: String(describing: Contact.self))
38 | let caseInsensitiveSelector = #selector(NSString.caseInsensitiveCompare(_:))
39 | let sortDescriptorFirstName = NSSortDescriptor(key: "firstName", ascending: true, selector: caseInsensitiveSelector)
40 | let sortDescriptorLastName = NSSortDescriptor(key: "lastName", ascending: true, selector: caseInsensitiveSelector)
41 | request.sortDescriptors = [sortDescriptorFirstName, sortDescriptorLastName]
42 |
43 | return try managedOC.fetch(request)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/ContactsViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | class ContactsViewController: UIViewController {
4 |
5 | @IBOutlet var tableView: UITableView!
6 | let contactViewModelController = ContactViewModelController()
7 |
8 | override func viewDidLoad() {
9 | super.viewDidLoad()
10 | tableView.tableFooterView = UIView()
11 | contactViewModelController.retrieveContacts({ [unowned self] in
12 | self.tableView.reloadData()
13 | }, failure: nil)
14 | }
15 |
16 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
17 | let addContactNavigationController = segue.destination as? UINavigationController
18 | let addContactVC = addContactNavigationController?.viewControllers[0] as? AddContactViewController
19 |
20 | addContactVC?.contactsViewModelController = contactViewModelController
21 | addContactVC?.didAddContact = { [unowned self] (contactViewModel, index) in
22 | let indexPath = IndexPath(row: index, section: 0)
23 | self.tableView.beginUpdates()
24 | self.tableView.insertRows(at: [indexPath], with: .left)
25 | self.tableView.endUpdates()
26 | }
27 | }
28 |
29 | }
30 |
31 | extension ContactsViewController: UITableViewDataSource {
32 |
33 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
34 | let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell") as? ContactsTableViewCell
35 | guard let contactsCell = cell else {
36 | return UITableViewCell()
37 | }
38 |
39 | contactsCell.cellModel = contactViewModelController.viewModel(at: (indexPath as NSIndexPath).row)
40 | return contactsCell
41 | }
42 |
43 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
44 | return contactViewModelController.contactsCount
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AddContact/Protocols/AddContactProtocols.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import CoreData
3 |
4 | protocol AddModuleDelegate: class {
5 | func didAddContact(_ contact: Contact)
6 | func didCancelAddContact()
7 | }
8 |
9 | protocol AddContactViewProtocol: class {
10 | var presenter: AddContactPresenterProtocol? { get set }
11 | /**
12 | * Add here your methods for communication PRESENTER -> VIEW
13 | */
14 | }
15 |
16 | protocol AddContactWireFrameProtocol: class {
17 | static func createAddContactModule(with delegate: AddModuleDelegate) -> UIViewController
18 | /**
19 | * Add here your methods for communication PRESENTER -> WIREFRAME
20 | */
21 | func dismissAddContactInterface(from view: AddContactViewProtocol, completion: (() -> Void)?)
22 | }
23 |
24 | protocol AddContactPresenterProtocol: class {
25 | var view: AddContactViewProtocol? { get set }
26 | var interactor: AddContactInteractorInputProtocol? { get set }
27 | var wireFrame: AddContactWireFrameProtocol? { get set }
28 | var delegate: AddModuleDelegate? { get set }
29 |
30 | // VIEW -> PRESENTER
31 | func cancelAddContactAction()
32 | func addNewContact(firstName: String, lastName: String)
33 | }
34 |
35 | protocol AddContactInteractorOutputProtocol: class {
36 | /**
37 | * Add here your methods for communication INTERACTOR -> PRESENTER
38 | */
39 | }
40 |
41 | protocol AddContactInteractorInputProtocol: class {
42 | var presenter: AddContactInteractorOutputProtocol? { get set }
43 | var APIDataManager: AddContactAPIDataManagerInputProtocol? { get set }
44 | var localDatamanager: AddContactLocalDataManagerInputProtocol? { get set }
45 |
46 | // PRESENTER -> INTERACTOR
47 | func saveNewContact(firstName: String, lastName: String) -> Contact?
48 | }
49 |
50 | protocol AddContactDataManagerInputProtocol: class {
51 | /**
52 | * Add here your methods for communication INTERACTOR -> DATAMANAGER
53 | */
54 | }
55 |
56 | protocol AddContactAPIDataManagerInputProtocol: class {
57 | /**
58 | * Add here your methods for communication INTERACTOR -> APIDATAMANAGER
59 | */
60 | }
61 |
62 | protocol AddContactLocalDataManagerInputProtocol: class {
63 | /**
64 | * Add here your methods for communication INTERACTOR -> LOCALDATAMANAGER
65 | */
66 | func createContact(firstName: String, lastName: String) throws -> Contact
67 | }
68 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AddContact/Protocols/AddContactProtocols.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by AUTHOR
3 | // Copyright (c) YEAR AUTHOR. All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import CoreData
8 |
9 | protocol AddModuleDelegate: class {
10 | func didAddContact(_ contact: Contact)
11 | func didCancelAddContact()
12 | }
13 |
14 | protocol AddContactViewProtocol: class {
15 | var presenter: AddContactPresenterProtocol? { get set }
16 | /**
17 | * Add here your methods for communication PRESENTER -> VIEW
18 | */
19 | }
20 |
21 | protocol AddContactWireFrameProtocol: class {
22 | static func createAddContactModule(with delegate: AddModuleDelegate) -> UIViewController
23 | /**
24 | * Add here your methods for communication PRESENTER -> WIREFRAME
25 | */
26 | func dismissAddContactInterface(from view: AddContactViewProtocol, completion: (() -> Void)?)
27 | }
28 |
29 | protocol AddContactPresenterProtocol: class {
30 | var view: AddContactViewProtocol? { get set }
31 | var interactor: AddContactInteractorInputProtocol? { get set }
32 | var wireFrame: AddContactWireFrameProtocol? { get set }
33 | var delegate: AddModuleDelegate? { get set }
34 |
35 | // VIEW -> PRESENTER
36 | func cancelAddContactAction()
37 | func addNewContact(firstName: String, lastName: String)
38 | }
39 |
40 | protocol AddContactInteractorOutputProtocol: class {
41 | /**
42 | * Add here your methods for communication INTERACTOR -> PRESENTER
43 | */
44 | }
45 |
46 | protocol AddContactInteractorInputProtocol: class {
47 | var presenter: AddContactInteractorOutputProtocol? { get set }
48 | var APIDataManager: AddContactAPIDataManagerInputProtocol? { get set }
49 | var localDatamanager: AddContactLocalDataManagerInputProtocol? { get set }
50 |
51 | // PRESENTER -> INTERACTOR
52 | func saveNewContact(firstName: String, lastName: String) -> Contact?
53 | }
54 |
55 | protocol AddContactDataManagerInputProtocol: class {
56 | /**
57 | * Add here your methods for communication INTERACTOR -> DATAMANAGER
58 | */
59 | }
60 |
61 | protocol AddContactAPIDataManagerInputProtocol: class {
62 | /**
63 | * Add here your methods for communication INTERACTOR -> APIDATAMANAGER
64 | */
65 | }
66 |
67 | protocol AddContactLocalDataManagerInputProtocol: class {
68 | /**
69 | * Add here your methods for communication INTERACTOR -> LOCALDATAMANAGER
70 | */
71 | func createContact(firstName: String, lastName: String) throws -> Contact
72 | }
73 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import CoreData
3 |
4 | @UIApplicationMain
5 | class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 |
10 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
11 | // Override point for customization after application launch.
12 | return true
13 | }
14 |
15 | func applicationWillResignActive(_ application: UIApplication) {
16 | // 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.
17 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
18 | }
19 |
20 | func applicationDidEnterBackground(_ application: UIApplication) {
21 | // 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.
22 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
23 | }
24 |
25 | func applicationWillEnterForeground(_ application: UIApplication) {
26 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
27 | }
28 |
29 | func applicationDidBecomeActive(_ application: UIApplication) {
30 | // 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.
31 | }
32 |
33 | func applicationWillTerminate(_ application: UIApplication) {
34 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
35 | }
36 |
37 | // MARK: - Core Data stack
38 |
39 | lazy var applicationDocumentsDirectory: URL = {
40 | let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
41 | return urls[urls.count-1]
42 | }()
43 |
44 | lazy var managedObjectModel: NSManagedObjectModel = {
45 | // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
46 | let modelURL = Bundle.main.url(forResource: "MVVM_Contacts", withExtension: "momd")!
47 | return NSManagedObjectModel(contentsOf: modelURL)!
48 | }()
49 |
50 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
51 | // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
52 | // Create the coordinator and store
53 | let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
54 | let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
55 | var failureReason = "There was an error creating or loading the application's saved data."
56 | do {
57 | try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
58 | } catch {
59 | // Report any error we got.
60 | var dict = [String: AnyObject]()
61 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
62 | dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
63 |
64 | dict[NSUnderlyingErrorKey] = error as NSError
65 | let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
66 | // Replace this with code to handle the error appropriately.
67 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
68 | NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
69 | abort()
70 | }
71 |
72 | return coordinator
73 | }()
74 |
75 | lazy var managedObjectContext: NSManagedObjectContext = {
76 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
77 | let coordinator = self.persistentStoreCoordinator
78 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
79 | managedObjectContext.persistentStoreCoordinator = coordinator
80 | return managedObjectContext
81 | }()
82 |
83 | // MARK: - Core Data Saving support
84 |
85 | func saveContext () {
86 | if managedObjectContext.hasChanges {
87 | do {
88 | try managedObjectContext.save()
89 | } catch {
90 | // Replace this implementation with code to handle the error appropriately.
91 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
92 | let nserror = error as NSError
93 | NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
94 | abort()
95 | }
96 | }
97 | }
98 |
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import CoreData
3 |
4 | @UIApplicationMain
5 | class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 |
10 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
11 | // Override point for customization after application launch.
12 | return true
13 | }
14 |
15 | func applicationWillResignActive(_ application: UIApplication) {
16 | // 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.
17 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
18 | }
19 |
20 | func applicationDidEnterBackground(_ application: UIApplication) {
21 | // 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.
22 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
23 | }
24 |
25 | func applicationWillEnterForeground(_ application: UIApplication) {
26 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
27 | }
28 |
29 | func applicationDidBecomeActive(_ application: UIApplication) {
30 | // 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.
31 | }
32 |
33 | func applicationWillTerminate(_ application: UIApplication) {
34 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
35 | }
36 |
37 | // MARK: - Core Data stack
38 |
39 | lazy var applicationDocumentsDirectory: URL = {
40 | let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
41 | return urls[urls.count-1]
42 | }()
43 |
44 | lazy var managedObjectModel: NSManagedObjectModel = {
45 | // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
46 | let modelURL = Bundle.main.url(forResource: "MVVM_Contacts", withExtension: "momd")!
47 | return NSManagedObjectModel(contentsOf: modelURL)!
48 | }()
49 |
50 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
51 | // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
52 | // Create the coordinator and store
53 | let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
54 | let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
55 | var failureReason = "There was an error creating or loading the application's saved data."
56 | do {
57 | try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
58 | } catch {
59 | // Report any error we got.
60 | var dict = [String: AnyObject]()
61 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
62 | dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
63 |
64 | dict[NSUnderlyingErrorKey] = error as NSError
65 | let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
66 | // Replace this with code to handle the error appropriately.
67 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
68 | NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
69 | abort()
70 | }
71 |
72 | return coordinator
73 | }()
74 |
75 | lazy var managedObjectContext: NSManagedObjectContext = {
76 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
77 | let coordinator = self.persistentStoreCoordinator
78 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
79 | managedObjectContext.persistentStoreCoordinator = coordinator
80 | return managedObjectContext
81 | }()
82 |
83 | // MARK: - Core Data Saving support
84 |
85 | func saveContext () {
86 | if managedObjectContext.hasChanges {
87 | do {
88 | try managedObjectContext.save()
89 | } catch {
90 | // Replace this implementation with code to handle the error appropriately.
91 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
92 | let nserror = error as NSError
93 | NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
94 | abort()
95 | }
96 | }
97 | }
98 |
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import CoreData
3 |
4 | @UIApplicationMain
5 | class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
10 |
11 | let contactsList = ContactListWireFrame.createContactListModule()
12 |
13 | window = UIWindow(frame: UIScreen.main.bounds)
14 | window?.rootViewController = contactsList
15 | window?.makeKeyAndVisible()
16 |
17 | return true
18 | }
19 |
20 | func applicationWillResignActive(_ application: UIApplication) {
21 | // 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.
22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
23 | }
24 |
25 | func applicationDidEnterBackground(_ application: UIApplication) {
26 | // 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.
27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
28 | }
29 |
30 | func applicationWillEnterForeground(_ application: UIApplication) {
31 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
32 | }
33 |
34 | func applicationDidBecomeActive(_ application: UIApplication) {
35 | // 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.
36 | }
37 |
38 | func applicationWillTerminate(_ application: UIApplication) {
39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
40 | self.saveContext()
41 | }
42 |
43 | // MARK: - Core Data stack
44 |
45 | lazy var applicationDocumentsDirectory: URL = {
46 | let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
47 | return urls[urls.count-1]
48 | }()
49 |
50 | lazy var managedObjectModel: NSManagedObjectModel = {
51 | // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
52 | let modelURL = Bundle.main.url(forResource: "VIPER_Contacts_Starter", withExtension: "momd")!
53 | return NSManagedObjectModel(contentsOf: modelURL)!
54 | }()
55 |
56 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
57 | // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
58 | // Create the coordinator and store
59 | let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
60 | let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
61 | var failureReason = "There was an error creating or loading the application's saved data."
62 | do {
63 | try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
64 | } catch {
65 | // Report any error we got.
66 | var dict = [String: AnyObject]()
67 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
68 | dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
69 |
70 | dict[NSUnderlyingErrorKey] = error as NSError
71 | let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
72 | // Replace this with code to handle the error appropriately.
73 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
74 | NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
75 | abort()
76 | }
77 |
78 | return coordinator
79 | }()
80 |
81 | lazy var managedObjectContext: NSManagedObjectContext = {
82 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
83 | let coordinator = self.persistentStoreCoordinator
84 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
85 | managedObjectContext.persistentStoreCoordinator = coordinator
86 | return managedObjectContext
87 | }()
88 |
89 | // MARK: - Core Data Saving support
90 |
91 | func saveContext () {
92 | if managedObjectContext.hasChanges {
93 | do {
94 | try managedObjectContext.save()
95 | } catch {
96 | // Replace this implementation with code to handle the error appropriately.
97 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
98 | let nserror = error as NSError
99 | NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
100 | abort()
101 | }
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/VIPER Contacts Starter/VIPER Contacts Starter/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import CoreData
3 |
4 | @UIApplicationMain
5 | class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
10 |
11 | let contactsList = ContactListWireFrame.createContactListModule()
12 |
13 | window = UIWindow(frame: UIScreen.main.bounds)
14 | window?.rootViewController = contactsList
15 | window?.makeKeyAndVisible()
16 |
17 | return true
18 | }
19 |
20 | func applicationWillResignActive(_ application: UIApplication) {
21 | // 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.
22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
23 | }
24 |
25 | func applicationDidEnterBackground(_ application: UIApplication) {
26 | // 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.
27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
28 | }
29 |
30 | func applicationWillEnterForeground(_ application: UIApplication) {
31 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
32 | }
33 |
34 | func applicationDidBecomeActive(_ application: UIApplication) {
35 | // 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.
36 | }
37 |
38 | func applicationWillTerminate(_ application: UIApplication) {
39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
40 | self.saveContext()
41 | }
42 |
43 | // MARK: - Core Data stack
44 |
45 | lazy var applicationDocumentsDirectory: URL = {
46 | let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
47 | return urls[urls.count-1]
48 | }()
49 |
50 | lazy var managedObjectModel: NSManagedObjectModel = {
51 | // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
52 | let modelURL = Bundle.main.url(forResource: "VIPER_Contacts_Starter", withExtension: "momd")!
53 | return NSManagedObjectModel(contentsOf: modelURL)!
54 | }()
55 |
56 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
57 | // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
58 | // Create the coordinator and store
59 | let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
60 | let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
61 | var failureReason = "There was an error creating or loading the application's saved data."
62 | do {
63 | try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
64 | } catch {
65 | // Report any error we got.
66 | var dict = [String: AnyObject]()
67 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
68 | dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
69 |
70 | dict[NSUnderlyingErrorKey] = error as NSError
71 | let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
72 | // Replace this with code to handle the error appropriately.
73 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
74 | NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
75 | abort()
76 | }
77 |
78 | return coordinator
79 | }()
80 |
81 | lazy var managedObjectContext: NSManagedObjectContext = {
82 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
83 | let coordinator = self.persistentStoreCoordinator
84 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
85 | managedObjectContext.persistentStoreCoordinator = coordinator
86 | return managedObjectContext
87 | }()
88 |
89 | // MARK: - Core Data Saving support
90 |
91 | func saveContext () {
92 | if managedObjectContext.hasChanges {
93 | do {
94 | try managedObjectContext.save()
95 | } catch {
96 | // Replace this implementation with code to handle the error appropriately.
97 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
98 | let nserror = error as NSError
99 | NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
100 | abort()
101 | }
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8D24E8441D766F79006CB8CB /* ContactLocalDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24E8411D766F79006CB8CB /* ContactLocalDataManager.swift */; };
11 | 8D24E8461D766F79006CB8CB /* CoreDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24E8431D766F79006CB8CB /* CoreDataStore.swift */; };
12 | 8D24E84A1D766FDE006CB8CB /* Contact+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24E8491D766FDE006CB8CB /* Contact+CoreDataProperties.swift */; };
13 | 8D24E84E1D767228006CB8CB /* MVVM_Contacts.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 8D24E84C1D767228006CB8CB /* MVVM_Contacts.xcdatamodeld */; };
14 | 8D24E8521D7E50A5006CB8CB /* ContactViewModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24E8511D7E50A5006CB8CB /* ContactViewModelController.swift */; };
15 | 8D24E8541D7E5711006CB8CB /* ContactsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24E8531D7E5711006CB8CB /* ContactsTableViewCell.swift */; };
16 | 8D660A0F1D60AC1A00F3D26B /* AddContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D660A031D60AC1A00F3D26B /* AddContactViewController.swift */; };
17 | 8D660A111D60AC1A00F3D26B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D660A051D60AC1A00F3D26B /* AppDelegate.swift */; };
18 | 8D660A121D60AC1A00F3D26B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D660A061D60AC1A00F3D26B /* Assets.xcassets */; };
19 | 8D660A131D60AC1A00F3D26B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D660A071D60AC1A00F3D26B /* LaunchScreen.storyboard */; };
20 | 8D660A141D60AC1A00F3D26B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D660A091D60AC1A00F3D26B /* Main.storyboard */; };
21 | 8D660A151D60AC1A00F3D26B /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D660A0B1D60AC1A00F3D26B /* Contact.swift */; };
22 | 8D660A161D60AC1A00F3D26B /* ContactsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D660A0C1D60AC1A00F3D26B /* ContactsViewController.swift */; };
23 | 8D660A1C1D61158000F3D26B /* Array+Insertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D660A1B1D61158000F3D26B /* Array+Insertion.swift */; };
24 | 8DF7220F1D7FA20B007F6129 /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF7220E1D7FA20B007F6129 /* ContactViewModel.swift */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXFileReference section */
28 | 8D24E8411D766F79006CB8CB /* ContactLocalDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactLocalDataManager.swift; sourceTree = ""; };
29 | 8D24E8431D766F79006CB8CB /* CoreDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStore.swift; sourceTree = ""; };
30 | 8D24E8491D766FDE006CB8CB /* Contact+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Contact+CoreDataProperties.swift"; sourceTree = ""; };
31 | 8D24E84D1D767228006CB8CB /* MVVM_Contacts.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MVVM_Contacts.xcdatamodel; sourceTree = ""; };
32 | 8D24E8511D7E50A5006CB8CB /* ContactViewModelController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModelController.swift; sourceTree = ""; };
33 | 8D24E8531D7E5711006CB8CB /* ContactsTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsTableViewCell.swift; sourceTree = ""; };
34 | 8D660A031D60AC1A00F3D26B /* AddContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddContactViewController.swift; sourceTree = ""; };
35 | 8D660A051D60AC1A00F3D26B /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
36 | 8D660A061D60AC1A00F3D26B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
37 | 8D660A081D60AC1A00F3D26B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
38 | 8D660A0A1D60AC1A00F3D26B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
39 | 8D660A0B1D60AC1A00F3D26B /* Contact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = ""; };
40 | 8D660A0C1D60AC1A00F3D26B /* ContactsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsViewController.swift; sourceTree = ""; };
41 | 8D660A0E1D60AC1A00F3D26B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
42 | 8D660A1B1D61158000F3D26B /* Array+Insertion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Insertion.swift"; sourceTree = ""; };
43 | 8D676C7E1D5FBB3300F3B088 /* MVVM Contacts Final.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MVVM Contacts Final.app"; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 8DF7220E1D7FA20B007F6129 /* ContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 8D676C7B1D5FBB3200F3B088 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 8D24E84B1D766FE5006CB8CB /* Contact Model */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 8D24E8491D766FDE006CB8CB /* Contact+CoreDataProperties.swift */,
62 | 8D660A0B1D60AC1A00F3D26B /* Contact.swift */,
63 | );
64 | name = "Contact Model";
65 | sourceTree = "";
66 | };
67 | 8D660A021D60AC1A00F3D26B /* MVVM Contacts Final */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 8DF722261D804809007F6129 /* DataManager */,
71 | 8DF7220D1D7FA1F9007F6129 /* Contact ViewModel */,
72 | 8D24E84B1D766FE5006CB8CB /* Contact Model */,
73 | 8D660A1A1D60ADDB00F3D26B /* Contacts View */,
74 | 8D660A191D60ADD100F3D26B /* AddContact View */,
75 | 8D660A051D60AC1A00F3D26B /* AppDelegate.swift */,
76 | 8D660A061D60AC1A00F3D26B /* Assets.xcassets */,
77 | 8D660A071D60AC1A00F3D26B /* LaunchScreen.storyboard */,
78 | 8D660A091D60AC1A00F3D26B /* Main.storyboard */,
79 | 8D660A0E1D60AC1A00F3D26B /* Info.plist */,
80 | 8D660A1B1D61158000F3D26B /* Array+Insertion.swift */,
81 | );
82 | path = "MVVM Contacts Final";
83 | sourceTree = "";
84 | };
85 | 8D660A191D60ADD100F3D26B /* AddContact View */ = {
86 | isa = PBXGroup;
87 | children = (
88 | 8D660A031D60AC1A00F3D26B /* AddContactViewController.swift */,
89 | );
90 | name = "AddContact View";
91 | sourceTree = "";
92 | };
93 | 8D660A1A1D60ADDB00F3D26B /* Contacts View */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 8D660A0C1D60AC1A00F3D26B /* ContactsViewController.swift */,
97 | 8D24E8531D7E5711006CB8CB /* ContactsTableViewCell.swift */,
98 | );
99 | name = "Contacts View";
100 | sourceTree = "";
101 | };
102 | 8D676C751D5FBB3200F3B088 = {
103 | isa = PBXGroup;
104 | children = (
105 | 8D660A021D60AC1A00F3D26B /* MVVM Contacts Final */,
106 | 8D676C7F1D5FBB3300F3B088 /* Products */,
107 | );
108 | sourceTree = "";
109 | };
110 | 8D676C7F1D5FBB3300F3B088 /* Products */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 8D676C7E1D5FBB3300F3B088 /* MVVM Contacts Final.app */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 8DF7220D1D7FA1F9007F6129 /* Contact ViewModel */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 8D24E8511D7E50A5006CB8CB /* ContactViewModelController.swift */,
122 | 8DF7220E1D7FA20B007F6129 /* ContactViewModel.swift */,
123 | );
124 | name = "Contact ViewModel";
125 | sourceTree = "";
126 | };
127 | 8DF722261D804809007F6129 /* DataManager */ = {
128 | isa = PBXGroup;
129 | children = (
130 | 8D24E8411D766F79006CB8CB /* ContactLocalDataManager.swift */,
131 | 8D24E8431D766F79006CB8CB /* CoreDataStore.swift */,
132 | 8D24E84C1D767228006CB8CB /* MVVM_Contacts.xcdatamodeld */,
133 | );
134 | name = DataManager;
135 | sourceTree = "";
136 | };
137 | /* End PBXGroup section */
138 |
139 | /* Begin PBXNativeTarget section */
140 | 8D676C7D1D5FBB3200F3B088 /* MVVM Contacts Final */ = {
141 | isa = PBXNativeTarget;
142 | buildConfigurationList = 8D676C901D5FBB3300F3B088 /* Build configuration list for PBXNativeTarget "MVVM Contacts Final" */;
143 | buildPhases = (
144 | 8D676C7A1D5FBB3200F3B088 /* Sources */,
145 | 8D676C7B1D5FBB3200F3B088 /* Frameworks */,
146 | 8D676C7C1D5FBB3200F3B088 /* Resources */,
147 | );
148 | buildRules = (
149 | );
150 | dependencies = (
151 | );
152 | name = "MVVM Contacts Final";
153 | productName = "MVVM Contacts Starter";
154 | productReference = 8D676C7E1D5FBB3300F3B088 /* MVVM Contacts Final.app */;
155 | productType = "com.apple.product-type.application";
156 | };
157 | /* End PBXNativeTarget section */
158 |
159 | /* Begin PBXProject section */
160 | 8D676C761D5FBB3200F3B088 /* Project object */ = {
161 | isa = PBXProject;
162 | attributes = {
163 | LastSwiftUpdateCheck = 0730;
164 | LastUpgradeCheck = 0800;
165 | ORGANIZATIONNAME = "Rafael Sacchi";
166 | TargetAttributes = {
167 | 8D676C7D1D5FBB3200F3B088 = {
168 | CreatedOnToolsVersion = 7.3.1;
169 | DevelopmentTeam = JK33ZA2AUD;
170 | LastSwiftMigration = 0800;
171 | };
172 | };
173 | };
174 | buildConfigurationList = 8D676C791D5FBB3200F3B088 /* Build configuration list for PBXProject "MVVM Contacts Final" */;
175 | compatibilityVersion = "Xcode 3.2";
176 | developmentRegion = English;
177 | hasScannedForEncodings = 0;
178 | knownRegions = (
179 | en,
180 | Base,
181 | );
182 | mainGroup = 8D676C751D5FBB3200F3B088;
183 | productRefGroup = 8D676C7F1D5FBB3300F3B088 /* Products */;
184 | projectDirPath = "";
185 | projectRoot = "";
186 | targets = (
187 | 8D676C7D1D5FBB3200F3B088 /* MVVM Contacts Final */,
188 | );
189 | };
190 | /* End PBXProject section */
191 |
192 | /* Begin PBXResourcesBuildPhase section */
193 | 8D676C7C1D5FBB3200F3B088 /* Resources */ = {
194 | isa = PBXResourcesBuildPhase;
195 | buildActionMask = 2147483647;
196 | files = (
197 | 8D660A141D60AC1A00F3D26B /* Main.storyboard in Resources */,
198 | 8D660A121D60AC1A00F3D26B /* Assets.xcassets in Resources */,
199 | 8D660A131D60AC1A00F3D26B /* LaunchScreen.storyboard in Resources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXResourcesBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | 8D676C7A1D5FBB3200F3B088 /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | 8D24E8541D7E5711006CB8CB /* ContactsTableViewCell.swift in Sources */,
211 | 8DF7220F1D7FA20B007F6129 /* ContactViewModel.swift in Sources */,
212 | 8D24E8441D766F79006CB8CB /* ContactLocalDataManager.swift in Sources */,
213 | 8D660A1C1D61158000F3D26B /* Array+Insertion.swift in Sources */,
214 | 8D24E84E1D767228006CB8CB /* MVVM_Contacts.xcdatamodeld in Sources */,
215 | 8D24E8521D7E50A5006CB8CB /* ContactViewModelController.swift in Sources */,
216 | 8D660A151D60AC1A00F3D26B /* Contact.swift in Sources */,
217 | 8D24E8461D766F79006CB8CB /* CoreDataStore.swift in Sources */,
218 | 8D660A111D60AC1A00F3D26B /* AppDelegate.swift in Sources */,
219 | 8D24E84A1D766FDE006CB8CB /* Contact+CoreDataProperties.swift in Sources */,
220 | 8D660A0F1D60AC1A00F3D26B /* AddContactViewController.swift in Sources */,
221 | 8D660A161D60AC1A00F3D26B /* ContactsViewController.swift in Sources */,
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | };
225 | /* End PBXSourcesBuildPhase section */
226 |
227 | /* Begin PBXVariantGroup section */
228 | 8D660A071D60AC1A00F3D26B /* LaunchScreen.storyboard */ = {
229 | isa = PBXVariantGroup;
230 | children = (
231 | 8D660A081D60AC1A00F3D26B /* Base */,
232 | );
233 | name = LaunchScreen.storyboard;
234 | sourceTree = "";
235 | };
236 | 8D660A091D60AC1A00F3D26B /* Main.storyboard */ = {
237 | isa = PBXVariantGroup;
238 | children = (
239 | 8D660A0A1D60AC1A00F3D26B /* Base */,
240 | );
241 | name = Main.storyboard;
242 | sourceTree = "";
243 | };
244 | /* End PBXVariantGroup section */
245 |
246 | /* Begin XCBuildConfiguration section */
247 | 8D676C8E1D5FBB3300F3B088 /* Debug */ = {
248 | isa = XCBuildConfiguration;
249 | buildSettings = {
250 | ALWAYS_SEARCH_USER_PATHS = NO;
251 | CLANG_ANALYZER_NONNULL = YES;
252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
253 | CLANG_CXX_LIBRARY = "libc++";
254 | CLANG_ENABLE_MODULES = YES;
255 | CLANG_ENABLE_OBJC_ARC = YES;
256 | CLANG_WARN_BOOL_CONVERSION = YES;
257 | CLANG_WARN_CONSTANT_CONVERSION = YES;
258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
259 | CLANG_WARN_EMPTY_BODY = YES;
260 | CLANG_WARN_ENUM_CONVERSION = YES;
261 | CLANG_WARN_INFINITE_RECURSION = YES;
262 | CLANG_WARN_INT_CONVERSION = YES;
263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
264 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
265 | CLANG_WARN_UNREACHABLE_CODE = YES;
266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
267 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
268 | COPY_PHASE_STRIP = NO;
269 | DEBUG_INFORMATION_FORMAT = dwarf;
270 | ENABLE_STRICT_OBJC_MSGSEND = YES;
271 | ENABLE_TESTABILITY = YES;
272 | GCC_C_LANGUAGE_STANDARD = gnu99;
273 | GCC_DYNAMIC_NO_PIC = NO;
274 | GCC_NO_COMMON_BLOCKS = YES;
275 | GCC_OPTIMIZATION_LEVEL = 0;
276 | GCC_PREPROCESSOR_DEFINITIONS = (
277 | "DEBUG=1",
278 | "$(inherited)",
279 | );
280 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
281 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
282 | GCC_WARN_UNDECLARED_SELECTOR = YES;
283 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
284 | GCC_WARN_UNUSED_FUNCTION = YES;
285 | GCC_WARN_UNUSED_VARIABLE = YES;
286 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
287 | MTL_ENABLE_DEBUG_INFO = YES;
288 | ONLY_ACTIVE_ARCH = YES;
289 | SDKROOT = iphoneos;
290 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
291 | };
292 | name = Debug;
293 | };
294 | 8D676C8F1D5FBB3300F3B088 /* Release */ = {
295 | isa = XCBuildConfiguration;
296 | buildSettings = {
297 | ALWAYS_SEARCH_USER_PATHS = NO;
298 | CLANG_ANALYZER_NONNULL = YES;
299 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
300 | CLANG_CXX_LIBRARY = "libc++";
301 | CLANG_ENABLE_MODULES = YES;
302 | CLANG_ENABLE_OBJC_ARC = YES;
303 | CLANG_WARN_BOOL_CONVERSION = YES;
304 | CLANG_WARN_CONSTANT_CONVERSION = YES;
305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
306 | CLANG_WARN_EMPTY_BODY = YES;
307 | CLANG_WARN_ENUM_CONVERSION = YES;
308 | CLANG_WARN_INFINITE_RECURSION = YES;
309 | CLANG_WARN_INT_CONVERSION = YES;
310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
311 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
312 | CLANG_WARN_UNREACHABLE_CODE = YES;
313 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
314 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
315 | COPY_PHASE_STRIP = NO;
316 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
317 | ENABLE_NS_ASSERTIONS = NO;
318 | ENABLE_STRICT_OBJC_MSGSEND = YES;
319 | GCC_C_LANGUAGE_STANDARD = gnu99;
320 | GCC_NO_COMMON_BLOCKS = YES;
321 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
322 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
323 | GCC_WARN_UNDECLARED_SELECTOR = YES;
324 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
325 | GCC_WARN_UNUSED_FUNCTION = YES;
326 | GCC_WARN_UNUSED_VARIABLE = YES;
327 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
328 | MTL_ENABLE_DEBUG_INFO = NO;
329 | SDKROOT = iphoneos;
330 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
331 | VALIDATE_PRODUCT = YES;
332 | };
333 | name = Release;
334 | };
335 | 8D676C911D5FBB3300F3B088 /* Debug */ = {
336 | isa = XCBuildConfiguration;
337 | buildSettings = {
338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
339 | DEVELOPMENT_TEAM = JK33ZA2AUD;
340 | INFOPLIST_FILE = "$(SRCROOT)/MVVM Contacts Final/Info.plist";
341 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
342 | PRODUCT_BUNDLE_IDENTIFIER = "com.rsacchi.MVVM-Contacts-Final";
343 | PRODUCT_NAME = "MVVM Contacts Final";
344 | SWIFT_VERSION = 3.0;
345 | };
346 | name = Debug;
347 | };
348 | 8D676C921D5FBB3300F3B088 /* Release */ = {
349 | isa = XCBuildConfiguration;
350 | buildSettings = {
351 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
352 | DEVELOPMENT_TEAM = JK33ZA2AUD;
353 | INFOPLIST_FILE = "$(SRCROOT)/MVVM Contacts Final/Info.plist";
354 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
355 | PRODUCT_BUNDLE_IDENTIFIER = "com.rsacchi.MVVM-Contacts-Final";
356 | PRODUCT_NAME = "MVVM Contacts Final";
357 | SWIFT_VERSION = 3.0;
358 | };
359 | name = Release;
360 | };
361 | /* End XCBuildConfiguration section */
362 |
363 | /* Begin XCConfigurationList section */
364 | 8D676C791D5FBB3200F3B088 /* Build configuration list for PBXProject "MVVM Contacts Final" */ = {
365 | isa = XCConfigurationList;
366 | buildConfigurations = (
367 | 8D676C8E1D5FBB3300F3B088 /* Debug */,
368 | 8D676C8F1D5FBB3300F3B088 /* Release */,
369 | );
370 | defaultConfigurationIsVisible = 0;
371 | defaultConfigurationName = Release;
372 | };
373 | 8D676C901D5FBB3300F3B088 /* Build configuration list for PBXNativeTarget "MVVM Contacts Final" */ = {
374 | isa = XCConfigurationList;
375 | buildConfigurations = (
376 | 8D676C911D5FBB3300F3B088 /* Debug */,
377 | 8D676C921D5FBB3300F3B088 /* Release */,
378 | );
379 | defaultConfigurationIsVisible = 0;
380 | defaultConfigurationName = Release;
381 | };
382 | /* End XCConfigurationList section */
383 |
384 | /* Begin XCVersionGroup section */
385 | 8D24E84C1D767228006CB8CB /* MVVM_Contacts.xcdatamodeld */ = {
386 | isa = XCVersionGroup;
387 | children = (
388 | 8D24E84D1D767228006CB8CB /* MVVM_Contacts.xcdatamodel */,
389 | );
390 | currentVersion = 8D24E84D1D767228006CB8CB /* MVVM_Contacts.xcdatamodel */;
391 | path = MVVM_Contacts.xcdatamodeld;
392 | sourceTree = "";
393 | versionGroupType = wrapper.xcdatamodel;
394 | };
395 | /* End XCVersionGroup section */
396 | };
397 | rootObject = 8D676C761D5FBB3200F3B088 /* Project object */;
398 | }
399 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8D6609E11D5FCCE900F3D26B /* AddContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6609DB1D5FCCE900F3D26B /* AddContactViewController.swift */; };
11 | 8D660A1E1D61566B00F3D26B /* Array+Insertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D660A1D1D61566B00F3D26B /* Array+Insertion.swift */; };
12 | 8D676C821D5FBB3300F3B088 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D676C811D5FBB3300F3B088 /* AppDelegate.swift */; };
13 | 8D676C841D5FBB3300F3B088 /* ContactsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D676C831D5FBB3300F3B088 /* ContactsViewController.swift */; };
14 | 8D676C871D5FBB3300F3B088 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D676C851D5FBB3300F3B088 /* Main.storyboard */; };
15 | 8D676C891D5FBB3300F3B088 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D676C881D5FBB3300F3B088 /* Assets.xcassets */; };
16 | 8D676C8C1D5FBB3300F3B088 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D676C8A1D5FBB3300F3B088 /* LaunchScreen.storyboard */; };
17 | 8DF722141D804075007F6129 /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF722121D804075007F6129 /* ContactViewModel.swift */; };
18 | 8DF722161D804091007F6129 /* ContactViewModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF722151D804091007F6129 /* ContactViewModelController.swift */; };
19 | 8DF722181D80409B007F6129 /* CoreDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF722171D80409B007F6129 /* CoreDataStore.swift */; };
20 | 8DF7221C1D8040BC007F6129 /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF7221A1D8040BC007F6129 /* Contact.swift */; };
21 | 8DF7221D1D8040BC007F6129 /* Contact+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF7221B1D8040BC007F6129 /* Contact+CoreDataProperties.swift */; };
22 | 8DF722201D8040D9007F6129 /* ContactLocalDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF7221F1D8040D9007F6129 /* ContactLocalDataManager.swift */; };
23 | 8DF722221D8040EF007F6129 /* ContactsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF722211D8040EF007F6129 /* ContactsTableViewCell.swift */; };
24 | 8DF722251D804116007F6129 /* MVVM_Contacts.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 8DF722231D804116007F6129 /* MVVM_Contacts.xcdatamodeld */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXFileReference section */
28 | 8D6609DB1D5FCCE900F3D26B /* AddContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddContactViewController.swift; sourceTree = ""; };
29 | 8D660A1D1D61566B00F3D26B /* Array+Insertion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Insertion.swift"; sourceTree = ""; };
30 | 8D676C7E1D5FBB3300F3B088 /* MVVM Contacts Starter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MVVM Contacts Starter.app"; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 8D676C811D5FBB3300F3B088 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
32 | 8D676C831D5FBB3300F3B088 /* ContactsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsViewController.swift; sourceTree = ""; };
33 | 8D676C861D5FBB3300F3B088 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
34 | 8D676C881D5FBB3300F3B088 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
35 | 8D676C8B1D5FBB3300F3B088 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
36 | 8D676C8D1D5FBB3300F3B088 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
37 | 8DF722121D804075007F6129 /* ContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = ""; };
38 | 8DF722151D804091007F6129 /* ContactViewModelController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModelController.swift; sourceTree = ""; };
39 | 8DF722171D80409B007F6129 /* CoreDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStore.swift; sourceTree = ""; };
40 | 8DF7221A1D8040BC007F6129 /* Contact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = ""; };
41 | 8DF7221B1D8040BC007F6129 /* Contact+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Contact+CoreDataProperties.swift"; sourceTree = ""; };
42 | 8DF7221F1D8040D9007F6129 /* ContactLocalDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactLocalDataManager.swift; sourceTree = ""; };
43 | 8DF722211D8040EF007F6129 /* ContactsTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsTableViewCell.swift; sourceTree = ""; };
44 | 8DF722241D804116007F6129 /* MVVM_Contacts.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MVVM_Contacts.xcdatamodel; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 8D676C7B1D5FBB3200F3B088 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 8D6609D71D5FCCBD00F3D26B /* ContactList View */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 8DF722211D8040EF007F6129 /* ContactsTableViewCell.swift */,
62 | 8D676C831D5FBB3300F3B088 /* ContactsViewController.swift */,
63 | );
64 | name = "ContactList View";
65 | sourceTree = "";
66 | };
67 | 8D6609D81D5FCCC400F3D26B /* AddContact View */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 8D6609DB1D5FCCE900F3D26B /* AddContactViewController.swift */,
71 | );
72 | name = "AddContact View";
73 | sourceTree = "";
74 | };
75 | 8D676C751D5FBB3200F3B088 = {
76 | isa = PBXGroup;
77 | children = (
78 | 8D676C801D5FBB3300F3B088 /* MVVM Contacts Starter */,
79 | 8D676C7F1D5FBB3300F3B088 /* Products */,
80 | );
81 | sourceTree = "";
82 | };
83 | 8D676C7F1D5FBB3300F3B088 /* Products */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 8D676C7E1D5FBB3300F3B088 /* MVVM Contacts Starter.app */,
87 | );
88 | name = Products;
89 | sourceTree = "";
90 | };
91 | 8D676C801D5FBB3300F3B088 /* MVVM Contacts Starter */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 8DF7221E1D8040CD007F6129 /* Data Manager */,
95 | 8DF722191D8040A1007F6129 /* Contact Model */,
96 | 8DF722101D80405D007F6129 /* Contact ViewModel */,
97 | 8D6609D81D5FCCC400F3D26B /* AddContact View */,
98 | 8D6609D71D5FCCBD00F3D26B /* ContactList View */,
99 | 8D660A1D1D61566B00F3D26B /* Array+Insertion.swift */,
100 | 8D676C811D5FBB3300F3B088 /* AppDelegate.swift */,
101 | 8D676C851D5FBB3300F3B088 /* Main.storyboard */,
102 | 8D676C881D5FBB3300F3B088 /* Assets.xcassets */,
103 | 8D676C8A1D5FBB3300F3B088 /* LaunchScreen.storyboard */,
104 | 8D676C8D1D5FBB3300F3B088 /* Info.plist */,
105 | );
106 | path = "MVVM Contacts Starter";
107 | sourceTree = "";
108 | };
109 | 8DF722101D80405D007F6129 /* Contact ViewModel */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 8DF722151D804091007F6129 /* ContactViewModelController.swift */,
113 | 8DF722121D804075007F6129 /* ContactViewModel.swift */,
114 | );
115 | name = "Contact ViewModel";
116 | sourceTree = "";
117 | };
118 | 8DF722191D8040A1007F6129 /* Contact Model */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 8DF7221A1D8040BC007F6129 /* Contact.swift */,
122 | 8DF7221B1D8040BC007F6129 /* Contact+CoreDataProperties.swift */,
123 | );
124 | name = "Contact Model";
125 | sourceTree = "";
126 | };
127 | 8DF7221E1D8040CD007F6129 /* Data Manager */ = {
128 | isa = PBXGroup;
129 | children = (
130 | 8DF722231D804116007F6129 /* MVVM_Contacts.xcdatamodeld */,
131 | 8DF7221F1D8040D9007F6129 /* ContactLocalDataManager.swift */,
132 | 8DF722171D80409B007F6129 /* CoreDataStore.swift */,
133 | );
134 | name = "Data Manager";
135 | sourceTree = "";
136 | };
137 | /* End PBXGroup section */
138 |
139 | /* Begin PBXNativeTarget section */
140 | 8D676C7D1D5FBB3200F3B088 /* MVVM Contacts Starter */ = {
141 | isa = PBXNativeTarget;
142 | buildConfigurationList = 8D676C901D5FBB3300F3B088 /* Build configuration list for PBXNativeTarget "MVVM Contacts Starter" */;
143 | buildPhases = (
144 | 8D676C7A1D5FBB3200F3B088 /* Sources */,
145 | 8D676C7B1D5FBB3200F3B088 /* Frameworks */,
146 | 8D676C7C1D5FBB3200F3B088 /* Resources */,
147 | );
148 | buildRules = (
149 | );
150 | dependencies = (
151 | );
152 | name = "MVVM Contacts Starter";
153 | productName = "MVVM Contacts Starter";
154 | productReference = 8D676C7E1D5FBB3300F3B088 /* MVVM Contacts Starter.app */;
155 | productType = "com.apple.product-type.application";
156 | };
157 | /* End PBXNativeTarget section */
158 |
159 | /* Begin PBXProject section */
160 | 8D676C761D5FBB3200F3B088 /* Project object */ = {
161 | isa = PBXProject;
162 | attributes = {
163 | LastSwiftUpdateCheck = 0730;
164 | LastUpgradeCheck = 0800;
165 | ORGANIZATIONNAME = "Rafael Sacchi";
166 | TargetAttributes = {
167 | 8D676C7D1D5FBB3200F3B088 = {
168 | CreatedOnToolsVersion = 7.3.1;
169 | DevelopmentTeam = JK33ZA2AUD;
170 | LastSwiftMigration = 0800;
171 | };
172 | };
173 | };
174 | buildConfigurationList = 8D676C791D5FBB3200F3B088 /* Build configuration list for PBXProject "MVVM Contacts Starter" */;
175 | compatibilityVersion = "Xcode 3.2";
176 | developmentRegion = English;
177 | hasScannedForEncodings = 0;
178 | knownRegions = (
179 | en,
180 | Base,
181 | );
182 | mainGroup = 8D676C751D5FBB3200F3B088;
183 | productRefGroup = 8D676C7F1D5FBB3300F3B088 /* Products */;
184 | projectDirPath = "";
185 | projectRoot = "";
186 | targets = (
187 | 8D676C7D1D5FBB3200F3B088 /* MVVM Contacts Starter */,
188 | );
189 | };
190 | /* End PBXProject section */
191 |
192 | /* Begin PBXResourcesBuildPhase section */
193 | 8D676C7C1D5FBB3200F3B088 /* Resources */ = {
194 | isa = PBXResourcesBuildPhase;
195 | buildActionMask = 2147483647;
196 | files = (
197 | 8D676C8C1D5FBB3300F3B088 /* LaunchScreen.storyboard in Resources */,
198 | 8D676C891D5FBB3300F3B088 /* Assets.xcassets in Resources */,
199 | 8D676C871D5FBB3300F3B088 /* Main.storyboard in Resources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXResourcesBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | 8D676C7A1D5FBB3200F3B088 /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | 8DF722221D8040EF007F6129 /* ContactsTableViewCell.swift in Sources */,
211 | 8D660A1E1D61566B00F3D26B /* Array+Insertion.swift in Sources */,
212 | 8DF722251D804116007F6129 /* MVVM_Contacts.xcdatamodeld in Sources */,
213 | 8D6609E11D5FCCE900F3D26B /* AddContactViewController.swift in Sources */,
214 | 8DF722161D804091007F6129 /* ContactViewModelController.swift in Sources */,
215 | 8DF7221D1D8040BC007F6129 /* Contact+CoreDataProperties.swift in Sources */,
216 | 8DF722201D8040D9007F6129 /* ContactLocalDataManager.swift in Sources */,
217 | 8D676C841D5FBB3300F3B088 /* ContactsViewController.swift in Sources */,
218 | 8DF7221C1D8040BC007F6129 /* Contact.swift in Sources */,
219 | 8DF722141D804075007F6129 /* ContactViewModel.swift in Sources */,
220 | 8DF722181D80409B007F6129 /* CoreDataStore.swift in Sources */,
221 | 8D676C821D5FBB3300F3B088 /* AppDelegate.swift in Sources */,
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | };
225 | /* End PBXSourcesBuildPhase section */
226 |
227 | /* Begin PBXVariantGroup section */
228 | 8D676C851D5FBB3300F3B088 /* Main.storyboard */ = {
229 | isa = PBXVariantGroup;
230 | children = (
231 | 8D676C861D5FBB3300F3B088 /* Base */,
232 | );
233 | name = Main.storyboard;
234 | sourceTree = "";
235 | };
236 | 8D676C8A1D5FBB3300F3B088 /* LaunchScreen.storyboard */ = {
237 | isa = PBXVariantGroup;
238 | children = (
239 | 8D676C8B1D5FBB3300F3B088 /* Base */,
240 | );
241 | name = LaunchScreen.storyboard;
242 | sourceTree = "";
243 | };
244 | /* End PBXVariantGroup section */
245 |
246 | /* Begin XCBuildConfiguration section */
247 | 8D676C8E1D5FBB3300F3B088 /* Debug */ = {
248 | isa = XCBuildConfiguration;
249 | buildSettings = {
250 | ALWAYS_SEARCH_USER_PATHS = NO;
251 | CLANG_ANALYZER_NONNULL = YES;
252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
253 | CLANG_CXX_LIBRARY = "libc++";
254 | CLANG_ENABLE_MODULES = YES;
255 | CLANG_ENABLE_OBJC_ARC = YES;
256 | CLANG_WARN_BOOL_CONVERSION = YES;
257 | CLANG_WARN_CONSTANT_CONVERSION = YES;
258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
259 | CLANG_WARN_EMPTY_BODY = YES;
260 | CLANG_WARN_ENUM_CONVERSION = YES;
261 | CLANG_WARN_INFINITE_RECURSION = YES;
262 | CLANG_WARN_INT_CONVERSION = YES;
263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
264 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
265 | CLANG_WARN_UNREACHABLE_CODE = YES;
266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
267 | CODE_SIGN_IDENTITY = "iPhone Developer";
268 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
269 | COPY_PHASE_STRIP = NO;
270 | DEBUG_INFORMATION_FORMAT = dwarf;
271 | ENABLE_STRICT_OBJC_MSGSEND = YES;
272 | ENABLE_TESTABILITY = YES;
273 | GCC_C_LANGUAGE_STANDARD = gnu99;
274 | GCC_DYNAMIC_NO_PIC = NO;
275 | GCC_NO_COMMON_BLOCKS = YES;
276 | GCC_OPTIMIZATION_LEVEL = 0;
277 | GCC_PREPROCESSOR_DEFINITIONS = (
278 | "DEBUG=1",
279 | "$(inherited)",
280 | );
281 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
282 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
283 | GCC_WARN_UNDECLARED_SELECTOR = YES;
284 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
285 | GCC_WARN_UNUSED_FUNCTION = YES;
286 | GCC_WARN_UNUSED_VARIABLE = YES;
287 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
288 | MTL_ENABLE_DEBUG_INFO = YES;
289 | ONLY_ACTIVE_ARCH = YES;
290 | SDKROOT = iphoneos;
291 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
292 | };
293 | name = Debug;
294 | };
295 | 8D676C8F1D5FBB3300F3B088 /* Release */ = {
296 | isa = XCBuildConfiguration;
297 | buildSettings = {
298 | ALWAYS_SEARCH_USER_PATHS = NO;
299 | CLANG_ANALYZER_NONNULL = YES;
300 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
301 | CLANG_CXX_LIBRARY = "libc++";
302 | CLANG_ENABLE_MODULES = YES;
303 | CLANG_ENABLE_OBJC_ARC = YES;
304 | CLANG_WARN_BOOL_CONVERSION = YES;
305 | CLANG_WARN_CONSTANT_CONVERSION = YES;
306 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
307 | CLANG_WARN_EMPTY_BODY = YES;
308 | CLANG_WARN_ENUM_CONVERSION = YES;
309 | CLANG_WARN_INFINITE_RECURSION = YES;
310 | CLANG_WARN_INT_CONVERSION = YES;
311 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
312 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
313 | CLANG_WARN_UNREACHABLE_CODE = YES;
314 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
315 | CODE_SIGN_IDENTITY = "iPhone Developer";
316 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
317 | COPY_PHASE_STRIP = NO;
318 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
319 | ENABLE_NS_ASSERTIONS = NO;
320 | ENABLE_STRICT_OBJC_MSGSEND = YES;
321 | GCC_C_LANGUAGE_STANDARD = gnu99;
322 | GCC_NO_COMMON_BLOCKS = YES;
323 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
324 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
325 | GCC_WARN_UNDECLARED_SELECTOR = YES;
326 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
327 | GCC_WARN_UNUSED_FUNCTION = YES;
328 | GCC_WARN_UNUSED_VARIABLE = YES;
329 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
330 | MTL_ENABLE_DEBUG_INFO = NO;
331 | SDKROOT = iphoneos;
332 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
333 | VALIDATE_PRODUCT = YES;
334 | };
335 | name = Release;
336 | };
337 | 8D676C911D5FBB3300F3B088 /* Debug */ = {
338 | isa = XCBuildConfiguration;
339 | buildSettings = {
340 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
341 | DEVELOPMENT_TEAM = JK33ZA2AUD;
342 | INFOPLIST_FILE = "MVVM Contacts Starter/Info.plist";
343 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
344 | PRODUCT_BUNDLE_IDENTIFIER = "com.rsacchi.MVVM-Contacts-Starter";
345 | PRODUCT_NAME = "$(TARGET_NAME)";
346 | SWIFT_VERSION = 3.0;
347 | };
348 | name = Debug;
349 | };
350 | 8D676C921D5FBB3300F3B088 /* Release */ = {
351 | isa = XCBuildConfiguration;
352 | buildSettings = {
353 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
354 | DEVELOPMENT_TEAM = JK33ZA2AUD;
355 | INFOPLIST_FILE = "MVVM Contacts Starter/Info.plist";
356 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
357 | PRODUCT_BUNDLE_IDENTIFIER = "com.rsacchi.MVVM-Contacts-Starter";
358 | PRODUCT_NAME = "$(TARGET_NAME)";
359 | SWIFT_VERSION = 3.0;
360 | };
361 | name = Release;
362 | };
363 | /* End XCBuildConfiguration section */
364 |
365 | /* Begin XCConfigurationList section */
366 | 8D676C791D5FBB3200F3B088 /* Build configuration list for PBXProject "MVVM Contacts Starter" */ = {
367 | isa = XCConfigurationList;
368 | buildConfigurations = (
369 | 8D676C8E1D5FBB3300F3B088 /* Debug */,
370 | 8D676C8F1D5FBB3300F3B088 /* Release */,
371 | );
372 | defaultConfigurationIsVisible = 0;
373 | defaultConfigurationName = Release;
374 | };
375 | 8D676C901D5FBB3300F3B088 /* Build configuration list for PBXNativeTarget "MVVM Contacts Starter" */ = {
376 | isa = XCConfigurationList;
377 | buildConfigurations = (
378 | 8D676C911D5FBB3300F3B088 /* Debug */,
379 | 8D676C921D5FBB3300F3B088 /* Release */,
380 | );
381 | defaultConfigurationIsVisible = 0;
382 | defaultConfigurationName = Release;
383 | };
384 | /* End XCConfigurationList section */
385 |
386 | /* Begin XCVersionGroup section */
387 | 8DF722231D804116007F6129 /* MVVM_Contacts.xcdatamodeld */ = {
388 | isa = XCVersionGroup;
389 | children = (
390 | 8DF722241D804116007F6129 /* MVVM_Contacts.xcdatamodel */,
391 | );
392 | currentVersion = 8DF722241D804116007F6129 /* MVVM_Contacts.xcdatamodel */;
393 | path = MVVM_Contacts.xcdatamodeld;
394 | sourceTree = "";
395 | versionGroupType = wrapper.xcdatamodel;
396 | };
397 | /* End XCVersionGroup section */
398 | };
399 | rootObject = 8D676C761D5FBB3200F3B088 /* Project object */;
400 | }
401 |
--------------------------------------------------------------------------------
/MVVM Contacts Final/MVVM Contacts Final/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
--------------------------------------------------------------------------------
/VIPER Contacts Final/VIPER Contacts Final/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
--------------------------------------------------------------------------------
/MVVM Contacts Starter/MVVM Contacts Starter/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
--------------------------------------------------------------------------------