diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.swift-version b/.swift-version old mode 100644 new mode 100755 diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 diff --git a/.xctool-args b/.xctool-args old mode 100644 new mode 100755 diff --git a/Contacts Picker.xcodeproj/project.pbxproj b/Contacts Picker.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 6d93bb0..73449cf --- a/Contacts Picker.xcodeproj/project.pbxproj +++ b/Contacts Picker.xcodeproj/project.pbxproj @@ -127,8 +127,9 @@ TargetAttributes = { F4C1C93D1BDF8AB7001AA643 = { CreatedOnToolsVersion = 7.0; + DevelopmentTeam = NDQC9EKP48; LastSwiftMigration = 0800; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -294,7 +295,8 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = NDQC9EKP48; INFOPLIST_FILE = "Contacts Picker/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.prabaharan.eppicker.Contacts-Picker"; @@ -307,7 +309,8 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = NDQC9EKP48; INFOPLIST_FILE = "Contacts Picker/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.prabaharan.eppicker.Contacts-Picker"; diff --git a/Contacts Picker.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Contacts Picker.xcodeproj/project.xcworkspace/contents.xcworkspacedata old mode 100644 new mode 100755 diff --git a/Contacts Picker.xcodeproj/project.xcworkspace/xcuserdata/prabaharan.e.xcuserdatad/UserInterfaceState.xcuserstate b/Contacts Picker.xcodeproj/project.xcworkspace/xcuserdata/prabaharan.e.xcuserdatad/UserInterfaceState.xcuserstate old mode 100644 new mode 100755 diff --git a/Contacts Picker.xcodeproj/xcshareddata/xcschemes/Contacts Picker.xcscheme b/Contacts Picker.xcodeproj/xcshareddata/xcschemes/Contacts Picker.xcscheme old mode 100644 new mode 100755 diff --git a/Contacts Picker.xcodeproj/xcuserdata/prabaharan.e.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Contacts Picker.xcodeproj/xcuserdata/prabaharan.e.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist old mode 100644 new mode 100755 diff --git a/Contacts Picker.xcodeproj/xcuserdata/prabaharan.e.xcuserdatad/xcschemes/xcschememanagement.plist b/Contacts Picker.xcodeproj/xcuserdata/prabaharan.e.xcuserdatad/xcschemes/xcschememanagement.plist old mode 100644 new mode 100755 diff --git a/Contacts Picker/AppDelegate.swift b/Contacts Picker/AppDelegate.swift old mode 100644 new mode 100755 diff --git a/Contacts Picker/Assets.xcassets/AppIcon.appiconset/Contents.json b/Contacts Picker/Assets.xcassets/AppIcon.appiconset/Contents.json old mode 100644 new mode 100755 diff --git a/Contacts Picker/Base.lproj/LaunchScreen.storyboard b/Contacts Picker/Base.lproj/LaunchScreen.storyboard old mode 100644 new mode 100755 diff --git a/Contacts Picker/Base.lproj/Main.storyboard b/Contacts Picker/Base.lproj/Main.storyboard old mode 100644 new mode 100755 diff --git a/Contacts Picker/Info.plist b/Contacts Picker/Info.plist old mode 100644 new mode 100755 diff --git a/Contacts Picker/ViewController.swift b/Contacts Picker/ViewController.swift old mode 100644 new mode 100755 index 54a3cc3..c95d49b --- a/Contacts Picker/ViewController.swift +++ b/Contacts Picker/ViewController.swift @@ -22,7 +22,7 @@ class ViewController: UIViewController, EPPickerDelegate { @IBAction func onTouchShowMeContactsButton(_ sender: AnyObject) { - let contactPickerScene = EPContactsPicker(delegate: self, multiSelection:true, subtitleCellType: SubtitleCellValue.email) + let contactPickerScene = EPContactsPicker(delegate: self, multiSelection:true, subtitleCellType: SubtitleCellValue.phoneNumber) let navigationController = UINavigationController(rootViewController: contactPickerScene) self.present(navigationController, animated: true, completion: nil) diff --git a/EPContactsPicker.podspec b/EPContactsPicker.podspec old mode 100644 new mode 100755 index 64555be..aa0d5c2 --- a/EPContactsPicker.podspec +++ b/EPContactsPicker.podspec @@ -23,5 +23,8 @@ DESC s.resource_bundles = { 'EPContactsPicker' => ['Pods/**/*.xib'] } + s.pod_target_xcconfig = { + 'SWIFT_VERSION' => '3.0' + } end diff --git a/EPContactsPickerLogo.jpg b/EPContactsPickerLogo.jpg old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/Pods/EPConstants.swift b/Pods/EPConstants.swift old mode 100644 new mode 100755 diff --git a/Pods/EPContact.swift b/Pods/EPContact.swift old mode 100644 new mode 100755 index 5f5efb5..6c2bbd7 --- a/Pods/EPContact.swift +++ b/Pods/EPContact.swift @@ -9,8 +9,9 @@ import UIKit import Contacts -open class EPContact { +@objc open class EPContact: NSObject { + open var originalCNContact: CNContact open var firstName: String open var lastName: String open var company: String @@ -23,6 +24,7 @@ open class EPContact { open var emails = [(email: String, emailLabel: String )]() public init (contact: CNContact) { + originalCNContact = contact firstName = contact.givenName lastName = contact.familyName company = contact.organizationName diff --git a/Pods/EPContactCell.swift b/Pods/EPContactCell.swift old mode 100644 new mode 100755 index 7944249..ff55b68 --- a/Pods/EPContactCell.swift +++ b/Pods/EPContactCell.swift @@ -8,7 +8,7 @@ import UIKit -class EPContactCell: UITableViewCell { +@objc open class EPContactCell: UITableViewCell { @IBOutlet weak var contactTextLabel: UILabel! @IBOutlet weak var contactDetailTextLabel: UILabel! @@ -18,7 +18,7 @@ class EPContactCell: UITableViewCell { var contact: EPContact? - override func awakeFromNib() { + override open func awakeFromNib() { super.awakeFromNib() // Initialization code @@ -27,7 +27,7 @@ class EPContactCell: UITableViewCell { contactContainerView.layer.cornerRadius = contactContainerView.frame.size.width/2 } - override func setSelected(_ selected: Bool, animated: Bool) { + override open func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } diff --git a/Pods/EPContactCell.xib b/Pods/EPContactCell.xib old mode 100644 new mode 100755 diff --git a/Pods/EPContactsPicker.swift b/Pods/EPContactsPicker.swift old mode 100644 new mode 100755 index d72678c..9c28124 --- a/Pods/EPContactsPicker.swift +++ b/Pods/EPContactsPicker.swift @@ -10,30 +10,31 @@ import UIKit import Contacts -public protocol EPPickerDelegate { - func epContactPicker(_: EPContactsPicker, didContactFetchFailed error: NSError) - func epContactPicker(_: EPContactsPicker, didCancel error: NSError) - func epContactPicker(_: EPContactsPicker, didSelectContact contact: EPContact) - func epContactPicker(_: EPContactsPicker, didSelectMultipleContacts contacts: [EPContact]) -} -public extension EPPickerDelegate { - func epContactPicker(_: EPContactsPicker, didContactFetchFailed error: NSError) { } - func epContactPicker(_: EPContactsPicker, didCancel error: NSError) { } - func epContactPicker(_: EPContactsPicker, didSelectContact contact: EPContact) { } - func epContactPicker(_: EPContactsPicker, didSelectMultipleContacts contacts: [EPContact]) { } +@objc public protocol EPPickerDelegate { + @objc optional func epContactPicker(_: EPContactsPicker, didContactFetchFailed error: NSError) + @objc optional func epContactPicker(_: EPContactsPicker, didCancel error: NSError) + @objc optional func epContactPicker(_: EPContactsPicker, didSelectContact contact: EPContact) + @objc optional func epContactPicker(_: EPContactsPicker, didSelectMultipleContacts contacts: [EPContact]) } -typealias ContactsHandler = (_ contacts : [CNContact] , _ error : NSError?) -> Void +//public extension EPPickerDelegate { +// func epContactPicker(_: EPContactsPicker, didContactFetchFailed error: NSError) { } +// func epContactPicker(_: EPContactsPicker, didCancel error: NSError) { } +// func epContactPicker(_: EPContactsPicker, didSelectContact contact: EPContact) { } +// func epContactPicker(_: EPContactsPicker, didSelectMultipleContacts contacts: [EPContact]) { } +//} + +public typealias ContactsHandler = (_ contacts : [CNContact] , _ error : NSError?) -> Void -public enum SubtitleCellValue{ +@objc public enum SubtitleCellValue: Int { case phoneNumber case email case birthday case organization } -open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate { +@objc open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate { // MARK: - Properties @@ -48,12 +49,15 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS var subtitleCellValue = SubtitleCellValue.phoneNumber var multiSelectEnabled: Bool = false //Default is single selection contact + var filterOnlyWithPhones: Bool = false // MARK: - Lifecycle Methods override open func viewDidLoad() { super.viewDidLoad() - self.title = EPGlobalConstants.Strings.contactsTitle + if (self.title == nil) { + self.title = EPGlobalConstants.Strings.contactsTitle + } registerContactCell() inititlizeBarButtons() @@ -121,7 +125,14 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS self.multiSelectEnabled = multiSelection contactDelegate = delegate } - + + convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool, contactsTitle: String) { + self.init(style: .plain) + self.multiSelectEnabled = multiSelection + contactDelegate = delegate + self.title = contactsTitle + } + convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool, subtitleCellType: SubtitleCellValue) { self.init(style: .plain) self.multiSelectEnabled = multiSelection @@ -129,10 +140,26 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS subtitleCellValue = subtitleCellType } + convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool, contactsTitle: String, subtitleCellType: SubtitleCellValue) { + self.init(style: .plain) + self.multiSelectEnabled = multiSelection + contactDelegate = delegate + self.title = contactsTitle + subtitleCellValue = subtitleCellType + } + + convenience public init(delegate: EPPickerDelegate?, multiSelection : Bool, contactsTitle: String, subtitleCellType: SubtitleCellValue, filterOnlyWithPhones: Bool) { + self.init(style: .plain) + self.multiSelectEnabled = multiSelection + contactDelegate = delegate + self.title = contactsTitle + subtitleCellValue = subtitleCellType + self.filterOnlyWithPhones = filterOnlyWithPhones + } // MARK: - Contact Operations - open func reloadContacts() { + open func reloadContacts() { getContacts( {(contacts, error) in if (error == nil) { DispatchQueue.main.async(execute: { @@ -140,9 +167,9 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS }) } }) - } + } - func getContacts(_ completion: @escaping ContactsHandler) { + open func getContacts(_ completion: @escaping ContactsHandler) { if contactsStore == nil { //ContactStore is control for accessing the Contacts contactsStore = CNContactStore() @@ -157,7 +184,7 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS let alert = UIAlertController(title: "Unable to access contacts", message: "\(productName) does not have access to contacts. Kindly enable it in privacy settings ", preferredStyle: UIAlertControllerStyle.alert) let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { action in - self.contactDelegate?.epContactPicker(self, didContactFetchFailed: error) + self.contactDelegate?.epContactPicker!(self, didContactFetchFailed: error) completion([], error) self.dismiss(animated: true, completion: nil) }) @@ -180,12 +207,24 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS case CNAuthorizationStatus.authorized: //Authorization granted by user for this app. - var contactsArray = [CNContact]() - let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys()) - - do { - try contactsStore?.enumerateContacts(with: contactFetchRequest, usingBlock: { (contact, stop) -> Void in + getContactsOnBackgroundThread { (contactsArray) in + completion(contactsArray, nil) + } + + } + } + + open func getContactsOnBackgroundThread ( completion:@escaping (_ contacts:[CNContact])->()) { + + DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in + + var contactsArray = [CNContact]() + let contactFetchRequest = CNContactFetchRequest(keysToFetch: self.allowedContactKeys()) + + do { + try self.contactsStore?.enumerateContacts(with: contactFetchRequest, usingBlock: { (contact, stop) -> Void in + if self.filterOnlyWithPhones == false || contact.phoneNumbers.count > 0 { //Ordering contacts based on alphabets in firstname contactsArray.append(contact) var key: String = "#" @@ -200,23 +239,88 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS } contacts.append(contact) self.orderedContacts[key] = contacts - - }) - self.sortedContactKeys = Array(self.orderedContacts.keys).sorted(by: <) - if self.sortedContactKeys.first == "#" { - self.sortedContactKeys.removeFirst() - self.sortedContactKeys.append("#") } - completion(contactsArray, nil) - } - //Catching exception as enumerateContactsWithFetchRequest can throw errors - catch let error as NSError { - print(error.localizedDescription) + }) + self.sortedContactKeys = Array(self.orderedContacts.keys).sorted(by: <) + if self.sortedContactKeys.first == "#" { + self.sortedContactKeys.removeFirst() + self.sortedContactKeys.append("#") } + } + //Catching exception as enumerateContactsWithFetchRequest can throw errors + catch let error as NSError { + print(error.localizedDescription) + } - } + DispatchQueue.main.async(execute: { () -> Void in + completion(contactsArray) + }) + }) } - + + open func updateSearchResults(for searchController: UISearchController) { + if let searchText = resultSearchController.searchBar.text, searchController.isActive { + + if searchText.characters.count == 0 { + filteredContacts = [] + self.tableView.reloadData() + return + } + + let predicate: NSPredicate + if searchText.characters.count > 0 { + predicate = CNContact.predicateForContacts(matchingName: searchText) + } else { + predicate = CNContact.predicateForContactsInContainer(withIdentifier: contactsStore!.defaultContainerIdentifier()) + } + + let store = CNContactStore() + do { + filteredContacts = try store.unifiedContacts(matching: predicate, keysToFetch: allowedContactKeys()) + self.tableView.reloadData() + } + catch { + print("Error!") + } + } + } + +// open func updateSearchResults(for searchController: UISearchController) { +// if resultSearchController.searchBar.text != nil, searchController.isActive { +// updateSearchResultsOnBackgroundThread { (filteredContacts) in +// self.filteredContacts = filteredContacts +// self.tableView.reloadData() +// } +// } +// } +// +// open func updateSearchResultsOnBackgroundThread ( completion:@escaping (_ contacts:[CNContact])->()) { +// +// DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in +// +// let searchText = self.resultSearchController.searchBar.text! +// let predicate: NSPredicate +// if searchText.characters.count > 0 { +// predicate = CNContact.predicateForContacts(matchingName: (searchText)) +// } else { +// predicate = CNContact.predicateForContactsInContainer(withIdentifier: self.contactsStore!.defaultContainerIdentifier()) +// } +// +// let store = CNContactStore() +// var filteredContacts = [CNContact]() +// do { +// filteredContacts = try store.unifiedContacts(matching: predicate, keysToFetch: self.allowedContactKeys()) +// } +// catch { +// print("Error!") +// } +// +// DispatchQueue.main.async(execute: { () -> Void in +// completion(filteredContacts) +// }) +// }) +// } + func allowedContactKeys() -> [CNKeyDescriptor]{ //We have to provide only the keys which we have to access. We should avoid unnecessary keys when fetching the contact. Reducing the keys means faster the access. return [CNContactNamePrefixKey as CNKeyDescriptor, @@ -296,7 +400,7 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS resultSearchController.isActive = false self.dismiss(animated: true, completion: { DispatchQueue.main.async { - self.contactDelegate?.epContactPicker(self, didSelectContact: selectedContact) + self.contactDelegate?.epContactPicker!(self, didSelectContact: selectedContact) } }) } @@ -325,43 +429,17 @@ open class EPContactsPicker: UITableViewController, UISearchResultsUpdating, UIS // MARK: - Button Actions func onTouchCancelButton() { - contactDelegate?.epContactPicker(self, didCancel: NSError(domain: "EPContactPickerErrorDomain", code: 2, userInfo: [ NSLocalizedDescriptionKey: "User Canceled Selection"])) + contactDelegate?.epContactPicker!(self, didCancel: NSError(domain: "EPContactPickerErrorDomain", code: 2, userInfo: [ NSLocalizedDescriptionKey: "User Canceled Selection"])) dismiss(animated: true, completion: nil) } func onTouchDoneButton() { - contactDelegate?.epContactPicker(self, didSelectMultipleContacts: selectedContacts) + contactDelegate?.epContactPicker!(self, didSelectMultipleContacts: selectedContacts) dismiss(animated: true, completion: nil) } // MARK: - Search Actions - open func updateSearchResults(for searchController: UISearchController) - { - if let searchText = resultSearchController.searchBar.text , searchController.isActive { - - let predicate: NSPredicate - if searchText.characters.count > 0 { - predicate = CNContact.predicateForContacts(matchingName: searchText) - } else { - predicate = CNContact.predicateForContactsInContainer(withIdentifier: contactsStore!.defaultContainerIdentifier()) - } - - let store = CNContactStore() - do { - filteredContacts = try store.unifiedContacts(matching: predicate, - keysToFetch: allowedContactKeys()) - //print("\(filteredContacts.count) count") - - self.tableView.reloadData() - - } - catch { - print("Error!") - } - } - } - open func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { DispatchQueue.main.async(execute: { diff --git a/Pods/EPExtensions.swift b/Pods/EPExtensions.swift old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/Screenshots/Screen1.png b/Screenshots/Screen1.png old mode 100644 new mode 100755 diff --git a/Screenshots/Screen2.png b/Screenshots/Screen2.png old mode 100644 new mode 100755 diff --git a/Screenshots/Screen3.png b/Screenshots/Screen3.png old mode 100644 new mode 100755