티스토리 뷰


출처

https://developer.apple.com/documentation/contacts



개요

iOS 9 이상에서 지원되는 Contact Framework 로
대부분이 앱이 Contact 편집보다는 조회를 많이 하고 있기에,
thread-safe 와 read-only 사용에 optimize 되어있다.




Contact Object

CNContact 은 thread-safe 하고 imuutable 한 
contact properties (name, image, phonenumber) 를 가지고있다.


CNContact 을 상속받은 CNMutableContact 은 mutable 한 속성을 가지고 있으며

contact properties 를 수정할 수 있다.


contact properties 는 phone numbers, email, 

CNLabeledValue 를 가진 array 와 같은 multiple values 를 가진다.


Label 는 thread-safe, immutable tuple 속성을 가지며

home, work 와 같이 각각의 value 를 가리킨다.


Create Contact Example

fileprivate func saveContact() -> Bool {

    let contact = CNMutableContact()

    contact.givenName = "John"

    contact.familyName = "Appleseed"

        

    let homeEmail = CNLabeledValue(label:CNLabelHome,

                                   value:"john@example.com" as NSString)

    let workEmail = CNLabeledValue(label:CNLabelWork,

                                       value:"j.appleseed@icloud.com" as NSString)

    contact.emailAddresses = [homeEmail, workEmail]

        

    let phoneNumberInfo = CNLabeledValue(label:CNLabelPhoneNumberiPhone,

                                         value:CNPhoneNumber(stringValue:"(408) 555-0126"))

    contact.phoneNumbers = [ phoneNumberInfo ]

        

    let homeAddress = CNMutablePostalAddress()

    homeAddress.street = "1 Infinite Loop"

    homeAddress.city = "Cupertino"

    homeAddress.state = "CA"

    homeAddress.postalCode = "95014"

    contact.postalAddresses = [CNLabeledValue(label:CNLabelHome, value:homeAddress)]

        

    var birthday = DateComponents()

    birthday.day = 1

    birthday.month = 4

    birthday.year = 1988

    contact.birthday = birthday

        

    let store = CNContactStore()

    let saveRequest = CNSaveRequest()

    saveRequest.add(contact, toContainerWithIdentifier: nil)

        

    var result = false

    do {

        try store.execute(saveRequest)

        result = true

    } catch let error {

        print("error occured : \(error.localizedDescription)")

    }

        

    return result

}






Formatting and Localization

Contact Framework 는 contact 정보에 대한 
formatting 과 localize 를 지원한다.

contact name formatting 은 CNContactFormatter 로
international postal address 는 
CNPostalAddressFormatter 를 이용하면된다.

Name and postal address Formating Example

let contact = CNMutableContact()

contact.givenName = "John"

contact.familyName = "Appleseed"

        

let fullName = CNContactFormatter.string(from: contact, style: .fullName)

print(fullName ?? "")

// John Appleseed

        

let homeAddress = CNMutablePostalAddress()

homeAddress.street = "1 Infinite Loop"

homeAddress.city = "Cupertino"

homeAddress.state = "CA"

homeAddress.postalCode = "95014"

contact.postalAddresses = [CNLabeledValue(label:CNLabelHome, value:homeAddress)]

        

let postalString = CNPostalAddressFormatter.string(from: homeAddress,

                                                   style: .mailingAddress)

print(postalString)

// 1 Infinite Loop

// Cupertino

// CA

// 95014




CNContact 의 localizedString(forKey:) 와 

CNLabeledValue.localizedString(forLabel:) 등을 이용하여

device 의 current locale setting 에 따른 localizing 을 지원할 수 있다.

Localizing a given name Example

// device locale is Spanish

let displayName = CNContact.localizedString(forKey: CNContactNicknameKey)

print(displayName)

// alias

        

let displayLabel = CNLabeledValue<NSString>.localizedString(forLabel: CNLabelHome)

print(displayLabel)

// casa





Fetching Contact

CNContactStore 를 이용해서 contact 을 fetch 할 수 있다.
CNContactStore 는 모든 I/O operation 을 가지고 있고,
contact 과 group 의 fetching, saving 을 담당한다.

CNContactStore 의 method 들은 모두 synchronous 하기 때문에
background thread 에서 작업하는 것이 좋다.


필요하면 immutable 한 fetch result 를 안전하게 main thread 로

callBack 을 줄 수 있다.



Contact framework 는 contact 을 fetch 하기위해

predefined 된 predicates 와 keysToFetch property 와 같은

몇 가지 방법을 제공한다.


contact 을 filtering 하기 위한 몇가지 predicate 중 

하나는 이름을 통한 검색이 있다.

predicateForContacts(matchingName:) 을 이용하면 된다.

포괄적이거나 compound predicates 는 제공되지 않는다.



keysToFetch 는 가져오는 contact properties 를 지정할 수 있다.



Fetch Example 

fetch 도중 error 가 발생했다면 return 값은 nil 로,
조건에 맞는 contact 이 없었다면 empty array 로
값이 반환된다.

let store = CNContactStore()

        

var results: [CNContact]?

do {

    let fetchPropertes = [CNContactGivenNameKey, CNContactFamilyNameKey]

    let predicate = CNContact.predicateForContacts(matchingName: "Appleseed")

    results = try store.unifiedContacts(matching: predicate,

                                        keysToFetch: fetchPropertes as [CNKeyDescriptor])

} catch let error {

    print("error occured! : \(error.localizedDescription)")

}

print(results ?? "")




Fetch Example Using Key Descriptor

Fetch 결과에 formatting 된 결과를 받을수도 있다.

let keysToFetch = [CNContactEmailAddressesKey as CNKeyDescriptor,

                   CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]





Privacy

기본적으로 App 마다 권한을 키고 끌 수 있는데,
권한체크 없이 CNContactStore 의 어떤 call 이라도 불린다면
user 에게 grant / deny 여부를 물어보고, app 은 block 된다.

권한 요청 시 최초 1번 system alert 이 노출되고
이후 grant/deny 여부는 CNContactStore 의 requestAccess 를 통해 알 수 있다.

iOS 10 부터 plist 에 NSContactsUsageDescription 을 반드시 등록해줘야한다.
해당 description은 권한 요청 시 system alert 에 message 와 함께 노출된다.

권한 요청은 아래와같이 한다.

let store = CNContactStore()

store.requestAccess(for: .contacts) { (result, error) in


}




Partial Contacts

partial contact 은 contact store 로부터 
fetch 된 몇가지 property value 만 가진 것을 말한다.

모든 fetched contact 은 partial contacts 이다.
만약 fetch 되지 않은 다른 property 를 접근한다면
exception 이 발생할 수 있다.

contact 에서 fetch 되었는지 안되었는지 알 수 없다면
해당 property 에 접근할 수 있는지 
isKeyAvailable, areKeysAvailable 을 이용해서 확인할 수 있다.

if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {

    print("\(contact.phoneNumbers)")

} else {

    //Refetch the keys

    let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]

    let refetchedContact: CNContact?

            

    do {

        refetchedContact = try store.unifiedContact(withIdentifier: contact.identifier,

                                                    keysToFetch: keysToFetch as [CNKeyDescriptor])

    } catch let error {

        print("error occured! :\(error.localizedDescription)")

    }

}





Unified Contacts

다른 계정에 있는 Contacts 은 자동으로 link 되어
같은 사람처럼 보일 수 있다. (unified contact)
Unified Contact 은 merge 되어 하나의 contact 처럼 보이는 
임시적인 View 이다.



기본적으로 Contact framework 는 unified contacts 를 반환한다.
fetch 된 unified contact 은 묶인 연락처들의 identifier 가 아닌
unique 한 identifier 를 가진다.




Saving Contacts

CNContactStore 은 modify contact 도 지원한다.
CNSaveRequest 는 save 뿐만 아니라
여러개 contact 과 group  의 변경사항도 single operation 으로 저장할 수 있다.

하나의 saveRequest 에 모든 contact 변경사항을 add 한 뒤 execute 할 수 있다.
saveRequest 에 있는 contact 들은 변경 중이기 때문에 
save 가 실행되는 중에 접근해서는 안된다.


Save New Contact Example

// Creating a new contact

let newContact = CNMutableContact()

newContact.givenName = "John"

newContact.familyName = "Appleseed"

        

// Saving contact

let saveRequest = CNSaveRequest()

saveRequest.add(newContact, toContainerWithIdentifier: nil)

do {

    try store.execute(saveRequest)

} catch let error {

    print("\(error.localizedDescription)")

}



Saving a Modified Contact

let mutableContact = contact.mutableCopy() as! CNMutableContact

let newEmail = CNLabeledValue(label: CNLabelHome, value: "john@example.com" as NSString)

mutableContact.emailAddresses.append(newEmail)

        

let saveRequest = CNSaveRequest()

saveRequest.update(mutableContact)

        

do {

    try store.execute(saveRequest)

} catch let error {

    print("\(error.localizedDescription)")

}




Contact Changed Notifications

Save 에 성공한 이후 contact store 는 
CNContactStoreDidChange notification 을 posting 한다.

만약 Contacts framework 의 object 를 cache 하고 있었다면
identifier 를 사용하거나 원래 사용했던 predicate 를 이용해서
객체를 다시 가져온 다음 캐시된 객체를 release 해줘야한다.
cache 된 객체는 invalid 하지는 않지만 stale (이전 상태) 이다.




Containers and Groups

User 는 device 의 local account 나
동기화를 위한 server account (google, icloud) 를 가지고 있다.

각각의 계정은 최소 하나 이상의 contact container 를 가지고 있다.
연락처는 하나의 container 에 속할 수 있다.





groupd 은 container 내부에서의 연락처 set 이다.
모든 계정이 group 을 지원하지는 않고 
몇몇 계정은 subgroups 도 지원한다.

iCloud 계정은 하나의 container 를 가지고 많은 group 을 가질 수 있지만
subgroup 은 지원하지 않는다.
반면, Exchange 계정은 group 을 지원하지는 않지만
Exchange Folder 로 표현되는 여러개의 container 를 가지고 있다.












공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함