Contacts 프레임워크 톺아보기
개요
Contacts 프레임워크 톺아보기
Contacts 프레임워크 문서를 참고해서 사용자의 연락처를 가져와서 테이블뷰를 통해 보여주자.
🔨 사용자의 연락처를 가져와서 보여주기
UI 구성하는 부분은 패스. 코드나 스토리보드로 테이블뷰와 셀 구성을 해주면 된다. 셀의 스타일은 Subtitle
로 지정해줬다. 데이터를 많이 가져와서 보여줄 게 아니라서 간단하게 했다.
그다음으로 Fetching Contacts 문단에 있는 코드를 그대로 따라 하면 다음과 같은 코드를 작성할 수 있다.
override func viewDidLoad() {
super.viewDidLoad()
self.fetchContacts()
}
func fetchContacts() {
let store = CNContactStore()
do {
let predicate = CNContact.predicateForContacts(matchingName: "Appleseed")
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey] as [CNKeyDescriptor]
let contacts =
try store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
print("Fetched contacts: \(contacts)")
} catch {
print("Failed to fetch contact, error: \(error)")
// Handle the error
}
}
실행해보면 위와 같이 오류가 발생하는 걸 볼 수 있다. 왜냐하면 연락처는 개인정보에 해당하기 때문에 사용자에게 권한을 요청받아야 한다.
Contacts 문서 하단에는 관련된 토픽을 볼 수 있는데, 위 문제를 해결하려면 Requesting Authorization to Access Contacts 문서를 보면 된다.
영어만 봐도 눈이 핑 돌아간다. @_@…
앱은 사용자가 권한을 부여할 때까지 연락처 항목에 액세스 할 수 없다고 설명하면서 사용자에게 액세스 권한이 있는지 확인하려면 authorizationStatus(for:)
메서드를 사용하라고 쓰여있다.
조금 더 밑으로 내려서 읽어보면, Info.plist
파일에 NSContactsUsageDescription
키를 추가해야 한다고 쓰여있다. 이 키의 값은 앱이 사용자의 연락처로 수행하는 작업을 설명하는 문자열이라고 한다.
그리고 그 다음으로는 사용자에게 권한 승인 요청을 하는 방법이 쓰여있다. requestAccess(for:completionHandler:)
를 사용하면 된다고 한다. 필요한 정보를 전부 알아냈으니 문제를 문서에서 제시하는 대로 앱을 수정해본다.
먼저 Info.plist
파일에 NSContactsUsageDescription
키를 추가해야 하는데, 이것만 봐선 뭔지 모를 거 같지만 애플에선 친절하게 해당 키가 실제로 무슨 이름을 가졌는지 문서로 제공하고 있다.
Info.plist
에 다음과 같이 추가해준다. 사용자에게 이 권한이 왜 필요한지 설명을 기록해야 한다.
이렇게 한 다음에 바로 연락처에 접근하는 게 아니라 requestAccess(for:completionHandler:)
를 이용해 사용자에게 승인 요청을 한다. 권한이 있으면 연락처에 접근하고, 없으면 사용자에게 권한 요청을 한다.
let store = CNContactStore()
override func viewDidLoad() {
super.viewDidLoad()
self.requestCNContactStoreAccess {
// 승인 요청 성공시 할 작업
fetchContacts()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
func requestCNContactStoreAccess(completion: @escaping () -> Void) {
self.store.requestAccess(for: .contacts) { (granted, error) in
// 에러 발생
if let error = error {
print(error.localizedDescription)
return
}
// 사용자에게 승인 요청 성공시 탈출
if granted {
completion()
}
}
}
이 과정에서 fetchContacts
에 있던 store
객체를 외부로 보냈다.
requestCNContactStoreAccess
메서드를 만들면서 처음으로 탈출 클로저를 사용해봤다. 이렇게 작성하니 코드가 깔끔해져서 가독성이 좋아졌다.
원래는 fetch
와 request
를 같이 하고 있었는데 리팩토링을 해서 메서드를 분리했다.
사용자 승인 요청과 연락처 정보까지 잘 가져오는 것을 확인할 수 있다.
그런데 위 코드는 이름이 "Appleseed"인 사람의 연락처만 갖고 온다. 모든 연락처 정보를 가져올 수 있게 fetchContacts()
메서드를 수정해보자. 그리고 가져온 정보를 CNContact
배열에 저장한다.
let store = CNContactStore()
var contatcs: Array<CNContact> = []
func fetchContacts() {
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
do {
try self.store.enumerateContacts(with: request, usingBlock: { (contact, stopPointer) in
self.contacts.append(contact)
})
} catch {
print("Failed to fetch contact, error: \(error)")
}
}
연락처 정보를 가져올 때 모든 정보를 가져오는 게 아니라 keysToFetch
에 있는 값만 갖고 오기 때문에 필요한 값을 적어줘야 한다.
그리고 진짜 마지막으로.. 가져온 데이터를 테이블뷰를 통해 뿌려준다.
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return contacts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withpermalink: "cellIdentifier", for: indexPath)
let contact = contacts[indexPath.row]
cell.textLabel?.text = contact.familyName
cell.detailTextLabel?.text = contact.givenName
return cell
}
완성!
애플 개발자 문서는 진짜 잘 정리되어있다는 걸 또 한번 느꼈다.