ManagingContacts/ManagingContacts/MainViewController.swift

/*
    Copyright (C) 2017 Apple Inc. All Rights Reserved.
    See LICENSE.txt for this sample’s licensing information
    
    Abstract:
    A BaseViewController subclass that displays a list of containers, 
                contacts, or groups from which you can select an item.
                Destination view controller for the performFetchGroupsPerContainer,
                performFetchContactsPerContainer, performFetchContactsPerGroup,
                performUpdateGroup, performRemoveContactFromGroup, and 
                performUpdateContact segues.
                Source view controller for the fetchGroupsPerContainer,
                fetchContactsPerContainer, fetchContactsPerGroup, updateGroup,
                updateContact, and removeContactFromGroup segues.
                Implements the unwind updateGroup and updateContact segues that allow
                you to update a group and a contact, respectively.
*/
 
import UIKit
import Contacts
 
class MainViewController: BaseViewController {
    // MARK: - Properties
    
    /// Selected CNContact or CNGroup object.
    fileprivate var selectedItem: AnyObject?
    fileprivate var selectedIndexPath: IndexPath?
    fileprivate var contacStoretUtility = MGCContactStoreUtilities()
    
    
    // MARK: - UITableViewDelegate
    
     override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let model = data[indexPath.section]
        
        if model.tab == MGCAppConfiguration.Content.containers {
            
            let container = model.content[indexPath.row] as! CNContainer
            cell.textLabel!.text = (container.type == .local) ? MGCAppConfiguration.MainStoryboard.Cells.local : container.name
        }
        else if model.tab == MGCAppConfiguration.Content.groups {
            
            let group = model.content[indexPath.row] as! CNGroup
            cell.textLabel!.text = group.name
        }
        else if model.tab == MGCAppConfiguration.Content.contacts {
            
            let contact = model.content[indexPath.row] as! CNContact
            cell.textLabel!.text = (contact.isPerson) ? contact.formattedName : contact.organizationName
        }
     }
    
    
    // MARK: - Convenience Method
    
    /// Builds data source for a given view controller.
    fileprivate func handleFetching(_ identifier: String, with viewController: BaseViewController, segue: String) {
        if segue == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchGroupsPerContainer {
            
            // Use the container's identifier to fetch its associated groups.
            MGCContactStore.sharedInstance.fetchGroupsInContainer(with: identifier, completion: ({(groups: [CNGroup]) in
                if groups.count > 0 {
                    
                    viewController.data = [MGCModel(tab: MGCAppConfiguration.Content.containers,
                                                content: groups,
                                               category: MGCAppConfiguration.Content.groups)]
                }
                else {
                    // Display "No groups" when the container does not have any groups.
                    viewController.data = [MGCModel(tab: MGCAppConfiguration.Content.containers,
                                                content: [MGCAppConfiguration.Content.NoItems.groups.rawValue as AnyObject],
                                               category: MGCAppConfiguration.Content.NoItems.groups.rawValue)]
                }
            }))
        }
        else if segue == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchContactsPerContainer {
            
            // Use the container's identifier to fetch its associated contacts.
            MGCContactStore.sharedInstance.fetchContactsInContainer(with: identifier, completion: ({(contacts: [CNContact]) in
                if contacts.count > 0 {
                    viewController.data = [MGCModel(tab: MGCAppConfiguration.Content.containers,
                                                content: contacts,
                                               category: MGCAppConfiguration.Content.contacts)]
                }
                else {
                    // Diplay "No contacts" when the container does not have any contacts.
                    viewController.data = [MGCModel(tab: MGCAppConfiguration.Content.containers,
                                                content: [MGCAppConfiguration.Content.NoItems.contacts.rawValue as AnyObject],
                                               category: MGCAppConfiguration.Content.NoItems.contacts.rawValue)]
                }
            }))
        }
        else if segue == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Groups.fetchContactsPerGroup
        {
            
            // Use the group's identifier to fetch its associated contacts.
            MGCContactStore.sharedInstance.fetchContactsInGroup(with: identifier, completion: ({(contacts: [CNContact]) in
                if contacts.count > 0 {
                    viewController.data = [MGCModel(tab: MGCAppConfiguration.Content.groups,
                                                content: contacts,
                                               category: MGCAppConfiguration.Content.contacts)]
                }
                else {
                    // Display "No contacts" when the group does not any contacts.
                    viewController.data = [MGCModel(tab: MGCAppConfiguration.Content.groups,
                                                content: [MGCAppConfiguration.Content.NoItems.contacts.rawValue as AnyObject],
                                               category: MGCAppConfiguration.Content.NoItems.contacts.rawValue)]
                }
            }))
        }
    }
    
    
    // MARK: - Navigation
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
        selectedIndexPath = tableView.indexPathForSelectedRow!
        let model = data[selectedIndexPath!.section]
        
        /* Implement the "Fetch groups per container" and 
           "Fetch contacts per container" features.
        */
        if segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchGroupsPerContainer || segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchContactsPerContainer {
            
            let container = model.content[selectedIndexPath!.row] as! CNContainer
            let detailViewController = segue.destination as! DetailViewController
            detailViewController.title = (container.type == .local) ? MGCAppConfiguration.MainStoryboard.Cells.local : container.name
            
            let segue = (segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchGroupsPerContainer) ? MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchGroupsPerContainer : MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Containers.fetchContactsPerContainer
            
            handleFetching(container.identifier, with: detailViewController, segue: segue)
        }
        // Implement the "Fetch contacts per group" feature.
        else if segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Groups.fetchContactsPerGroup {
            
            let group = model.content[selectedIndexPath!.row] as! CNGroup
            let detailViewController = segue.destination as! DetailViewController
            detailViewController.title = group.name
            
            handleFetching(group.identifier, with: detailViewController, segue: MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Groups.fetchContactsPerGroup)
        }
        // Implement the "Update a group" feature.
        else if segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Groups.updateGroup {
            
            selectedItem = model.content[selectedIndexPath!.row] as! CNGroup
            let updateGroupViewController = segue.destination as! UpdateGroupViewController
            updateGroupViewController.name = selectedItem!.name
        }
        // Implement the "Update a contact" feature.
        else if segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Contacts.updateContact {
            
            selectedItem = model.content[selectedIndexPath!.row] as! CNContact
            let updateContactViewController =  segue.destination as! UpdateContactViewController
            updateContactViewController.contact = contacStoretUtility.parse(selectedItem as! CNContact)
        }
        // Implement the "Remove contact from a group" feature.
        else if segue.identifier == MGCAppConfiguration.MainStoryboard.SegueIdentifiers.Groups.removeContactFromGroup {
            
            let group = model.content[selectedIndexPath!.row] as! CNGroup
            let editViewController = segue.destination as! EditViewController
            editViewController.title = group.name
            editViewController.relatedGroup = group
            
            // Fetch contacts associated with the selected group.
            MGCContactStore.sharedInstance.fetchContactsInGroup(with: group.identifier, completion: ({(contacts: [CNContact]) in
                if contacts.count > 0 {
                    editViewController.data = [MGCModel(tab: MGCAppConfiguration.Content.groups,
                                                    content: contacts,
                                                      segue: model.segue!)]
                }
                else {
                    
                    editViewController.data = [MGCModel(tab: MGCAppConfiguration.Content.groups,
                                                    content: [MGCAppConfiguration.Content.NoItems.contacts.rawValue as AnyObject],
                                                   category: MGCAppConfiguration.Content.NoItems.contacts.rawValue)]
                    
                    editViewController.enableEditButton = false
                }
                
                editViewController.enableEditButton = (contacts.count > 0) ? true : false
            }))
        }
    }
    
    
    // MARK: - Actions
    
    /// Attempt to update a group.
    @IBAction func updateGroup(_ sender: UIStoryboardSegue) {
        if  let updateGroupViewController = sender.source as? UpdateGroupViewController, let name = updateGroupViewController.name, let selectedGroup = selectedItem as? CNGroup {
            
                // Attempt to update the group in Contacts.
                MGCContactStore.sharedInstance.update(selectedGroup, with: name, completion: ({(group: CNGroup?) in
                    /* If the update operation was successful, refresh the UI 
                       with the updated info.
                    */
                    if let updatedGroup = group {
                        
                        DispatchQueue.main.async {
                           self.data[self.selectedIndexPath!.section].content[self.selectedIndexPath!.row] = updatedGroup
                           self.tableView.reloadData()
                        }
                    }
            }))
        }
    }
    
    /// Attempt to update a contact.
    @IBAction func updateContact(_ sender: UIStoryboardSegue) {
         if let updateContactViewController = sender.source as? UpdateContactViewController, let contact = updateContactViewController.contact, let selectedContact = selectedItem as? CNContact {
            
            MGCContactStore.sharedInstance.update(selectedContact, with: contact, completion:({(contact: CNContact?) in
                if let updatedContact = contact {
                    
                    DispatchQueue.main.async {
                       self.data[self.selectedIndexPath!.section].content[self.selectedIndexPath!.row] = updatedContact
                       self.tableView.reloadData()
                    }
                }
            }))
        }
    }
}