Getting people name and number from mobile contact in IOS - ios

I want exactly this process in my app https://www.youtube.com/watch?v=HKCXwm7r838. But the code in this tutorial is for older IOS version, i'm using IOS 10, Xcode 8.2, Swift 3.0, So its not working for me. I tried the following code to display the contact list. It works.
import UIKit
import ContactsUI
class ViewController: UIViewController, CNContactPickerDelegate{
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func addExistingContact()
{
}
func contactPicker(picker: CNContactPickerViewController, didSelectContact contacts: CNContact)
{
// print(contacts.givenName)
}
#IBAction func button(_ sender: Any)
{
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
contactPicker.displayedPropertyKeys =
[CNContactGivenNameKey
, CNContactPhoneNumbersKey]
self.present(contactPicker, animated: true, completion: nil)
}
func contactPickerDidCancel(picker: CNContactPickerViewController)
{
print("Cancel Contact Picker")
}
}
Now, what I want is, when I select any contact name from the list, the contact list must disappear immediately, and then the selected name and number must be displayed in two labels/textFields in a viewController. Can someone tell me what code should i use ? Thanks in advance.

Replace methods contactPicker(picker: CNContactPickerViewController, didSelectContact contacts: CNContact) and contactPickerDidCancel(picker: CNContactPickerViewController) with
func contactPicker(_ picker: CNContactPickerViewController,
didSelect contactProperty: CNContactProperty) {
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
// You can fetch selected name and number in the following way
// user name
let userName:String = contact.givenName
// user phone number
let userPhoneNumbers:[CNLabeledValue<CNPhoneNumber>] = contact.phoneNumbers
let firstPhoneNumber:CNPhoneNumber = userPhoneNumbers[0].value
// user phone number string
let primaryPhoneNumberStr:String = firstPhoneNumber.stringValue
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
}
Given that your requirement is for the ContactPicker to close immediately after clicking on contact, you shouldn't implement the following methods. Remove the below given methods from your code.
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
}
func contactPicker(_ picker: CNContactPickerViewController, didSelectContactProperties contactProperties: [CNContactProperty]) {
}

I will tell you how to get your contact list. The last part of your post (The list stuff) you will have to do it yourself, as it's just playing around with TableView controllers.
First import:
import Contacts
I use this, for example, to get all my contacts:
let store = CNContactStore()
var contacts = [CNContact]()
let keys = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactSocialProfilesKey] as [Any]
let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
do{
try store.enumerateContacts(with: request){
(contact, stop) in
contacts.append(contact)
}
guard contacts.count > 0 else{
//Show contact list
return
}
} catch let err{
print(err)
}
Also, somewhere in your code, you will want to ask for Contact Permission:
switch CNContactStore.authorizationStatus(for: .contacts){
case .authorized:
//
case .notDetermined:
store.requestAccess(for: .contacts){succeeded, err in
guard err == nil && succeeded else{
return
}
}
case .denied:
//
case .restricted:
//
default:
print("Not handled")
}
IMPORTANT: Add in your info.plist:
Privacy - Contacts Usage Description

Related

Stripe iOS didCreatePaymentResult never gets called

The problem seems simple, didCreatePaymentResult never gets called.
BUT, in my old sample project, taken from your iOS example for payment intent, that didCreatePaymentResult gets called every single time I create or select a card, here's the repo of the working project: https://github.com/glennposadas/stripe-example-ios-nodejs
BUT again, my main concern is my current project.
I use v19.2.0 in both of these projects, I even tried the v19.3.0.
I wanted to use Stripe Charge really, but I believe Stripe does not support Apple pay for that. So I have no choice but to use Stripe Payment Intent.
CoreService.swift (conforms to STPCustomerEphemeralKeyProvider)
extension CoreService: STPCustomerEphemeralKeyProvider {
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
orderServiceProvider.request(.requestEphemeralKey(stripeAPIVersion: apiVersion)) { (result) in
switch result {
case let .success(response):
guard let json = ((try? JSONSerialization.jsonObject(with: response.data, options: []) as? [String : Any]) as [String : Any]??) else {
completion(nil, NSError(domain: "Error parsing stripe data", code: 300, userInfo: nil))
return
}
completion(json, nil)
default:
UIViewController.current()?.alert(title: "Error stripe", okayButtonTitle: "OK", withBlock: nil)
}
}
}
}
PaymentController.swift
class PaymentViewController: BaseViewController {
// MARK: - Properties
private var paymentContext: STPPaymentContext!
private let paymentConstantValue: Int = 3000
// MARK: - Functions
// MARK: Overrides
override func viewDidLoad() {
super.viewDidLoad()
self.setupStripe()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.hideNavBar(animated: true)
}
#IBAction func creditCardButtonTapped(_ sender: Any) {
self.paymentContext.presentPaymentOptionsViewController()
}
private func setupStripe() {
let config = STPPaymentConfiguration.shared()
config.appleMerchantIdentifier = "merchant.com.gsample.app"
config.companyName = "Scoutd LLC"
config.requiredBillingAddressFields = .none
config.requiredShippingAddressFields = .none
config.additionalPaymentOptions = .applePay
let customerContext = STPCustomerContext(keyProvider: CoreService())
let paymentContext = STPPaymentContext(
customerContext: customerContext,
configuration: config,
theme: STPTheme.default()
)
let userInformation = STPUserInformation()
paymentContext.prefilledInformation = userInformation
paymentContext.paymentAmount = self.paymentConstantValue
paymentContext.paymentCurrency = "usd"
self.paymentContext = paymentContext
self.paymentContext.delegate = self
self.paymentContext.hostViewController = self
}
}
// MARK: - STPPaymentContextDelegate
extension PaymentViewController: STPPaymentContextDelegate {
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
print("paymentContextDidChange")
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
// error alert....
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
print("didCreatePaymentResult ✅")
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
switch status {
case .success:
// success
case .error:
// error alert....
default:
break
}
}
}
SOLVED! This should help engineers struggling with Stripe implementation in the future.
So in my case, I have two buttons:
Apple Pay
Credit card.
The absolute solution for me is handle the selectedPaymentOption of the paymentContext.
Scenarios:
If the apple pay button is tapped, present apple pay sheet and don't present add/select card UI of Stripe.
If the credit card button is tapped, don't present apple pay sheet and instead present select card.
Related to #2, call requestPayment() if there's a selected option.
Voila! The didCreatePaymentResult now gets invoked!
// MARK: IBActions
#IBAction func applePayButtonTapped(_ sender: Any) {
if self.paymentContext.selectedPaymentOption is STPApplePayPaymentOption {
self.paymentContext.requestPayment()
}
}
#IBAction func creditCardButtonTapped(_ sender: Any) {
if let selectedPaymentOption = self.paymentContext.selectedPaymentOption,
!(selectedPaymentOption is STPApplePayPaymentOption) {
self.paymentContext.requestPayment()
return
}
self.paymentContext.presentPaymentOptionsViewController()
}

How do i compile all contact's phone numbers into one cell - using CNContact

My code I am using to source the contact's phone number is below, what i would like to do is have all the contacts phone numbers (landline, mobiles etc) shown together, example 0402 000 000, 618 802336, 0423 000 000 (together in the one textField)
My code below
// Retrieve contact details ---------------------------
#IBAction func show(_ sender: Any) {
let contacVC = CNContactPickerViewController()
contacVC.delegate = self
self.present(contacVC, animated: true, completion: nil)
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
print(contact.phoneNumbers)
let numbers = contact.phoneNumbers.first
print((numbers?.value)?.stringValue ?? "")
self.Phone.text = "\((numbers?.value)?.stringValue ?? "")"
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
self.dismiss(animated: true, completion: nil)
}
You can display all phone number as like below
let contact = CNContact()
let numbers = contact.phoneNumbers.compactMap { $0.value.stringValue }
self.Phone.text = numbers.joined(separator: ", ")

How to disable contact section ( message , call , video , mail ) from CNContactPickerViewController

I want to know it possible to remove or disable menu in that section.
Thank you.
extension ViewController: CNContactPickerDelegate {
#IBAction func pickerBtnAction(_ sender: Any) {
let contacVC = CNContactPickerViewController()
contacVC.displayedPropertyKeys = [CNContactPostalAddressesKey]
contacVC.hidesBottomBarWhenPushed = true
contacVC.displayedPropertyKeys = [CNContactGivenNameKey, CNContactImageDataAvailableKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactThumbnailImageDataKey, CNContactIdentifierKey];
contacVC.delegate = self
self.present(contacVC, animated: true, completion: nil)
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
if let phone = contactProperty.value as? CNPhoneNumber {
print(phone.stringValue)
}
}
}
let cncontactVC = CNContactViewController()
cncontactVC.allowsActions = false
You need to use instance property allowsActions of CNContactViewController.
From the official docs,
Determines whether to display buttons for actions such as sending a
text message or initiating a FaceTime call.
while creating an object of CNContactViewController,
let contactVC = CNContactViewController()
contactVC.allowsActions = false
There are other useful properties as well ,
shouldShowLinkedContacts
allowsEditing

how can I use a variable from another function swift?

I have a variable by the name of email in the contact picker function. I am trying to use that variable in the IBAction function for the MFMailComposeViewController. I want to apply it to toRecipient. How would I go about using a variable from another function?
import UIKit
import Contacts
import ContactsUI
import MessageUI
class ViewController: UIViewController, CNContactPickerDelegate, MFMailComposeViewControllerDelegate, UITextFieldDelegate {
//Message Setup
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var companyTextField: UITextField!
#IBOutlet weak var lblDetails: UILabel!
#IBAction func btnSelectEmployee(_ sender: Any) {
let entityType = CNEntityType.contacts
let authStatus = CNContactStore.authorizationStatus(for: entityType)
if authStatus == CNAuthorizationStatus.notDetermined {
let contactStore = CNContactStore.init()
contactStore.requestAccess(for: entityType, completionHandler: { (success, nil) in
if success {
self.openContacts()
}
else {
print("Not Authorized")
}
})
}
else if authStatus == CNAuthorizationStatus.authorized {
self.openContacts()
}
}
func openContacts() {
let contactPicker = CNContactPickerViewController.init()
contactPicker.delegate = self
self.present(contactPicker, animated: true, completion: nil)
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
picker.dismiss(animated: true) {
}
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
//When user select any contact
let fullName = "\(contact.givenName) \(contact.familyName)"
var email = "Not Available"
if !contact.emailAddresses.isEmpty {
let emailString = (((contact.emailAddresses[0] as AnyObject).value(forKey: "labelValuePair") as AnyObject).value(forKey: "value"))
email = emailString! as! String
self.lblDetails.text = "\(fullName)\n \(email)"
}
}
//Mail View
#IBAction func sendAction(_ sender: Any) {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self
mailVC.setSubject("Hello. You have a visitor in the lobby.")
let mailContent = "\(nameTextField.text!) from \(companyTextField.text!) is here to see you."
mailVC.setMessageBody(mailContent, isHTML: false)
let toRecipient = "somebody5555555#gmail.com"
mailVC.setToRecipients([toRecipient])
self.present(mailVC, animated: true) {
self.nameTextField.text = ""
self.companyTextField.text = ""
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
nameTextField.resignFirstResponder()
companyTextField.resignFirstResponder()
return true
}
}
Define the var outside the function
var email = "Not Available"
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
//When user select any contact
let fullName = "\(contact.givenName) \(contact.familyName)"
if !contact.emailAddresses.isEmpty {
let emailString = (((contact.emailAddresses[0] as AnyObject).value(forKey: "labelValuePair") as AnyObject).value(forKey: "value"))
email = emailString! as! String
self.lblDetails.text = "\(fullName)\n \(email)"
}
}
Now you can use the variable inside your class wherever you want.
To get more basics, read documentation Apple Documentation

iOS Swift CNContactPickerViewController search contact and add to selection

I am using iOS 9 and Swift 2.2
I have implemented iOS inbuilt CNContactPickerViewController using CNContactPickerDelegate to get the contact numbers,
In the CNContactPickerViewController Screen, when I click on search field on top and search for a name, I need to add that name to my selection but nothing happens after tapping the contact.
I searched everywhere and dint find any solution to this
Do I need to add anything to my code or is it a iOS 9 bug
#IBAction func AddBtnKlkFnc(sender: AnyObject)
{
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
contactPicker.displayedPropertyKeys =
[CNContactPhoneNumbersKey]
self.presentViewController(contactPicker, animated: true, completion: nil)
}
func contactPicker(picker: CNContactPickerViewController, didSelectContacts ContctAryVar: [CNContact])
{
for ContctVar in ContctAryVar
{
let ContctDtlVar = ContctDtlCls()
ContctDtlVar.ManNamVar = CNContactFormatter.stringFromContact(ContctVar, style: .FullName)!
for ContctNumVar: CNLabeledValue in ContctVar.phoneNumbers
{
var MobNumVar = ((ContctNumVar.value as! CNPhoneNumber).valueForKey("digits") as? String)!
if(MobNumVar.Len() > 10)
{
MobNumVar = MobNumVar.GetLstSubSrgFnc(10)
}
ContctDtlVar.MobNumVar = MobNumVar
ContctDtlAryVar.append(ContctDtlVar)
}
}
}
The search results seem to be working in single selection mode only, so make sure you implement
func contactPicker(CNContactPickerViewController, didSelect: CNContact)
only, but not
func contactPicker(CNContactPickerViewController, didSelect: [CNContact])
If you implement both, the version wich takes only one CNContact as argument is ignored and the multi selection mode is used instead.
Use this updated code and
#IBAction func AddBtnKlkFnc(sender: AnyObject)
{
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
contactPicker.displayedPropertyKeys =
[CNContactPhoneNumbersKey]
self.presentViewController(contactPicker, animated: true, completion: nil)
}
func contactPicker(picker: CNContactPickerViewController, didSelectContacts ContctAryVar: [CNContact])
{
for ContctVar in ContctAryVar
{
let ContctDtlVar = ContctDtlCls()
ContctDtlVar.ManNamVar = CNContactFormatter.stringFromContact(ContctVar, style: .FullName)!
for ContctNumVar: CNLabeledValue in ContctVar.phoneNumbers
{
var MobNumVar = ((ContctNumVar.value as! CNPhoneNumber).valueForKey("digits") as? String)!
if(MobNumVar.Len() > 10)
{
MobNumVar = MobNumVar.GetLstSubSrgFnc(10)
}
ContctDtlVar.MobNumVar = MobNumVar
ContctDtlAryVar.append(ContctDtlVar)
}
}
delegate.didFetchContacts([contact])
navigationController?.popViewControllerAnimated(true)
}
Here is a swift 4 version
#IBAction func addPhoneContact(_ sender: UIButton) {
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
contactPicker.displayedPropertyKeys =
[CNContactPhoneNumbersKey]
self.present(contactPicker, animated: true, completion: nil)
}
extension ViewController: CNContactPickerDelegate {
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
picker.dismiss(animated: true, completion: nil)
let name = CNContactFormatter.string(from: contact, style: .fullName)
for number in contact.phoneNumbers {
let mobile = number.value.value(forKey: "digits") as? String
if (mobile?.count)! > 7 {
// your code goes here
}
}
}
}
Multi selection and search are mutually exclusive. If you want search to be working you have to go with single selection only and implement only single selection delegate method.

Resources