[UIActivityViewController] 우리 앱에 "공유하기" 기능 (Share Sheet) 구현하는 방법

2024. 12. 23. 16:15UIKit, SwiftUI, H.I.G

지난번 글은 외부 앱에서 하단 툴바에 있는 "공유하기" 버튼을 눌렀을 때, 우리의 앱이 연결되도록 하는 Share Extension 내용을 다뤄봤는데요.
이번 글은 우리의 앱에서 외부 앱으로 공유를 가능하도록 하는 Activity View(= Share Sheet)를 만드는 방법에 대해 알아보고자 합니다.

2차, 3차 스프린트를 거치며 우리 앱 <토스터 TOASTER>와 다른 앱 간의 상호작용이 더욱 쉬워지도록 개선되는 일환이라고 생각하면 좋겠네요!

 

[Share Extension] 다른 앱의 "공유하기" 버튼에 우리 앱을 설정하고 싶다면? (feat. NSExtensionActivationRule)

[Chore] #209 - 사파리 외에 다른 앱에서도 Share Extension 대응 by mini-min · Pull Request #210 · Link-MIND/TOASTER✨ 해결한 이슈 Resolved: [Chore] ShareExtension 사파리 제외한 타앱에서도 대응 #209 🛠️ 작업내용 1. N

mini-min-dev.tistory.com

 

1️⃣ iOS 개발자 관점에서 HIG 살펴보기 : Activity View (Share Sheet)란?

 

Activity views | Apple Developer Documentation

An activity view — often called a share sheet — presents a range of tasks that people can perform in the current context.

developer.apple.com


흔히 Share Sheet라고 부르는 화면의 Apple 정식 명칭은 Activity View입니다.
주로 공유하기를 목적으로 사용되는 화면이고, 사용자의 인터렉션을 획기적으로 줄여줄 수 있다는 장점 (복사하고 - 다른 앱에 접속해서 - 붙여넣고 - 전송하는 인터렉션을 해당 화면에서 클릭 한 번으로 수행할 수 있죠!)이 있어 많이 애용되고 있어요!

Share Sheet라고 익숙하게 부르는 이유는 보통 공유 버튼을 눌렀을 때 하단에서 올라오는 시트 형태의 화면이라서 그런 듯해요.
*공유 버튼 (Share Button)과 Activity View는 서로 이어지는 화면입니다. 이는 HIG에서도 명시되어 있는 사항이에요!
**이것을 다르게 말하면, Share Button이 아닌 다른 버튼을 통해 Activity View가 이어지는 상황은 적합한 화면 흐름이 아니라는 의미겠네요!

하지만, 사실 이 화면의 역할은 공유 뿐만 아니라 채팅 전달 (messaging), 복사 (copying), 인쇄 (print) 등 매우 다양하고
화면 또한 기기에 따라 시트(sheet)가 아닌 팝오버(popover) 형태로 표출되는 경우도 존재하기 때문에 Activity View라고 부르는 것이 더 적합하다고 볼 수 있을 것 같습니다.
*그렇다고 Apple이 Share Sheet라는 용어를 사용하지 않는 것은 아니에요. Apple도 병행해서 사용하고 있답니다 ^__^

HIG 문서에 나와있는 Activity View 이미지와 이번 스프린트에 반영된 Activity View 화면 부분


HIG에서는 Activity View에 대한 몇 가지 가이드라인을 제공하고 있습니다.

눈여겨봐야 할 것 같은 몇 가지 부분만 간략하게 정리해 볼게요!

  • Activity View에서 사용할 수 있는 작업을 중복되도록 만들지 마세요.
    -> 예를 들어, Activity View에서 print 기능을 지원하고 있음에도 / 별도의 버튼을 만들어 print 작업을 수행할 수 있도록 만드는 경우를 지양하라는 의미네요.

  • Activity View에 포함되어 있는 활동(Activity)은 현재 상황에 적합하도록 구성하세요.
    -> 앱 내에서 인쇄 작업과 관련 없는 경우, 인쇄 활동 (print activity)는 제외시켜도 좋다는 의미겠죠?

  • 확장 프로그램 위에 추가적인 모달(modal)의 배치는 피하도록 하세요. (Avoid placing a modal view above your extension.)

  • Activity View에서의 작업이 오래 걸리는 경우, 메인 앱 상에서 긴 작업의 진행 상황을 표시하는 방법을 고려하세요.
    -> Activity View는 작업이 완료되어야 사라지는 것이 일반적이지만, 작업이 오래 걸리는 경우에는 시트를 내리고 백그라운드에서 작업을 수행하도록 권장합니다. 그리고 작업이 끝나는 경우에 이를 메인앱에서 표출시키는 방법 (프로그레스바, 토스트 메시지 등을 고려할 수 있겠네요!)을 고민하라고 명시했습니다.
    -> 단, 작업 종료를 알리기 위한 목적으로만 알림 (Notification)을 사용하는 것을 지양하라고 합니다!

 

 

2️⃣ UIActivityController 알아보기, 실제 present 코드 작성하기

위에서 길게 설명했던 Activity View를 구현할 수 있도록 Apple은 UIActivityViewController라는 객체를 지원합니다.

우리는 이 UIActivityViewController 객체를 각 상황 필요에 맞춰 구성하고, 모달(modal)로 present 해주면 되는 거죠!
*단, 아이패드에서는 해당 뷰 컨트롤러를 팝오버로 표시하도록 소개하고 있네요. (On iPad, you must present the view controller in a popover.)

 

UIActivityViewController | Apple Developer Documentation

A view controller that you use to offer standard services from your app.

developer.apple.com


UIActivityViewController는 초기화 시, 두 개의 파라미터(activityItems, applicationActivities)를 받도록 구성되어 있습니다.

  • activityItems: [Any] : 공유하고자 하는 아이템을 담고 있는 배열. 최소 1개 이상의 Any 객체가 담겨야 합니다. (공유하고자 하는 아이템은 이미지가 될 수도, URL이 될 수도, 파일이 될 수도, 그냥 일반 텍스트가 될 수도 있겠죠?)
  • applicationActivities: [UIActivity]? : 애플리케이션이 지원할 활동 (UIActivity)을 담고 있는 배열. nil이 default 값입니다. (이메일이나 메시지, 파일 앱과의 연결, AirDrop 등의 작업이 들어갈 수 있겠네요!)


그래서 막상 "웹 URL 링크를 다른 앱으로 공유가능하도록 하는 것"을 목적으로 하는 이번 코드는 매우 간단했습니다.

activityItem은 현재 웹 뷰에서 표출되고 있는 웹 URL 링크를 담고,
별도의 applicationActivities에 담을 작업은 존재하지 않았기 때문에
그저 UIActivityViewController 객체를 만들고 위 두 파라미터를 담아서 present만 시켜주는 매우 간단한 작업이었죠. 끝입니다!

guard let url = self.webView.url else { return }  // 현재 표출되고 있는 웹뷰의 url 
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)

 

 

3️⃣ UIActivityController 추가 - excludedActivityTypes, completionWithItemsHandler

추가로 사용하지는 않았지만, UIActivityController가 지원하고 있는 속성과 메서드들도 함께 알아보겠습니다.

excludedActivityTypesActivity View에서 제외하고 싶은 작업을 Array로 지정하는 프로퍼티입니다.
예를 들어, 아래 왼쪽과 같이 기본으로 제공되고 있는 활동(Activity)에 대해 오른쪽과 같이 표출되지 않도록 설정할 수 있는거죠!

activityViewController.excludedActivityTypes = [
    .copyToPasteboard,
    .addToReadingList
]

excludedActivityTypes로 Activity View의 Active를 지울 수 있다.

지울 수 있는 항목은 UIActivity.ActivityType에서 직접 확인할 수 있습니다.
복사하기, 에어드롭과 같은 특정 작업은 물론이고 / 메일, 메시지, 페이스북과 같은 특정 앱에 대한 접근도 포함하고 있다는 점이 흥미롭네요!

궁금하신 분들은 직접 더 확인해보시길!


사용자가 Activity의 작업을 완료한 후, 다르게 말하면 Activity View가 닫힌 이후 실행할 코드가 있는 경우에는 completionWithItemsHandler를 통해 completion 클로저 형태로 작업을 담을 수 있습니다.


해당 클로저는 4가지의 파라미터를 통해 작업의 내용, 결과 등을 확인할 수 있도록 친절하게 지원해주고 있네요!

  • UIActivity.Activity : 해당 Activity View에서 수행한 활동 (Activity)이 무엇인지
  • Bool : Activity View에서 수행한 활동에 대한 성공 여부
  • [Any]? : 수정된 Item을 가져온다. -> 원본 데이터에서 수정된 경우에만 가져오며, 수정이 없는 경우에는 nil로 지정
  • (any Error)? : Activity View에서 수행한 활동이 정상적으로 완료되지 않은 경우의 에러를 가져온다.


직접 URL을 복사하는 작업을 수행했을 때, 아래의 코드를 입력하고 나온 결과물을 확인해 보죠!
activity는 복사 작업 / 성공 여부는 true / 복사를 통해 수정된 Item은 없으니 nil로 / 에러도 없으니 nil로 입력된 것을 확인할 수 있네요.

activityViewController.completionWithItemsHandler = { activity, isSuccess, items, error in
    print("💡", activity, isSuccess, items, error)
}

 

 

4️⃣ UIActivityController 더 공부하면 좋을 내용들!

아래 내용들은 나중에 기회가 되면, 이어서 정리를 해나가겠지만 / 현재 수준에서는 참고만 하면 되는 내용들이라 관련 부분 첨부만 해두겠습니다! ^__^

[Action과 Activity의 Customization과 관련한 내용]

 

Customizing the browser | Apple Developer Documentation

Customize the document browser’s look and behavior.

developer.apple.com

 

Adding custom actions and activities | Apple Developer Documentation

Add custom document browser actions, activities, and bar items.

developer.apple.com


[iCloud Document, CloudKit과의 활성화 관련]

 

Collaborating and sharing copies of your data | Apple Developer Documentation

Share data and collaborate with people from your app.

developer.apple.com


[UIActivityItemsConfiguration : UIContextMenu, UIMenu 등에서 데이터 공유를 더욱 심화적으로 요구하는 경우]

 

UIActivityItemsConfiguration | Apple Developer Documentation

A configuration that allows a responder to export data through a variety of interactions.

developer.apple.com