소개

아래와 같이 홈 스크린에서 앱 아이콘을 길게 누르거나 혹은 포스 터치(세게 누름; 일부 모델만 지원)를 한 경우 메뉴가 뜨는데 이걸 퀵 액션(Quick Action)이라고 합니다.

퀵 액션에는 두 가지 종류가 있습니다.

  1. Static – 앱의 상황과 관계 없이 동일한 역할을 수행하며, 앱을 설치하자마자 바로 이용할 수 있는 메뉴입니다.
    • 예) 검색 기능, 새로운 메모 추가 기능 등
  2. Dynamic – 앱의 상황에 따라 이름과 역할이 바뀔 수 있는 메뉴이며, 최소한 앱이 실행된 이후에 이용할 수 있는 메뉴입니다.
    • 예) 최근 파일 열기, 최근 검색 결과로 이동

 

아래는 모 음악 앱의 퀵 액션 메뉴인데 첫 번째 [검색하기]는 어느 상황에서도 검색 메뉴로 이동하므로 Static 퀵 액션이며, 그 아래 3개 메뉴는 최근 감상한 음악에 따라 키워드에 따라 이름과 이동 페이지가 달라지므로 Dynamic 퀵 액션입니다.

퀵 액션 (홈 스크린 바로가기 메뉴)

 

최근 감상 이력이 있는 [Johan de Meij: …] 메뉴를 클릭한 경우 해당 앨범의 페이지로 이동 (Dynamic Quick Action)

퀵 액션은 Static과 Dynamic을 합쳐 최대 4개까지 추가할 수 있습니다.

 

구현

새로운 iOS App 프로젝트를 생성합니다. 이 포스트는 스토리보드(Interface Builder)를 기준으로 설명합니다.

 

Static 퀵 액션

Static 퀵 액션은 앱을 설치하자마자 바로 이용할 수 있고, info.plist 파일에 메뉴 정보를 추가하는 방식입니다.

info.plist 파일을 열고 아래와 같이 트리를 구성합니다.

  • UIApplicationShortcutItemType (필수) – Icon Type 
    • 퀵 액션을 식별하는 고유한 문자열입니다.
    • 예) com.mediumproject.helloWorld.
  • UIApplicationShortcutItemTitle (필수) – Title
    • 사용자에게 표시되는 퀵 액션의 제목입니다.
  • UIApplicationShortcutItemSubtitle (선택 사항) – Subtitle
    • 빠른 작업의 부제입니다. 제목 밑에 표시됩니다.
  • UIApplicationShortcutItemIconType (선택 사항) – Icon Type
    • 여기에서 Apple에서 만든 미리 정의된 아이콘을 설정할 수 있습니다.
    • 이름은 UIApplicationShortcutIconType+이름입니다. 예를 들면 search 아이콘을 사용하고자 한다면 UIApplicationShortcutIconType<span style="text-decoration: underline;">Search</span> 라고 적으면 됩니다.
  • UIApplicationShortcutItemIconFile (선택 사항) – Icon File
    • 사용자 지정 아이콘을 설정하는 데 사용합니다.
    • 파일은 Assets 폴더에 추가된 이름으로 지정합니다.
    • Icon Type과 Icon File 둘 중 하나만 사용할 수 있습니다.
  • UIApplicationShortcutItemUserInfo (선택 사항) – User Info Dictionary
    • 사용자 지정 데이터를 저장할 사전(dictionary)입니다.

 

참고 – 소스 코드로 추가하기

위와 같은 트리를 구성하려면 시간이 걸리므로 아래 XML 코드를 복사 붙여넣기 해서 추가할 수도 있습니다.

 

1) info.plist 파일을 마우스 오른쪽 클릭한 후 Open As > Source Code를 선택해 소스 편집기로 엽니다.

2) 아래 XML 코드를 <dict> 아래에 복붙합니다.

<key>UIApplicationShortcutItems</key>
<array>
  <dict>
    <key>UIApplicationShortcutItemIconFile</key>
    <string>Custom-Punched</string>
    <key>UIApplicationShortcutItemSubtitle</key>
    <string>아티스트 - 노래 제목</string>
    <key>UIApplicationShortcutItemTitle</key>
    <string>Favorites</string>
    <key>UIApplicationShortcutItemType</key>
    <string>com.mycompany.myapp.openfavorites</string>
    <key>UIApplicationShortcutItemUserInfo</key>
    <dict>
      <key>key1</key>
      <string>value1</string>
    </dict>
  </dict>
  <dict>
    <key>UIApplicationShortcutItemIconType</key>
    <string>UIApplicationShortcutIconTypeCompose</string>
    <key>UIApplicationShortcutItemTitle</key>
    <string>New Message</string>
    <key>UIApplicationShortcutItemType</key>
    <string>com.mycompany.myapp.newmessage</string>
    <key>UIApplicationShortcutItemUserInfo</key>
    <dict>
      <key>key2</key>
      <string>value2</string>
    </dict>
  </dict>
</array>

아래와 같이 구성되었는지 확인합니다.

3) Property List로 다시 열어 Home Screen Shorcut Items가 추가되었는지 확인합니다.

 

앱을 빌드 및 실행합니다. 홈 스크린으로 이동 후 Static 퀵 액션이 추가되었는지 확인합니다.

Animated GIF - Find & Share on GIPHY

 

Dynamic 퀵 액션

Dynamic 퀵 액션은 앱 내부에서 코드로 작성하여 추가할 수 있는 퀵 액션입니다. 예를 들면 검색 결과, 조회 결과가 갱신되었거나 하는 등의 상황에서 메뉴가 갱신되도록 해야 합니다. Swift 코드로 작성합니다.

아래 예제는 UIStepper의 값이 바뀌면, 그 값의 배수를 메뉴 제목에 표시하고 계산 과정을 메뉴 부제에 표시하도록 갱신하는 예제입니다.

Animated GIF - Find & Share on GIPHY

 

뷰 컨트롤러에 다음 코드를 추가합니다.

@IBOutlet weak var lblCount: UILabel!

func setDynamicQuickActions(_ value: Int) {
    let items: [UIApplicationShortcutItem] = (2...5).map { multiple in
        let result = value * multiple
        // userInfo의 값(value)는 NSSecureCoding을 준수해야 함
        let userInfo = [
            "value": NSString(string: "\(value)"),
            "multiple": NSString(string: "\(multiple)"),
            "result": NSString(string: "\(result)"),
            "message": "from dynamic quick action" as NSString
        ]
        
        return UIApplicationShortcutItem(
            type: "com.mycompany.myapp.dynamic",
            localizedTitle: "\(result)",
            localizedSubtitle: "\(value) * \(multiple) = \(result)",
            icon: UIApplicationShortcutIcon(templateImageName: "Custom-Punched"),
            userInfo: userInfo
        )
    }
    
    // 기존 static quick action은 지워지지 않음
    UIApplication.shared.shortcutItems = items
}

@IBAction func stepperActionChangeCount(_ sender: UIStepper) {
    let value = Int(sender.value)
    lblCount.text = "\(value)"
    
    setDynamicQuickActions(value)
}
  • UIApplicationShortcutItem(…)
    • 퀵 액션 메뉴를 생성합니다.
    • type – 고유 식별자 (UIApplicationShortcutItemType) 입니다.
    • localizedTitle – 제목(현지화) 입니다. 현지화 방법은 info.plist 파일 현지화로 가능합니다.
    • localizedSubtitle – 부제(현지화) 입니다.
    • icon – 아이콘 파일로 추가합니다. UIApplicationShortcutIcon 타입이 필요하며 systemImageName(시스템 이미지 아이콘) 또는 tempateImageName(Assets폴더의 이미지 이름)으로 생성할 수 있습니다.
    • userInfo – 사용자 지정 정보입니다.
      • 주의점 – 해당 사전의 값(value)은 NSSecureCoding을 준수하는 타입만 입력 가능합니다.
      • 위의  let userInfo를 보면 값이 전부 NSString 타입으로 되어 있는데 NSStringNSSecureCoding을 준수하기 때문입니다.
      • NSSecureCoding이 필수인 이유를 애플은 다음과 같이 설명하고 있습니다.

        이 속성의 사전에 있는 키와 값은 NSSecureCoding 프로토콜을 준수해야 하며 속성 목록 인코딩이 가능해야 합니다(property-list-encodable). 그렇지 않은 경우 시스템은 퀵 액션을 초기화할 때 런타임 예외를 발생시킵니다.

  • UIApplication.shared.shortcutItems = items
    • [UIApplicationShortcutItem] 배열을 추가하여 앱에 Dynamic 퀵 액션을 추가합니다.
    • 기존에 Static 퀵 액션은 바뀌지 않으며 그 밑에 새로 추가됩니다.
    • 퀵 액션은 총합 4개까지 추가 가능하므로 그 범위를 넘어서는 UIApplicationShortcutItem은 무시됩니다.

 

퀵 액션을 클릭하면 특정 작업이 실행되게 하기

위의 퀵 액션을 클릭하면 앱이 열리긴 하지만 아무런 추가 작업이 실행되지 않습니다. 특정 작업을 하려면 SceneDelegate.swift에서 지정해야 합니다.

SceneDelegate.swift 파일을 열고 SceneDelegate 클래스의 하단에 windowScene(...perfomActionFor...)를 추가합니다.

 

windowScene(...perfomActionFor...) 부분을 아래와 같이 작성합니다.

func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
    if let userInfo = shortcutItem.userInfo {
        print(userInfo)
    }
    
    // 경고창
    let alert = UIAlertController(title: "Quick Actions Tutorial", message: "Quick Action Identifier: \(shortcutItem.type)", preferredStyle: .alert)
    let action = UIAlertAction(title: "OK", style: .default, handler: nil)
    alert.addAction(action)
    
    DispatchQueue.main.async {
        self.window?.rootViewController?.present(alert, animated: true)
    }
    
    // Shortcut Item Type(고유 이름)마다 해야할 작업 작성
    switch shortcutItem.type {
    case "com.mycompany.myapp.openfavorites":
        break
    case "com.mycompany.myapp.newmessage":
        break
    case "com.mycompany.myapp.dynamic":
        if let userInfo = shortcutItem.userInfo {
            let result = Int(userInfo["result"] as! String)!
            let message = userInfo["message"] as! String
            print(result, message)
        }
    default:
        break
    }
}
  • shorcutItem.type
    • 고유 식별자를 String 타입으로 반환합니다.
  • shortcutItem.userInfo
    • userInfo가 있다면 [String: NSSecureCoding]? 타입으로 반환합니다.
  • DispatchQueue.main.async {…}
    • 루트 뷰 컨트롤러(rootViewController)를 찾아 경고창을 표시합니다.
    • UI 관련 부분은 무조건 main 스레드에서 실행되어야 하기 때문에 다른 스레드에서 실행되는것을 방지하기 위해 main 스레드에서 실행되도록 지정합니다.
  • switch shortcutItem.type {…}
    • 고유 식별자로 작업을 분리할 필요가 있는 경우 switch 문으로 분기 실행합니다.
  • case “com.mycompany.myapp.dynamic”:
    • userInfo로부터 result, message 키의 값을 각각 Int, String으로 변환해 표시합니다.

 

[New Message] 퀵 액션을 클릭해서 열면 다음과 같이 경고창이 표시됩니다.

 

또한 임의로 만든 Dynamic 퀵 액션을 클릭해서 열면 콘솔에 다음과 같이 표시됩니다.

 

self.window?.rootViewController? 외에 다른 방식으로 뷰 컨트롤러를 참고할 수 있습니다 아래 참고 링크 중 Add Home Screen Quick actions in Swift and iOS 13(영문)에서

  • UIStoryboard를 이용하여 스토리보드에서 ID를 이용해 뷰 컨트롤러를 가져오고 표시하는 방법
  • 프로그래밍 방식으로 작성된 뷰 컨트롤러 인스턴스를 생성하고 표시하는 방법

을 찾을 수 있습니다.

 

참고

문의 | 코멘트 또는 yoonbumtae@gmail.com


카테고리: Swift


0개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다