App content를 인덱싱 하기
- import CoreSpotlight
CSSearchableItemAttributeSet
객체를 적절한 itemContentType 과 함께 생성 및 초기화.
- attributeSet이란 검색결과에 나타날 메타데이터를 특정하는 속성의 집합.
- 이때 itemContentType은
kUTTypePNG
나kUTTypeMovie
과 같은 콘텐츠의 uniform type identifier이고, 이를 위해서 MobileCoreServices 임포트 필요
import MobileCoreServices
3. 인덱스하려는 아이템에 대한 속성을 설정. 다음과 같은 속성들을 통해 Spotlight에서 검색 시 나타날 값을 지정할 수 있음
title
: 검색 결과에서 메인으로 표시될 값contentDescription
: 아이템 콘텐츠 설명. 검색 결과에서 title 밑에 부가적으로 뜨는 값thumbnailData
: 검색 결과에서 섬네일로 뜨게 될 데이터
4. 아이템을 나타내는 CSSearchableItem
객체 생성. 해당 객체에 대해 참조 가능한 unique identifier 가짐. 만약 NULL
로 설정하면 자동으로 할당됨
5. 필요하다면 domain identifier 설정을 통해 다수의 아이템들을 그룹으로 묶어 한번에 관리할 수 있게 함
- domain identifier란 아이템의 domain이나 owner을 표시하는 식별자(optional). album 등과 같이 표기함으로써 아이템들을 그룹화할 수 있음.
- 계정이 삭제될 때 관련된 아이템들도 모두 삭제 되기를 원하는 어떤 메일 박스(e.g. naver)가 있을때, domain 식별자는 account-id.mailbox-id 형식이 되어야함. 이때 각각은 온점을 포함하면 안되며
deleteSearchableItems(withDomainIdentifiers:completionHandler:)
를 호출해서 첫번째 파라미터로 전달하면 됨. 아니면, 계정이 삭제될 때 그와 관련된 모든 메일박스(e.g. naver, gmail ..) 아이템을 지우고 싶으면 account-id를 인자로 넘기는 방식으로 해결할 수 있음.
6. attributeset 과 searchable item 을 관련지음(associate)
7. CSSearchableIndex에 searchable item 추가. CSSearchableIndex는 Spotlight에 indexing 되는 콘텐츠와 관련이 있는 클래스. CSSearchableItem의 배열 요구.
Searchable item 삭제하기 (세가지 방법 존재)
- index로 부터 모든 searchable item 삭제
func deleteAllSearchableItems(completionHandler: ((Error?) -> Void)?)
2. 특정 identifier를 지정해서 index로 부터 해당 searchable item 삭제
func deleteSearchableItems(withIdentifiers: [String], completionHandler: ((Error?) -> Void)?)
3. 특정 domain을 지정해서 index로 부터 관련된 searchable item 삭제
func deleteSearchableItems(withDomainIdentifiers: [String], completionHandler: ((Error?) -> Void)?)
Searchable item 수정하기
업데이트를 원하는 아이템의 unique identifier와 함께 CSSearchableItem 객체를 새로 생성하고 아이템을 인덱싱할 때 사용했던 동일한 메소드를 호출.
<예시 코드>
//생성 및 수정
func indexSearchableItems(selectedItem : MySearchableItem){
// 아이템 설명하는 속성 생성
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeData as String)
// 아이템에 대한 디테일 보충하는 metadata 추가
attributeSet.title = selectedItem.title
attributeSet.contentDescription = selectedItem.desc
// unique identifier, domain identifier 그리고 위에서 만든 attribute set과 함께 생성
let item = CSSearchableItem(uniqueIdentifier: selectedItem
.uniqueId, domainIdentifier: selectedItem.type.rawValue, attributeSet: attributeSet)
// on-device index에 item 추가.
CSSearchableIndex.default().indexSearchableItems([item]) { error in
print(error != nil ? error?.localizedDescription ?? "" : "item indexed")
}
}
//특정 아이템 삭제
func deleteSearchableItems(uniqueId : String){
//삭제할때 연관되어있는 identifier 설정 통해서 지우는 것
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: [uniqueId]) { error in
print(error != nil ? error?.localizedDescription ?? "" : "item deleted")
}
}
//전체 아이템 삭제
func deleteAllSearchableItems(){
CSSearchableIndex.default().deleteAllSearchableItems { error in
print(error != nil ? error?.localizedDescription ?? "" : "all item deleted")
}
}
사용자 응답
유저가 Spotlight 검색 결과에서 indexed item 을 탭하면 app delegate의 application:continueUserActivity:restorationHandler:
함수 호출 (위의 NSUserActivity와 동일). 이 상황에서 app delegate가 받는 activity type은 CSSearchableItemActionType
이고 해당 type을 기준으로 app content 보여줄건지 말건지 판단하면 됨.
<예시 코드>
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
//Core Spotlight
if let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
//CSSearchableItemActivityIdentifier를 키값으로해서 uniqueIdentifier 찾을 수 있음
//필요한 동작 수행
}
} else {
//NSUserActivity
}
return true
}
Reference
공식문서
Apple Developer Documentation — Core Spotlight
Apple Developer Documentation — NSUserActivity
Apple Developer Documentation — WKNavigationDelegate
Apple Developer Documentation — App Search Programming Guide
기타 참고 사이트
NSUSERACTIVITY TUTORIAL HOW TO USE IOS 10 SEARCH API
iOS 10 Spotlight App Discovery: NSUserActivity and Search Relevancy
[Stack over Flow]NSUserActivity와 Core Spotlight의 차이