project/Kuring(공지알림)

[iOS] Spotlight (SearchAPI)

lgvv 2023. 10. 11. 02:31

Spotlight

 

Kuring 1.4.3 버전에 해당 기능을 도입했습니다.

 

현재 public 레포지토리로 kuring-v2를 작업하고 있어서, 해당 기능을  v1 레포에서 작업했습니다.

Kuring은 app과 sdk로 이루어져 있는데, 해당 기능은 app단 작업만으로도 가능했습니다.

 

목차

 - 개발환경

 - 작동화면

 - kuring에 적용한 코드 설명

 - 개발 이슈 정리사항

 

개발 환경

Xcode 15.0

iOS 16.0 +

SwiftUI

 

🎉 벌써 프로젝트가 2년이 넘었네요!! 118번째 PR


🌟 쿠링 새로운 기능 🌟

https://kuring.notion.site/kuring/iOS-eef51c986b7f4320b97424df3f4a5e3c

 

Core Spotlight

 

 

 

작동화면 GIF

 

 

 

 

 

Core Spotlight 중 Search 기능을 사용하기 위해서는 2가지로 나뉩니다.

 - 1. 검색 가능하도록 등록

 - 2. 검색한 아이템을 클릭해 들어왔을 때의 처리


# 1. 검색 가능하도록 등록

 

기존 쿠링 북마크 등록 로직을 활용했습니다.

- 북마크 액션이 들어오면 인덱싱 가능하도록 아이템 추가

 

// MARK: - Bookmark
    private func updateSpotlight(notice: Notice) {
        let attributeSet = CSSearchableItemAttributeSet(contentType: .text) // 1. ✅ 컨텐츠 타입은 텍스트로! (성능 향상? 추측)
        
        attributeSet.displayName = notice.subject // 2. ✅ 검색 화면에 보여질 부분 - 공지 제목
        if !notice.tags.isEmpty {
        // 3. ✅ 컨텐츠 설명에 보여질 부분 - 태그를 해시태그처럼 분자열 재조합
            attributeSet.contentDescription = "#" + notice.tags.joined(separator: " #")
        }
        // 4. ✅ 썸네일 이미지
        attributeSet.thumbnailData = UIImage(systemName: "AppIcon")?.pngData()
        
		// 5. ✅ 검색 가능한 아이템으로 변경
        let searchableItem = CSSearchableItem(
            uniqueIdentifier: notice.id, // 5-1. ✅ 나중에 검색할 때 사용하는 유니크 한 아이디 지정
            domainIdentifier: Kuring.appleID, // 5-2. ✅ 도메인 아이디 즉, 그룹화 가능
            attributeSet: attributeSet // 5-3. ✅ 위에서 지정해준 속성 넣어주기
        )
        
        // 6. ✅ 검색 가능한 아이템 인덱싱 하여 등록!
        CSSearchableIndex.default().indexSearchableItems([searchableItem]) { error in
            if let error = error {
                Logger.debug("인덱싱 실패 \(error.localizedDescription)")
            } else {
                Logger.debug("인덱싱 성공 \(searchableItem)")
            }
        }
    }

 

검색 가능하도록 등록했습니다.

 

searchItem이 여러개인 경우에는 아래와 같이 표현됩니다.

아이템이 여러개인 경우

 

등록이 가능하면 당연히 삭제도 가능합니다.

func deleteSearchableItems(
    withDomainIdentifiers domainIdentifiers: [String],
    completionHandler: ((Error?) -> Void)? = nil
)

func deleteAllSearchableItems(completionHandler: ((Error?) -> Void)? = nil)

func deleteSearchableItems(
    withIdentifiers identifiers: [String],
    completionHandler: ((Error?) -> Void)? = nil
)

 

하지만 이번 로직에서는 삭제를 별도로 처리하지 않았습니다.

 

해당 인덱스 아이템을 삭제하지 않더라도 한번 인덱싱 된 부분에 수정이 일어나지 않는다면 만료기간이 30일이라 자동으로 정리됩니다.

 

 

# 2. 검색한 아이템을 클릭했을 때의 처리

struct KuringApp: App { 
    var body: some View { 
        ...
    }
    .onContinueUserActivity(CSSearchableItemActionType, perform: onContinueUserActivity)

    /// core spotlihgt search api
    private func onContinueUserActivity(_ userActivity: NSUserActivity) { 
        // ✅ 1. search를 사용하므로 해당 타입의 액션인지 체크
        if userActivity.activityType == CSSearchableItemActionType { 
            // ✅ 2. 아까 지정한 유니크 한 키 값을 가져오기
            if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
                
                // ✅ 3. 앱 서비스 로직
                let notices = Kuring.noticeBookmark // 로컬 디비에서 북마크 노티스를 가져와서
                    .filter { $0.id == identifier } // 해당 아이디 값을 찾고
                    
                if let notice = notices.first {
                    newNotice = notice // 웹뷰를 열 수 있도록 처리
                }
            }
        }
    }
}

 

홈에서 아이템을 검색해서 들어오면 해당 로직을 타게 됩니다.

 

아까 지정했던 유니크한 아이디 값을 기반으로 해당 데이터를 찾아서 사용하게 됩니다.

 

 

 

# 개발 이슈 정리사항

 - 검색 결과가 노출되지 않는 현상에 대한 고민

깃헙 내 PR로 대체

 

 - CSSearchbleItem에 Notice struct를 Data로 변환해서 넣어주려는 시도

    - 이건 결국은 id값을 준 다음 그걸 찾는 방식으로 해결했음. 열린 시각으로 바라보면 손쉽게 문제를 해결할 수 있는 더 나은 방법이 존재하니까 개발에 과몰입 할 때는 잠깐 쉬어주자!! 

 

- 애플의 검색 결과가 매우 우수하다.
   - 💡 유의어를 포함하여 검색이 가능한데, 애플의 알고리즘을 활용해 우리 검색부분에 이를 적용하면 매우 좋지 않을까?

 

 

 

 

(공식문서)
https://developer.apple.com/documentation/corespotlight

 

Core Spotlight | Apple Developer Documentation

Index your app so users can search the content from Spotlight and Safari.

developer.apple.com

 

 

(참고)
https://betterprogramming.pub/implement-core-spotlight-in-a-swiftui-app-859cb703f55d

 

Implement Core Spotlight in a SwiftUI App

Part 1 of 2

betterprogramming.pub