Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> BRUCE STADNYK:
Good afternoon!
I am Bruce Stadnyk from the iOS
contacts team and I'm excited
to introduce the new
Contacts Framework to you.
[ Applause ]
>> BRUCE STADNYK: If you are new
to Apple's platforms you
will learn how easy it is
to use contacts in your app.
If you are an experienced
developer with Address Book,
you will love this
new Framework.
So what is this new
Contacts Framework?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So what is this new
Contacts Framework?
Well, we have been listening
to your Address Book feedback
and today we are addressing
the most frequent request.
The Address Book Framework
provides an Objective-C API
to access contacts,
and the API is designed
to work well with Swift
[ Applause ]
>> BRUCE STADNYK: We are as
excited about this as you are.
There are many design goals with
this new Contacts Framework.
I'm going to review a few of
the key ones with you now.
One, to address the
majority of apps
that are mostly get contacts
and do not modify contacts,
we have designed the API for
thread safe read-only usage.
This is done mostly with
immutable value objects
that do not reference
a data store.
This allows you to pass
contacts easily between queues
on your app and not have
unexpected I/O occur on them.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on your app and not have
unexpected I/O occur on them.
Another is to have
the same contacts API
on OS X, iOS and watchOS.
[ Applause ]
>> BRUCE STADNYK: You learn one
API and then use the same code
to access contacts across
multiple Apple platforms.
And, Address Book,
if you are using it,
it is being deprecated.
[ Cheers and applause ]
>> BRUCE STADNYK: We don't
realize how much we take
for granted the contacts
on our devices.
For example when we
receive a phone call
and we only see a phone number,
we ask ourselves: Who is that?
Is it someone from our family?
Is it a friend?
Another telemarketer?
It is a much better experience
when a contact is displayed.
Oh, it's John Apleseed,
one of my best friends.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Oh, it's John Apleseed,
one of my best friends.
So everyone has contacts
on their devices.
They help us to identify who
we are communicating with
and also help us to make
phone calls, send e-mails,
and initiate other
means of communication.
For example, I can speak
to my device and say: Hey,
Siri, call John Appleseed.
Okay. There we go.
>> SIRI: Calling John
Appleseed, iPhone on speaker.
>> BRUCE STADNYK: So
contacts are a central part
of the user experience
on our devices.
So how does the Contacts
Framework handle this
contact information?
Let's take a look at an
example with John Appleseed.
For those of you
experienced with Address Book,
this is a quick review.
Here we have John's profile
picture, his first name,
last name, his home e-mail
address, work e-mail address,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
last name, his home e-mail
address, work e-mail address,
and the phone number
for his iPhone.
These contact properties
represent this
contact information.
The profile image is
represented by image data.
The name is broken
into components.
So the first name is
represented by given name
and the last name
by family name.
Both the home and work e-mail
addresses are represented
by e-mail addresses.
And the phone number is
represented by phone numbers.
There are many more
contact properties.
You can refer to the contacts
documentation for more details.
Now, let's talk more
about this object.
This is the CNContact object.
It is an immutable value
object of contact properties.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It is an immutable value
object of contact properties.
It is modeled like NSDictionary.
It has a mutable
subclass, CNMuteableContact,
that you modify the
contact properties with.
You will see this
pattern throughout the
Contacts Framework.
For contact properties that
can have multiple values
like e-mail addresses,
or phone numbers,
an array of CNLabeledValue
is used.
CNLabeledValue is an immutable
Tuple of label and value,
where the label is a string,
and the value is an object.
Like string for an
e-mail address.
The value can be labeled
to help differentiate it
from the multiple
values for a property,
such as a home e-mail address,
or a work e-mail address.
For those of you
experienced with Address Book,
AB multivalue is replaced by
this array of CNLabeledValue.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
AB multivalue is replaced by
this array of CNLabeledValue.
Yes, you heard that correctly,
AB multivalue is dead.
[ Applause ]
>> BRUCE STADNYK: Now I
would like to take a look
at an example of creating a new
contact with John Appleseed.
So first we import Contacts,
then we create a
mutable contact,
as we are going to
be adding to it.
We then set John's profile
picture as NSData to imageData,
and then we set his name to
givenName and familyName.
Now, for John's two
e-mail addresses,
we create two CNLabeled
values, one with CNLabelHome,
the other with CNLabelWork.
These are some of
the predefined labels
in the Contacts Framework.
These predefined labels
have localized strings
that you can use
in your apps UI.
You can also create
your own custom labels.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You can also create
your own custom labels.
We take these two label values,
home e-mail and work e-mail,
place them in an array, and
set them on e-mail addresses.
It's that simple.
And we can do the same thing
with John's phone number.
Again, we create
a CNLabeled value,
use the iPhone predefined label,
create a CNPhoneNumber object
as the value, place the
label value in array,
and set that to phone numbers.
I also know John's home address,
so I can add that as well.
I create a
CNMuteablePostalAddress,
set the appropriate information,
again create a labeled
value using LabelHome,
place that in array, and set
that to postal addresses.
Finally, I also know
his birthday,
so I can create an
NSDateComponent, set the day,
month, and year component,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and set that to the
birthday property.
Note that all date-related
properties
in the Contacts Framework
are NSDate components.
This allows flexibility
to have dates
such as year-less birthdays,
where you would omit
the year component.
The Contacts Framework is also
able to perform operations
on these contact objects.
Of interest here is
formatting contact data.
The CNContactFormatter will
format a contact's name.
In this example, we're
formatting the full name,
and get back John Appleseed.
CNContact Formatter
will correctly format
international names.
We also have a formatter,
CNPostalAddressFormatter
for formatting mailing
labels for postal addresses.
That returns back...that.
CNPostalAddressFormatter will
correctly format international
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
CNPostalAddressFormatter will
correctly format international
postal addresses.
We recommend that you
use these Formatters
in your app when appropriate.
Now, I would like to
invite Dave up to the stage
to show you how you can
use Contacts in your app.
[ Applause ]
>> DAVE DRIBIN: Thank
you, Bruce.
My name is Dave Dribin, and I
work on the OS X Contacts team.
Sorry. So Bruce just
showed you how to create
and modify CNContact
objects in code.
However, these already
has many contacts as part
of the Contacts app of
OS X, iOS and watchOS.
And Bruce also showed how
system apps can integrate
with these contacts to provide
a richer user experience.
For example, the phone app
can show the person's name
and photo instead of
just a phone number,
for an incoming phone call.
The Contacts Framework
allows you to provide
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The Contacts Framework
allows you to provide
that same rich experience
for your app.
So the class you
are going to use
to access a user's contacts
is called CNContactStore.
We are going to talk
about how to fetch
and save users' contacts.
Let's start off with fetching.
The main method you are going
to use is called unifiedContactsMatchingPredicate,
keysToFetch.
This is going to return an
array of CNContact objects.
The predicate in Keys to
Fetch are there in order
to help your app fetch as
efficiently as possible.
Let's start off with
the predicate.
The user might have hundreds,
or even thousands of contacts.
You might only be interested
in a small subset of those.
The predicate allows you to, ah,
helps you limit the
number of results returned.
Now, for those that don't know,
NSPredicate is a standard
foundation-level object
that presents criteria
to be matched
against an object
while searching.
The Contacts Framework
provides predicates you can use
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The Contacts Framework
provides predicates you can use
with the contact store.
And the contacts store
will evaluate these
against the predicate,
or the contact.
The example here is predicate
ForContactsMatchingName.
And this will match
each of the contacts
against the specified name,
in this case Appleseed.
Let's see a quick example.
Say the user has
these three contacts.
John Appleseed, Jane
Appleseed, and Craig Bromley.
The contacts store can
effectively evaluate this
predicate [John Appleseed]
against each
of the contacts [Jane
Appleseed; Craig Bromley]
and only return the
matching ones.
In this case John
and Jane are going
to be returned, but not Craig.
So the predicate allows
you to limit the number
of contacts returned,
but there's still a lot
of contact information
on a contact,
and you might only be interested
in a small subset of that,
and that's where
keysToFetch comes in.
KeysToFetch is an
array of strings.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
KeysToFetch is an
array of strings.
And these are strings of keys,
in the key-value-coding sense.
So if you are just interested in
the given name and family name,
you could set up your
keysToFetch like this.
Of course, using string
literals is error prone.
We provide constants for this.
And what the Contact Store is
going to do is it's only going
to fetch the properties
that you specify.
In this case, given
name and family name.
So you can really
see how the predicate
in keysToFetch allows you
to scope down the amount
of information to returned
so that your app can be
as efficient as possible.
Let's go over a full example.
So here we are going to
start off with a predicate
and keysToFetch as
we had earlier,
but now you need a contacts
store to use these on.
And this is easy, you
can just create one
with the default initializer.
Then you call unified
ContactsMatchingPredicate,
keysToFetch with those values,
and assuming this works,
you're going to get back
an array of contacts,
and then you can use
them however you want.
Here we are just going to print
out the given name
and family name.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There's a couple of
important things I want
to talk about, about fetching.
The first one is
that the lifetime
of a CNContact is not tied
to the lifetime of
a CNContact store.
There is no need to keep a
strong reference to the Store
around after the
fetch completes.
It means the data on
the CNContact is, ah,
valid and it is from the time of
the fetch, basically a snapshot
from the time of the
fetch, and it is valid
for the lifetime
of that CNContact.
And the second important thing,
is that this is a
synchronous method,
and fetching contacts is a
relatively slow operation.
As such, you should really fetch
these on a background queue,
to keep your user
interface responsive.
And as Bruce mentioned earlier,
CNContacts are completely thread
safe, so it's safe to take these
from the background queue, and
move them over to the main queue
to update your user interface.
And another really
good reason to fetch
on a background queue
is data privacy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, users take the privacy of
their contacts very seriously.
And as such, we want to put
up a barrier between your app,
and the users contacts.
The very first time your
app accesses contacts
through the API, the OS is
going to show a dialogue box
or alert asking the user
to allow or deny access.
You have probably
seen these before.
This means that the first
time you call a method
on the contacts store, it
cannot provide the results
until the user responds,
and that can be a long time.
So while you can move your
contacts store access off
to a background queue
using GCD or NS operation,
we provide a helper method here,
an async method called
request access
for entity type completion
handler.
Now the user may deny access,
and your app should be able
to handle this gracefully.
And if the user allows access,
be sure to handle this
contact data with care.
Please see the privacy
in your app session
for more information.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I would like to talk a little
bit more about keysToFetch
and how they return
partial contacts.
So as I showed previously,
the keysToFetch allows you
to only fetch the properties
you are interested in.
In this case the given
name and family name.
What if you tried to access a
property you didn't request--
for example a phone number?
It's going to throw
an exception,
because that data is not there,
and we call these partial
contacts because only some
of the properties are available.
Now normally this isn't
a problem if you set
up your keysToFetch properly,as
we did in the previous examples,
but there are times you
are going to get a contact,
and you are unsure
which keysToFetch was
used when it was fetched.
In those cases, you are
going to want to check to see
if the key is available
before accessing the property,
just as you would
check the length
of an array before indexing
into it, to avoid an exception.
Here this example is using the
isKeyAvailable method to see
if the PhoneNumbers
key is available
for accessing the
phone numbers property.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for accessing the
phone numbers property.
You might be thinking
"well, that's great,
but I really wanted to
access those phone numbers."
In those cases, you can
re-fetch the contact
with the additional keysToFetch.
Let's see how that would work.
So, here we are setting
up keysToFetch,
but this time we're using
the phone numbers key,
and using a method called
unifiedContactWithIdentifier.
Now each contact
has a identifier
that uniquely identifies it,
and you can use to re-fetch
at a later point in time.
And then, once you re-fetch,
you can safely access
the phone numbers
on this re-fetched contact.
In partial contacts
it's important
to understand how they work
with the rest of the framework.
Now, the previous example showed
how we fetched the given name
and family name and
then printed them out,
to print the full name, but
that's really not ideal.
We should really use one
of the formatters, sorry,
the CNContact formatter
that Bruce showed earlier,
and the formatter may
access other properties
that you haven't fetched,
like name, prefix, or suffix.
If they're not there, it's
going to throw an exception.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If they're not there, it's
going to throw an exception.
So, we could document all
the keys you need in order
to use the formatter, but that's
rather tedious and error prone.
So we've come up with the
concept of key descriptors.
And key descriptors
represent a set of keys
for a particular operation.
In this case, the formatter
knows which keys it needs
to do its job, so it is
providing the key descriptors
with the descriptor
ForRequiredKeysForStyle method,
and you can include this
directly in your keys to fetch.
And this tells the contact
store all of the properties
that it needs to fetch
for the formatter.
Let's see an example of this.
So in this example, we
want to fetch all contacts
with the name Appleseed,
and we want to print their
full name and e-mail addresses.
We are going to set up the
predicate as we did before,
and set up the keysToFetch
a little bit differently.
We are going to start off with
descriptor for required keys
for style (.FullName).
And this is going to, uh,
allows us to get the full name,
using the Contact
Formatter later on.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
using the Contact
Formatter later on.
You can also include the
CNContact e-mail addresses key,
because we want to print
the e-mail addresses.
You can intermix key descriptors
and CNContact keys
in the same array.
With this setup, you fetch,
just as we did before,
calling unified contacts
matching predicate,
and then with the results,
you can get the full name
with the formatter and
the e-mail addresses.
So one other important point
about fetching is
unified contacts.
You may have the same
contact in multiple accounts.
For example, you might
have a John Appleseed
in your iCloud account with
his work e-mail address
and phone number, but
you might also be friends
with John on Facebook.
And that is going
to have an image,
home e-mail address,
and birthday.
So rather than display two
separate contacts there,
the contacts app is going
to use some heuristics
to link these together, and
display a single contact
with the union of
the information,
and we call these
unified contacts.
And contacts apps
have been doing this
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And contacts apps
have been doing this
for a few releases now.
The Contacts Framework will
return unified contacts
by default, as you may have
guessed from the method names.
So the good news is, that
means that you're going
to get the data back that
the user sees in the app.
The really great thing here is
that these are ordinary
CNContact objects, and they work
and act just like any
other CNContact object.
you can even modify them and
save them and it will just work.
So speaking of saving, let's
go over a few code examples
on how to save contacts.
I'm going to start off
by adding a new contact.
So let's say you've got
a C immutable contact,
and you set it up with data,
as Bruce showed you earlier.
In order to get this into the
user's contacts, you are going
to use what's called a save
request, and you are going
to execute that save
request on a contact store.
The first thing you do is create
a new CNSaveRequest object,
and then you are going
to call add contact,
to container with identifier.
Now we don't have time
to talk about containers,
but the nil container identifier
means the default container,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but the nil container identifier
means the default container,
and, so please refer
to the documentation
for more information.
And save request just marks
a contact for being added.
It doesn't actually
apply this change yet.
For that you need to
call execute SaveRequest
on a contacts store.
Assuming this completes
successfully, this will be added
to the user's contacts.
Updating an existing
contact is very similar,
but now you are going to
be starting off with, say,
an immutable contact that
you got from a fetch.
So, the first thing
you are going to want
to do is create a mutable copy,
and then make any
changes you want.
For example, here we are
adding in a new e-mail address.
Now, it is important
to note that,
when you make a mutable
copy of a partial contact,
only modify the property
that you had fetched.
And just as before, we
need to use a save request.
Create a new save request,
but this time we are going
to use the updateContact method.
And again, this just marks
the contact for being updated,
and doesn't actually
apply those changes
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and doesn't actually
apply those changes
until you call
executeSaveRequest.
And, a couple of important
things about saving.
Number one is that the save
request can include multiple
changes, and when
you execute it,
all of them will be
applied during, um,
when it gets executed.
And the second is that the
save request requires mutable
contacts, so you want
to be careful not
to access these mutable
contacts on another thread,
while a save is in process.
So now that you know
how to fetch
and save a user's contacts,
I'll invite Julien up on stage
to talk about using contacts
in the user interface.
[ Applause ]
>> JULIEN ROBERT:
Thank you, Dave.
Good afternoon.
I'm Julien and I'm an engineer
in the iOS contacts team.
And now that Bruce and
Dave made you experts
in the Contacts Framework, I
will talk about user interface
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the Contacts Framework, I
will talk about user interface
and show you how to pick and
display contacts in your app.
Coming along with Contacts
Framework we have a new UI
Framework called contacts UI.
It is available on both
iOS 9 and OS X El Capitan.
This provides you with two
clusters, first the picker,
which displays a list of
all the users contacts,
to let him selectively
import contact information
into your app.
And secondly, the
contact view controller,
which is used to
display a contact.
In the rest of this
presentation I will talk
about the iOS version
of the classes,
but the OS X counterparts
are pretty similar.
Let's talk about
picking contact first.
The class you use for
that is CNContact Picker
View Controller.
It is a direct replacement
of the class we had
in Address Book UI, AB People
Picker Navigation Controller.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in Address Book UI, AB People
Picker Navigation Controller.
As you can guess by its
name, it is a direct subclass
of UA View Controller but
you still must present it,
and not push it in a
navigation controller.
It is always out of process.
It has the great advantage
of not requiring the user
to let your app access
its contacts.
So when you present
a contact picker,
you will never see the dialogue
that Bruce -- that Dave showed.
One thing that is important is
that the contact picker may
return partial contacts.
For example, if you
set a limited set
of displayed property keys,
you will only get those keys
in the contact that
you get back.
And the behavior of the picker
is defined by two things,
first the delegate method that
you implement, and second,
the predicate that
you set on it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the predicate that
you set on it.
And we are going to talk
about those just after.
But last but not
least the picker must
support multiselection.
[ Applause ]
>> JULIEN ROBERT: Let's talk
about the delegate method first.
So if your app is interested
by a single contact,
and you want have this
familiar look of the picker,
you simply need to implement
the didSelectContact delegate
method, and you get
a CNContact back.
So again, these contacts
may be partial.
If you are interested
in a single property,
you implement a did Select
Contact Property delegate
method, and you get a
CNContactProperty object.
So, this object is actually
from the Contacts Framework
but we haven't seen it
before, so let's take a look.
It is a simple wrapper class
that contains the contact
that was selected, as well
as the key of the property
that was selected by the user.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that was selected by the user.
Its value and potentially,
the identifier,
is the property,
is labeled value.
But you may also be interested
in having multiple contacts.
And you would get
this appearance.
To do that is very simple.
You just implement the
didSelectContact delegate
method, and you get as expected,
an array of sent
contact objects.
This also works for properties,
you can get multiple of them
by implementing the
didSelectContactProperties
delegate method, and get an
array of CNContact properties.
All right.
So now let's talk
about predicates.
Predicates lets you customize
the behavior of the panel
and we have three of them.
The first one is predicate
for enabling contact.
This lets you decide which
contacts are available
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This lets you decide which
contacts are available
to the user and which are not.
If I take the example
that we've seen earlier,
and if you want a user
to only select members
of the Parker family,
for example,
you would create a predicate
where you want to match contacts
with the Parker family name.
You set it as a
predicateForEnablingContact.
And once the picker
is represented,
you can see that you
can only select people
with the Parker family name.
The second one that we have is
predicate ForSelectionOfContact.
This one is evaluated when the
user will tap on the contact.
And if it evaluates to true,
then the contact is
returned to your app.
Otherwise we will
display the contact card.
Similarly for properties
we have predicate
ForSelectionOf Property.
And if it evaluates to true,
the property that is tapped
by the user will be
returned to your app,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by the user will be
returned to your app,
otherwise the default
action will be performed,
such as making a phone call
or creating a new e-mail.
One thing to note is that this
last predicate is evaluated
on [the] CNContactProperty
object,
whereas the first two are
evaluated on CNContact objects.
I would like to point out
that you need to be coherent
between the predicates you set
and the delegate methods
that you implement.
For example, you should only
implement the didSelectContact
delegate method, but set
the predicate for selection
of property predicate, then,
doesn't really make sense,
and you would see a log, and
your predicate will be ignored.
Right, so now that you
know how to pick contacts,
let's talk about
viewing them in your app.
So we now have just one class
to replace the three classes
what we had in Address Book UI.
But you can still get the
behavior that you want
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by using the appropriate
creation method.
The first one is
viewControllerForContact,
which would give you a view
controller that is the same
as in contacts app, iPhone app.
If you want to create
a new contact,
you can use
viewControllerForNewContact,
and you get this View Controller
which is always in editing mode.
And lastly, if you
have a contact that is
from an unknown origin, such
as a vCard, for example,
you use
viewControllerForUnknownContact.
One thing we've added in iOS 9
is this update contact button
which will be displayed
automatically
if there is already a contact
in the user's contact
matching the name
of the contact you
are displaying.
And if the user taps on
it, it will display the UI
to update the existing contact
with the new information.
This Contact View Controller
is now always out of process,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This Contact View Controller
is now always out of process,
and the reason for that is
that we may add some
additional information
such as contacts data
that was found in mail.
And as Dave pointed
out, it is important
to fetch the contacts
using the right keys.
So if you want to display it in
the Contacts View Controller,
it must be fetched using the
descriptorForRequiredKeys
of the Contacts View Controller.
So let's look at a quick
code example on how
to display a contact
from an identifier.
First you would fetch
the contact
with the
descriptorForRequiredKeys
of the Contact View Controller.
Then you create the Contact
View Controller with forContact,
because we want a regular look
at the Contact View Controller.
If you already have a contact
store, you should set it
to the Contacts View Controller
so that we can reuse it.
Set self as a delegate.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Set self as a delegate.
Push the View Controller.
And then in your delegate
method you will get called
when the contact is modified.
Alright, so it is nice
to look at a code sample,
but what is better is [to]
build an app together,
let's do that now.
And the app we are going
to build is called Meow.
And it's an app that lets
you share your emotions
with your friends by
sending them cat sounds.
So if I run the app, you can see
at the top is a mood selector.
And then at the bottom is your
friends list, which is empty
for now, and this is what
we are going to build now.
The app is using an e-mail
address to send the cat sounds.
So what we need is just a
nickname and e-mail address.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So what we need is just a
nickname and e-mail address.
So if I hit save,
nothing happens
because it is not
implemented yet.
So this is what we
are going to do.
When I hit save, this
addBuddy function is called,
and we will implement it now.
First we create a
mutable contact
because we are going
to modify it.
Then we set the nickname to be
the value from the text field.
The emailAddresses
property is set as an array
of a single label value.
We have the value being
from the text field.
And finally we add this new
contact to our list of contacts.
So let's run and see
how it works now.
So I'm going to use prefilled
values, and add Emily
to my friends, OK, my family.
You can notice this
info button here.
And we want to show
the contact view
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And we want to show
the contact view
when we click on
this info button.
So this is what we are
going to implement now.
So when I tap this button, the
showContact function is called.
So let's fill it in.
First we create a Contact View
Controller for the contact
that was, ah, that
we want to show.
Then we set the contacts store
to the one we already have.
And finally, we push
the View Controller.
Let's just try, and okay, we
can see the info for Emily.
Great. But what if we want
to add a contact in our list,
that is already in
the user's contacts?
So this is what we
are going to do here.
And for that we are going
to use the contact picker.
So this function is called
when I tap on addFromContacts.
And I am going to
implement it now.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And I am going to
implement it now.
First thing I'm doing is create
the contact picker controller.
Then since we are only
interested in e-mail addresses,
I limit the properties
to e-mail addresses.
Then we want, ah, to, the user
to select contacts who have only
at least one e-mail address.
We see this is the part of the
predicate that is doing that.
And who are not already
in our list of friends,
because we don't
want duplicates.
Finally, is the user,
is contact already,
[who] has exactly one e-mail
address, we don't need
to push the [card?], We want
it to be written directly.
This is what this
predicate is doing.
We set ourself as a delegate and
we present the View Controller.
We also need delegate methods
where we add the new contact
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We also need delegate methods
where we add the new contact
to our list of contacts.
So it is straight now.
And if I select John Appleseed,
I know he has two e-mails.
So the contact card is pushed.
I would select one e-mail,
and we have John in our list.
If I add a new contact,
first you can see
that now John Appleseed
is unavailable
because he is already
in my friends list.
But if I select David, he
just has one e-mail address.
So the contact is
returned directly.
So what happens if I want
to show the card for John?
So we have an exception.
And the reason is that
because we were not careful
when presenting the
contact here,
because the contact picker
returned the partial contacts,
it is missing some keys for
the Contact View Controller.
So we are going to fix
that by first checking
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So we are going to fix
that by first checking
if the contact has the
required keys to be used
in the Contact View Controller.
And if he does, we just use
the code that we had before.
But if he does not, we
will first ask the Store
to request access for contacts.
If the user grants access,
we are going to have
to re-fetch the contact
using its identifier.
And this time using the
keysToFetch, the descriptor
for recorded keys of the
CNContactViewController.
Then we call the same
function again but this time
with our complete
contacts ready to be used.
So let's try again.
So if I view the
contact card for Emily,
we have no dialogue showing
up because Emily
was created in code.
So she has all the fetch keys.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So she has all the fetch keys.
If I do the same on John,
I re-fetch is necessary.
So at this point your app will
access the user's contact.
So we say okay, and we can see
all the contact information
for John.
All right.
So this concludes our demo.
Which makes me a bit sad.
So I will share my
emotions with John.
(Sound of cat crying.)
[ Applause ]
>> JULIEN ROBERT: And
I will call Bruce back
to conclude this presentation.
>> BRUCE STADNYK: Thank
you, Dave and Julien
for showing us how to
use contacts in our apps.
So you now have an Objective-C
API to access contacts,
and it works well with Swift.
The Contacts API is the same
across multiple Apple platforms.
Address book is being
deprecated,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Address book is being
deprecated,
so start adopting now.
For more information you
can refer to the contacts
in ContactsUI Framework
References
on the developer library.
For technical support you can
go to the Developer Forums,
and for general inquiries,
Paul loves e-mail,
so you can e-mail him, and he's
our app Frameworks evangelist.
Thank you
[ Applause ]