iOS 9 shows many new technologies and optimizations on existing technologies for users and developers. As we can see, there are many new contents that are shown for the first time in this version, and there are also many changes and updates of existing frameworks and classes. In addition, it is always surprising that some old APIs are abandoned and no longer recommended to use, making way for new APIs that are newly developed or used for transition. An example in iOS9 is the new Contacts framework, which replaces the old AddressBook framework with a more popular model, simpler and more direct. Every developer who has used the AddressBook API before can say for sure that it is definitely not an easy-to-use part of the iOS SDK. In general, AddressBook is difficult to understand and master, especially for newcomers. This is all due to historical reasons, and the new Contacts framework is much simpler to understand and use. Contact information can be retrieved, created or updated in a very short time, and the development time related to contacts can be greatly shortened, and changes and modifications can be completed quickly. In the following paragraphs we will highlight the most important parts of the Contacts framework. I will not show too many details because you can find relevant content in Apple's official documentation and WWDC 2015 session 223 video. So, first of all, I will start with the key point, which is user privacy. Users are often asked by an application whether it has the right to access the user's contact information. If the user agrees, then the application can freely interact with the user's contact database. If not, the user prohibits the application from accessing contact information, then this decision must be adopted by the application and it must not interact with the contact database. We will talk about it in more detail in a moment, and then we will see how all possible scenarios can be handled programmatically. Moreover, always remember that users always have the right to change the permissions of applications in the device settings, so you should always check whether your application has the right to access contact information before performing any related tasks. The biggest source of contact data has always been the database on the device. But when an app asks for contact information, the Contacts framework doesn't just look there. It actually searches other sources, like your iCloud account (if you've connected it, of course), and returns a consolidation of the information it gets from each source to the app. This is very useful because you don't have to create separate methods to search for contact information besides searching the device database. You get them all at once, and use them however you want. The Contacts framework has classes that serve various specific purposes. All of them are important, but there is one that is used the most, called CNContactStore. This class represents the contacts database programmatically and provides many methods to implement different tasks, such as retrieving, saving or updating records, permission checking and permission requests, and many more. A single contact record is represented by the CNContact class, but remember that the nature of this class is immutable. If you want to create a new contact record or update an existing one, you must use the CNMutableContact class. Note that when working with the Contacts framework, especially when retrieving contact information, you should always run these tasks in a background thread. If a contact information retrieval task takes too much time and is run on the main thread, your application may become unresponsive, which will eventually lead to a very poor interactive experience. When importing contacts into your app, it's rare that all of a contact's attributes are needed. Fetching all of the contact data from all of the data sources that the Contacts framework would search can prove to be a very resource-intensive process, so you should avoid doing so unless you're sure you'll actually need every piece of data, even the last one. Fortunately, the Contacts framework provides methods for fetching partial results, meaning only a subset of a contact's attribute values instead of all of them. For example, you can request just the first or last name, home address, home phone number, and so on, saving a lot of resources by excluding the data you don't need. In addition to all the ways that the Contacts framework provides to get contact information programmatically, it also provides some default UI that can be used in conjunction with your application to directly and visually access contact information. The provided UI is basically the same as the Contacts application, which means there is a contact picker view controller along with a details card that can be used to get contacts and properties (which can be customized to a certain level), and a contacts view controller that can be used to display contact details and implement certain actions (for example, making a call). The details of the above mentioned will be seen later in this guide. Once again, visit the official documentation to get more information about what I have shown or what I will show. Let's now see what the demo application will look like, and then we will learn about the classes of the Contacts framework. You will find that interacting with this new framework is very simple and fun. Demo APP Quick Tour Through this tutorial's demo application, I'm going to show you as much as I can about this new framework. In fact, in the next sections I'm going to show you how to:
I named this demo app Birthdays because its purpose is to display the birthdays of all the contacts introduced into the app. The contact's full name, photo (if any), and home email address will also be displayed. Ideally, this app should be a birthday reminder, of course, we will not handle notifications, SMS sending, and other related actions. This application is based on the navigation pattern and it consists of the following parts: When the app starts, the default ViewController is shown. It displays information about all the imported contacts I mentioned earlier, and provides actions to get more contact information (top right button), create a new contact (top left button), and view contact details by tapping a row. The contact details will be displayed in an embedded contact view controller. As you will see, you can display all properties or select only the ones you are interested in. Getting the contact information will be an interesting part later on. I will show you 3 different ways to do it.
Here’s the picker view controller, showing only the collection of contacts that have valid birthdays: The last part of the application is about creating a new contact. This is a very simple task, thanks to this demo application we will use the following view controller to enter the name, home email address and birthday of the contact to be created (we will not deal with the image here, because it is not very important at the moment). The demo data (demo contacts) of this demo app will be the default contacts saved in the simulator database. These contacts are sufficient and good enough for our purposes. Of course, you can use the contacts in your device or add new contacts to the simulator. By default, the simulator contacts do not include images, but you can easily find images in the image library and add them. As usual, download the starter project that we will use later as a starting point. Once it has downloaded, open it and browse through the files we have added. Once you are ready, you can start the next part. Contact Store Class One of the most basic classes you'll always use when working with contacts is the CNContactStore class. This class actually represents the contact database that exists on the device, and is responsible for managing all interactions between your app and this actual database. Furthermore, it manages all the work involved in fetching, saving, and updating contact and group records. In short, it's the starting point for most interactions with contact information, as you'll see in the code that's about to be written. In addition, as I mentioned in the introduction, user privacy is an important part of iOS, so be careful when dealing with this aspect. It is generally known that users can choose to allow or deny third-party applications to use contact information, so it is very important to ensure that whenever you implement contact-related tasks, your app is authorized to do so. Using the CNContactStore class, you can check the current authorization status of your application. Always remember that users can disable your application from accessing contact data at any time through Settings, even if your application may be allowed to access it at the beginning, so it is important to ensure that your tasks are operational and of course guide your application in the right direction for each different situation. Situations that are not handled will eventually lead to a bad user experience, which is exactly what you must avoid. In this guide, we will strictly consider the authorization situation of the demo application, even starting from this part. What we are about to do is to use it freely in your project as long as you want. You will immediately see that in the following scenario (as opposed to the other scenarios) the contacts store class is required:
With this in mind, we initialize a CNContactStore object, and we'll use it throughout the class. On the other hand, we could also create a new object whenever we want to use it, but since this class represents the contacts database in the code, why would we need multiple instances of it? So, let's get started. First open the AppDelegate.swift file, initialize and declare a CNContactStore property. At the top of the file, add the following code:
The following frame must be added at the top of the class declaration:
Great! Now, before we deal with the authorization state of the application and what we can do with it, let's write two simple convenience methods. Note that they are not required to proceed with this project, and we can do our work without them. However, implementing some small methods that accomplish a certain purpose can prove to be very convenient. Therefore, the first small method is to simply access the method of the application delegate class (AppDelegate) from any other class. Usually, the following code can be used to access the application delegate:
However, I personally find that writing all of the above code every time I need to get the app delegate feels like an interruption. What if we write the following class method?
Through it, we can access any property or method of the app delegate in a simpler way. For example, we can get the contacts store property in any class in the project as shown below:
The second convenience method we will add to the file is a controller that displays a tooltip. The tooltip variable is passed as a parameter. It is not complicated to implement, but there is a special point to note; a tooltip controller must be displayed by a view controller, and the app delegate of the application is not a view controller. To solve this problem, we need to find the top view controller on the current app window and then display the prompt controller on this view controller. Here is how to do it:
Now we have to do something important, which is to handle the authorization status of the application. This status is represented by the CNAuthorizationStatus enumeration value and belongs to the CNContactStore class. It contains the following 4 values:
#p# One thing you must be clear about is that after installing the app, the first time (and only the first time) the user tries to interact with the contact data (for example, to get contact information), iOS will display a predefined prompt controller to ask the user to authorize the app: If the user allows access, everything is fine. However, if the user denies access, then all functions based on contact information cannot be performed. In our demo application, and in this specific scenario, we will display a custom prompt message (using the method we have implemented above) to tell the user that he must set the permission to access contact information in Settings. We will handle this situation in the new method that we will implement soon. Of course, we will also consider all possible authorization states in that method. First, let's take a look at what this method looks like, and we will explain it more later:
Looking at the method above, you'll see that it contains a completion handler that returns true if the application is granted access, and false otherwise. Some states are simple, like Authorized or Restricted, and it's pretty clear what the completion handler should be for those states. However, what's interesting is that both the Denied and NotDetermined state values are handled in the same case, and for both of them the requestAccessForEntityType:completionHandler: method is called, letting the application request access. For just the Denied case, the custom message I mentioned earlier will be displayed. Note that both the requestAccessForEntityType:completionHandler: and authorizationStatusForEntityType:methods methods require a CNEntityType parameter. It is an enumeration that contains only one value called Contacts. This enumeration actually specifies the entity we are requesting access to. The above method will be used several times from now on, and will be used in the next section. It will be used every time we want to perform an operation on a contact, so that we know whether our operation can continue, and of course handle every possible situation to avoid a bad user experience. Now everything seems good, because we have prepared some reusable code, which will prove to be very convenient as we go further. Using Predicates to get contact information As I mentioned in the introduction of this tutorial, we will use 3 different methods to retrieve contact information. One of them is to enter part or all of the name (either first or last name) of the contact we want to retrieve in a text input box and then get the result from the Contacts framework. We start here, and the key method to implement it is the unifiedContactsMatchingPredicate:keysToFetch:error: method. This method is part of the CNContactStore class and requires two important parameters:
Note: This method can return an exception, so it must be called in a do-catch statement using the try keyword. Error conditions are handled by the catch statement. The unifiedContactsMatchingPredicate:keysToFetch:error: method returns an array of all CNContact objects that match the given predicate variable, or nil if an error occurs. With that in mind, it’s time to move on to the implementation steps. This time open the AddContactViewController.swift file and go directly to the top of the file. Import the Contacts framework here, without it you won’t be able to do anything.
Now we find the textFieldShouldReturn: delegate method. We start by using the last method we created in the application delegate so that we can check if the app has permission to access contacts in order to continue with the following work:
If it is authorized, we can prepare the predicate and keys for matching contacts. Along with them, we also declare some other variables: an array variable to store the results (if any), and a string variable to store a custom message to be displayed when there are no matching contact results or the fetch operation fails.
Notice here how we specify the predicate and the keys array before we move on. In the next step, we will try to fetch the contact data, and if the operation is successful, the contacts array we created at the beginning will be filled with the returned results. If no contact information is found or the fetch operation fails, we will then display a custom message; with this, the implementation code in this method is basically complete.
As you can see, we haven't implemented anything in the else method, but we will revisit it and add the missing code shortly. The most important thing to understand in this section is how we get the contact information that matches the given name, and what we do if we don't get the expected one. Display the obtained contacts In the best case, our fetch operation can return matching contact information, and then it is necessary to display them in the tableview of the ViewController. However, the first step is to tell the ViewController that the contact information has been fetched, and all this happens in AddContactViewController. The best and simplest implementation process is to use the well-known Delegate pattern. Therefore, we continue to implement it in this way to fill in the connection points in the application. In AddContactViewController.swift, create the following protocol above the class with one method:
By using the above delegate method, we can not only let the ViewController class know that the contact information has been obtained, but also pass the newly obtained contact information through it. Then, add the following delegate declaration to AddContactViewController:
Recall that we left the else section at the end of the textFieldShouldReturn: method blank. Now is the time to add the missing code. In fact, there are only two lines of code missing: one is our call to the delegate method just declared above, and the second is to pop the view controller from the navigation controller.
As you can see, we use the main thread when processing operations involving UI. This is a very important detail that you should not forget, otherwise the UI will not be updated at the right time and you will experience some unexpected behavior of your app. Now it’s time to move on to the ViewController.swift file to process the obtained contact information. First, we must also import the Contacts framework in this class.
Next, we need to accept the newly created custom protocol, so we need to add the name of the protocol after the class name:
Now, it is necessary to declare an array of CNContact objects. This array will hold all the contacts obtained from the fetch request, and it will be the datasource for our tableview. Therefore, add the following code at the top of the ViewController:
We also have to update the number of rows that the tableview will display, as follows:
Before we implement the previously declared delegate method, we must declare that the ViewController class is the delegate of the AddContactViewControllerDelegate protocol. This will be implemented in the prepareForSegue: method:
Finally, we have to implement our custom delegate method. In it we will get all the returned contacts one by one and add them to the contacts array. Finally, we will reload the tableview to show the latest contact information.
Now let's display the contact information. In each cell, the contact's first and last name will be displayed, if there is a birth date, it will be displayed, if not, a short message will be displayed, if there is an image and home email, they will also be displayed. The implementation code you will see below will be changed a little bit later, but it is enough for you to understand how the attribute information of a contact is obtained. So, let's take a look:
Let's go through the steps above. First, we set the contact's full name by concatenating the first and last name. I'll show you another way to get the full name later, but use this one for now. Next, we set the birthday information. If there is birthday information, we use the simplest method to display it. Note that this is only a temporary solution, and we will use a more appropriate method to construct the birthday date later. Also, it is very important to understand that the birthday information is not an NSDate object. Instead, it is an NSDateComponents object, which of course can be converted to an NSDate object and then converted to a String object. Next we need to set the image data. If it doesn't exist, you will see a background color of imgContactImage in its place that I added in the xib file of the custom cell. Finally, we are left with the home email address. You can see that we use a loop to iterate through all the email addresses until we find the one we want. This is done because the emailAddresses property of the contact contains all the existing CNLabeledValue objects with the email address as labeled values. If you run the app now, the contact information will be selected according to the name you enter. The above implementation code may work, but it may not work. In the second case, the app will crash, but don't worry. We will improve it later. I deliberately did not show you the final implementation of the above function code, just because it is clearer to show you how everything works in this way. Re-acquire contacts The reason your app might crash is that not all the contact attributes you need to access are available. Because of this, the CNContact class contains a method called isKeyAvailable: that must be called before you access any contact attributes. For example, we should add the following check method before trying to display the birthday date, picture, and email address:
If a key is not found, the necessary action is to retrieve the contact information and display it again. This is what we are going to do now, and in detail we are going to create a new method in ViewController. However, before we do that, let's patch up the implementation of displaying contact details by adding the isKeyAvailable: method. In fact, instead of adding three different methods for the above properties, we will create only one method to check if the property does not exist, just in case something is missing, we will call a method that we will implement to retrieve the contact information. I deliberately did not mention the keyword contact name, because you will see more about it in the later parts.
The method called above is what we are going to implement right now. Apart from that, I think the condition we added is pretty straightforward and you should be able to understand its logic. Note that with this change, the app will no longer crash, even when the result contains unavailable key information. #p# Now let's look at the new method:
First we check whether the application is authorized to access the contact database. Then we specify part of the key value information of the contact we want to obtain, and then re-get the contact information given. Note that this time we use a new method to do this, which is the unifiedContactWithIdentifier:keysToFetch: method. Its purpose is to obtain a specific contact data that satisfies the identifier parameter value. Once this result is returned, we use this new contact information to replace the old information in the array. Finally, we reload the specific row of the tableview. If you want, you can try running this app again. In case some contact information is not obtained, it is best to always do the operation of re-acquire the contact information, so that your application will not show any "surprise" to the user. Format output results As you can see now, we did not do any suitable format conversion before displaying each contact’s birthday information on the cell. We just link and show the birthday date attributes, and now after learning the important previous knowledge, it’s time to deal with this issue. We will patch the birthday date by creating a new custom method in the ViewController. In this method, we will use an NSDateFormatter object to convert the date information into a localized string, but first, we must convert the date component (that is, the date part) into an NSDate object. Let's take a look at this new method:
The parameter of the above method is a date represented by the NSDateComponents object (in our case the date of birth object). The return value is of course a string. To convert the dateComponents object to an NSDate object, more than one line of code is not used. We use the NSCalendar class to perform this conversion, and the date object is ready to be processed by the date formatter to be initialized. Setting the current geographic information for the date formatter is a necessary operation because it can obtain a localized date information. Finally, we set a preferred format for the date object (not too long or too short), and then we perform the final conversion operation. The converted value is finally returned to the caller. Now we can fix the display method of date information, just simply call the above function:
Very good. Now the birthday date will be shown in a more beautiful and fashionable way. Now let's look at the aspects of showing last name and name. The CNContact class provides an embedded format converter that helps us easily format two types of data: the contact's full name information (CNContactFormatter) and the address (CNPostalAddressFormatter). Now we will use the first one so that the contact's full name will be automatically formatted by the Contacts framework. Let's show the last time we change the contacts as follows:
As you can see, the code of this line cell.lblFullname.text = "(currentContact.givenName) (currentContact.familyName)" has been replaced with the following:
Obviously, we no longer need to manually link the last name and name to create the full name of the contact. CNContactFormatter did it for us and it outputs a localized string (by setting the name part in the appropriate order, relying on the localization of the device). However, the above operations will cause some complex operations because the contact formatter needs to access all keywords related to a contact name, even those keywords that we do not specify in the fetch array. However, we do not need to write them down one by one. All related keywords are specified by a key descriptor, which replaces all individual keywords specified in the keyword array. To make this more specific, find the AddContactViewController file, in the textFieldShouldReturn: method, put the following code:
Replace the following code using key descriptor
The way this descriptor is organized is very specific, as shown above. Apart from this, other keyword information remains the same. The above changes must be executed in the refetchContact: method (in ViewController). All you have to do is to use the above line of code to replace the definition of the keyword array, and do it:
Through these processing, we have made all format-related changes in the code. Of course, you can still use a single keyword to get single name information, which always depends on your requirements. Use custom filters to get contacts One of the first things I showed in this tutorial is how to use predicts to get contact information. We use a predicate from the Contacts framework to get a contact that matches a given name, but if you remember, there is a general disadvantage of this method; we must only use the framework's embedded predicates and we cannot use our own predicates. The question now is, how can we get contacts by using custom filters? This question is more specific for our demo application, so we can ask ourselves, how can we get contact information based on the contact's birthday month? In the AddContactViewController, there is a picker view that displays all the months. What we want to do now is to select a month, click the Done button, and finally get the same contact with the birthday month and the selected month in the record. As you may have guessed, there is a solution to "add" a custom filter, but the whole process is a little more manual than using predictates. Overall, the processing method we are going to see is based on the enumerateContactsWithFetchRequest(_:usingBlock) method recommended by Apple to use the CNContactStore class in this case. This method gets all the contact information, so the custom standard can be set in the block part (or closure) by comparing property values or applying any other custom logic, and ultimately retain the part of the contact information you really need. In our case, two things need to be checked: First, we must ensure that the birthday date of each contact is set to avoid unwanted crashes. Second, we just compare the birthday month with the month selected in the picker view, and if there is a matching name, we keep the contact record in the array. This is quite simple, because the birthday date is displayed by the NSDateComponents object, so we can directly access the month information. More than that, the rest is simpler: everything we want to see is already displayed in the previous section, and I have already explained it. Here we will only write new code in the performanceDoneItemTap custom method of the AddContactViewController file, because we want to get the contact record based on the month selection only when the Done button in the view controller is pressed. Please see:
As you can see, at the end we call delegate so that the new contact information can be updated in the tableview in the ViewController, and then we pop up the view controller. The above code can be useful to you in many cases, because what you want to do is simply change the filtering standard parameters in the above block. Contact Picker View Controller All our contact management and the work we are doing is programmed, but the story doesn't end here. The Contacts framework provides view controllers (UI) to access contact information directly and visually and interact with it immediately. These provided view controllers are similar to Contacts applications, a picker controller is provided to you to select a contact record (or many contact records), a view controller to view contact details, and a form to edit information. Rewriting the default behavior when selecting a contact is allowed, and there is a delegate method for you to handle the results you get by yourself. In this section we will show you how picker view controller is used to select and import contact records into your own app. There is not much preparation to do, but the level of customization depends on each app's own needs. Contact frameowrk allows setting three optional predicates, allowing you to limit the display of contacts and change default behavior.
Here we will only use the first predictate and request it from the picker view controller. This picker view controller only allows contacts with birthday dates to be available. It is not difficult to use the other two, but we do not need them here; for you to give you a reference, I recommend you to read the relevant documentation. Go back to our application and open the AddContactViewController.swift file. Find the top and introduce the ContactsUI framework:
Next, use the CNContactPickerDelegate protocol so that we can handle the returned contact record:
From now on, our work will be performed in the showContacts: IBAction method. This method will allow the button at the bottom of the AddContactViewController to operate. Let's look at the implementation code:
It's that simple! In this demo application, we can't display the detailed card information when clicking on a contact. But if you want to do this in your application, it's easy to control the value of the property to be displayed in the detailed interface. All you have to do is declare an array of keywords for the properties you want to display in a property called displayedPropertyKeys. For example, if we want to display the details in the application, we need to add the following line of code before displaying the picker view controller:
We used the CNContactPickerDelegate protocol before, and now we need to implement a necessary delegate method. In this method, we will get the selected contact record, and then we will return these records to the ViewController through our customized delegate method.
If you want to show contact details and want to process a returned property value, then you need to use contactPicker:didSelectContactProperty: this delegate method. We won't implement it here because we don't need it. You can find a collection of all delegate methods here. You can verify this application again. This time, use the "Open contacts to select" button to display the picker view controller. You will find that there is no valid birthday date in the contact. Select a contact and you will see that the contact will be displayed in the tableview in the ViewController. Contacts View Controller So far we have implemented 3 ways to allow you to get contact information and add them to our app. However, it is definitely not good to just show them in a tableview; we have asked for more, requiring a selected contact in a new view controller. Actually, we won't create a custom view controller, but we will use the contact view controller provided by the Contacts framework. Using it, we can not only view the contact data, but also edit the data. Of course, this method is implemented by CNContactViewController. We go back to the ViewController.swift file and then handle the situation when the user clicks on a contact. Before we present a CNContactViewController object, we must make sure that all keywords corresponding to the selected contact details are available. Although we check whether the keyword is available when presenting each row and if necessary, we will even re-get the contact, we still cannot 100% guarantee that the user may re-get the contact when clicking on a certain row. Therefore, this operation must be done. : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : #p# Also, the keyword array used to obtain contact information throughout this demo application has proved to be very convenient to use. Not in the check availability I just explained, but in the design of which properties should be displayed in the contacts view controller. You can see how it is used in the implementation code below. An additional tip, remember that if you ignore this property, then all the property values of contacts (not just those we want to display) will be displayed by the contacts view controller. Having said so much, let’s take a look at the corresponding code:
In the above code snippet, we can see that we simply use the displayedPropertyKeys property of the contacts view controller instance object to specify the property value to be displayed. Another detail is that we use the contactStore property to provide the contact ?store instance to the contacts view controller. If there is no existing CNContactStore instance in the application, this operation is not forced, because the CNContactsViewController will automatically create a new one. The others have been discussed. Finally, don't forget to introduce the following framework at the top of the file:
Create and save a new contact Now we have seen a lot of new features about this new contact framework. However, there is another part that has not been discussed yet, which is how to create a new contact information in the code and store it in the database. So, as you know, we will cover this aspect in this part. I will not spend too much effort on how to update an existing contact record, because this is very similar to the task we will see soon, so I leave this part to you to find the difference between these two tasks. In addition to the CNContact class that displays a single contact record and all its properties, the Contacts framework provides another class called CNMutableContact. As the name of this class is expressed, it is very similar to the first class; however, this class allows reassigning a contact property to create a new or updated an existing contact record. The real save (update) operation is handled by the contact store (CNContactStore) class we are familiar with, but this is the last step in creating a new contact. You will see the detailed steps immediately. Generally speaking, setting attribute values for a contact record using the CNMutableContact class contains the opposite operation when obtaining attribute values. This means that special attributes must perform special operations than just assigning a value directly to a simple attribute (for example, name). For example: When setting a birthday date for a contact record, an NSDateComponents object must be created and appropriately assigned to the corresponding attribute. When setting up a contact image, an NSData object must be assigned to it. When setting an email address, a CNLabeledValue object must be created for each individual email address, and then all of these objects must be formed into an array to assign to the emailAddresses property. The above are just some examples. Of course there are more contact attributes that need to be treated with care, but no matter what, it won't be difficult to do these operations as you're going to see. Go back to our demo application, this time we have to switch to the CreateContactViewController.swift file. Here, you will find an empty custom method called createContact(). All we have to do next is written here. Simple, we create a new instance of CNMutableContact, then we assign values to all the attributes of interest, and finally we use the contact store to store the new record in the database. Let's take a look at the implementation code:
The first step is to initialize a CNMutableContact object that will be used later. Obviously, setting the last name and name attribute is very simple. The next home email address attribute must be created as a CNLabeledValue object, and the creation method has been shown above. Once the new email address is created, it is assigned to the emailAddresses attribute as part of an email address array. Of course, there is no other address in our example. Finally, we set the birthday date for the new contact based on the date selected by the user. As shown above, using the NSCalendar class to create an NSDateComponents object from an NSDate object is very simple. Please note how calendar units (year, month, day) are combined to form a final ideal attribute value. The most interesting part of the given code snippet is how a new contact record is stored. As you may have noticed, it is necessary to create a CNSaveRequest object first and then add the new contact object to it. There is no substantial storage operation until then. The storage operation will happen soon, which is when the executeSaveRequest: method of the contact store object is called When a new contact cannot be stored, a warning message will be displayed to the user. Run the application and use the navigation bar button to the left of ViewController to create a new contact. Save your record and search for it using any of the methods we mentioned earlier. Important: I noticed that when I was writing this tutorial, in my tests, when creating a new contact record and storing it to the contact database, it is no longer allowed to access the contact details through the application (by clicking on a contact). The following message appears on the debug panel: [CNUI ERROR] error calling service – Couldn't communicate with a helper application. There is no useful help on the website at the moment, just submit this to Apple as a bug. Remember this, avoid creating a contact record at the same time when you want to test the application. Summarize By the end of this tutorial, I hope I have made you clear how easy it is to deal with the new contact framework Contactsframework. If you have used the AddressBook API before, you can confirm that the method you see here when dealing with contacts is a huge change. You can use this demo app as much as possible and expand it in any way you want. It always has room for improvement. But don't forget the user privacy settings and have to respect the user's choice of allowing this app to access contact information. Don't miss the official documentation, you'll find a lot of interesting things there. I hope you enjoy this tutorial and find it useful; see you next time, I hope you have a good time! |
<<: How to choose your first programming language based on what you want in life
>>: 11 Essential HTML5 Animation Tools
"At the beginning of the year, the industry&...
(Photo source: China Meteorological News Agency) ...
According to statistics and analysis by the China...
The recent local epidemic in Shanghai has become t...
With the continuous efforts of domestic automaker...
Introduction to the resources of the Wuwei Invest...
There are thousands of problems for newcomers in ...
Creativity is actually a kind of logical thinking ...
"One good copy is worth 100 sales experts.&q...
When you wash your face and brush your teeth in t...
With the fast-paced life, insomnia has become a c...
How has Zha Ge learned the values of socialism ...
The official release of LG G3 has attracted a lot ...
Before answering this question, let's take a ...