Airdrop 구현 방법을 알아보자
- UIKit
- Swift
🍚 에어드랍(AirDrop)에 대해

에어드랍(Airdrop) 은 Apple 기기 간 직접적인 무선 파일 공유 기능이다.
아이폰, 아이패드, 맥 기기에서 사진이나 동영상, 문서 혹은 앱 데이터 등을 WiFi와 Bluetooth로 1m 이내 기기에 초고속으로 전송하는 기능이다.
에어드롭은 무선이라는 특징을 가지고 있으며 케이블이나 인터넷 연결 없이 근처에 있는 Apple 기기라면 파일을 즉시 전송할 수 있다.
동작은 쉽게 설명하자면 Bluetooth로 기기를 발견한 뒤 WiFi로 데이터를 전송하는 흐름이다. (속도는 최대 30 ~50MB/s 까지 나온다고 한다.)
주요 특징을 아래 표에 정리해보았다.
| 특징 | 설명 |
|---|---|
| 지원 기기 | iOS 7+, iPadOS, macOS Yosemite+ |
| 필요 조건 | WiFi/Bluetooth ON, 개인 핫스팟 OFF |
| 수신 모드 | "받지 않음" / "연락처만" / "모두" |
| 전송 데이터 | 사진, 동영상, PDF, 앱 콘텐츠, 위치 등 |
🤔 Swift로 구현하는 방법
그래서 이 에어드랍을 코드로 어떻게 구현해야할까?
💡 Apple 개발자 공식 문서엔 직접적인
AirDrop클래스와 API가 존재하지 않으며공유 시스템의 일부로 다뤄진다고 한다.
에어드랍은 독립적인 API가 아니라 UIActivityViewController가 띄우는 공유 시트에서 자동으로 나타나는 옵션이다.
즉, UIActivityViewController나 SwifUI의 ShareLink 같은 공유 메커니즘 안에 포함된 Activity 형태로 제공되며 에어드랍만 콕 찝어서 선택하거나.. 곧바로 근처 디바이스 리스트를 보여주는 전용 컨트롤러는 존재하지 않는다..
1. UIKit에서 UIActivityViewController를 직접 Present 하기
Activity views | Apple Developer Documentation
가장 단순하면서도 UIActivityViewController를 사용하는 기본적인 방법이라고 한다.
직접 구현해보자.
class ViewController: UIViewController {
private let shareButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("에어드랍 공유", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 20, weight: .bold)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
...
shareButton.addTarget(self, action: #selector(shareButtonTapped), for: .touchUpInside)
}
@objc private func shareButtonTapped() {
guard let image = UIImage(named: "mong") else { return }
let activityVC = UIActivityViewController(
activityItems: [image],
applicationActivities: nil
)
// iPad 대응
activityVC.popoverPresentationController?.sourceView = shareButton
activityVC.popoverPresentationController?.sourceRect = shareButton.bounds
present(activityVC, animated: true)
}
}
위 코드를 실행해서 버튼을 클릭할 시 아래와 같이 에어드랍 창이 출력되며
AirDrop 버튼을 클릭 시 맥으로 이미지가 잘 넘어옴을 확인할 수 있다.


따로 설정을 넣은 게 없지만
카카오톡이나 _슬랙_에 공유가 가능하다!
한 번 UIActivityController의 생성자를 확인해보자.

activityItems 는 [Any] 타입이다. 이는 공유할 실제 데이터 리스트를 말한다.
→ activityItems는 내부에서 각 데이터의 타입을 보고(UIImage, URL, String, NSAttributedString, UIActivityItemSource 등) 어떤 액티비티에서 처리 가능한지 판단한다.
예를 들어서 아래와 같이 자동으로 처리된다.
UIImage→ 사진, 에어드랍, 메시지 등으로 처리 가능URL→ 사파리, 브라우저, 메신저, 에어드랍 등으로 처리 가능String→ 메모, 메일, 메시지, 복사 및 붙여넣기 등으로 처리 가능커스텀 타입→ 원하는 커스텀 타입을 만들고 싶다면UIActivityItemSource로 감싸서 지원할 수 있다.
이때
UIActivityItemSource는 액티비티 타입에 따라 다른 데이터, 제목, 썸네일을 제공하고 싶을 때 사용하는 프로토콜이다.
그렇다면 applicationActivities 는 뭘까?
→ 이는 UIActivity를 상속할 커스텀 액션들을 넣는 배열이다.
사용자가 시스템 액션(에어드랍이나 메시지, 메일 등을 뜻한다.) 외에 앱 전용 공유, 처리 버튼을 추가할 때 사용한다.
예를 들어서 아래와 같이 추가할 수 있다.
미러링 부스 앱 내부에 저장한다.PDF로 내보낸다.서버로 업로드한다.
한 번 멀티 아이템을 전송시켜보자.
기존 몽이 사진을 드랍하는 코디에 String과 URL을 추가해서 activityItems에 추가한다.
@objc private func shareButtonTapped() {
guard let image = UIImage(named: "mong") else { return }
let name = "mong"
let url = URL(string: "https://naver.com")!
let activityVC = UIActivityViewController(
activityItems: [image, name, url],
applicationActivities: nil
)
// iPad 대응
activityVC.popoverPresentationController?.sourceView = shareButton
activityVC.popoverPresentationController?.sourceRect = shareButton.bounds
present(activityVC, animated: true)
}

이렇게 총 3개의 데이터(String, URL, Image)가 에어드랍으로 전송되는 것을 확인할 수 있다.
🧐 아이패드에서 popover 설정이 필요한 이유
예시 코드에는 아래와 같은 코드가 존재한다.
activityVC.popoverPresentationController?.sourceView = shareButton
activityVC.popoverPresentationController?.sourceRect = shareButton.bounds
이는 아이패드 환경에서 activityVC가 발생될 위치를 지정하는 코드이다.
기본적으로 아이폰과 아이패드의 모달 표시 방식은 서로 상이하다.
- 아이폰의 경우 기본적으로 전체 화면 또는 카드 형태를 가지는 일종의
모달로 출력된다.
→ 이 때문에 별도의 소스가 되는 뷰를 연결하거나 ActivityVC의 위치를 지정하지 않아도 괜찮다.
- 아이패드의 경우 대부분의 모달 표시 방식이
popover로 표시된다. - UIActivityViewController로 기본적으로
popover presentation을 사용하기 때문에어디서부터 팝오버를 띄워야 할 지(화면상의 기준점이라고 이해하자.)시스템에 알려줘야한다. - ⭐️ 그렇지 않으면 런타임 상에서 크래시가 발생한다!
이러한 이유가 존재하여 아이패드에서 activityVC를 안전하게 사용하려면 아래와 같이 공유 화면이 어디에 출력될 지를 지정해줘야한다.
if let popover = activityVC.popoverPresentationController {
popover.sourceView = sender
popover.sourceRect = sender.bounds
}
popoverPresentationController- UIViewController의
presentationController중 하나로 팝 오버를 제어하는 컨트롤러다. - UIActivityController가 아이패드에서 표시될 떄 해당 컨트롤러를 사용하여
여기서 어디에 어떻게 표시할지, 방향은 어떻게 할지 등을 결정한다. sourceView- popover가
어떤 뷰를 기준으로 위치를 잡을지 결정한다. - 일반적으로 버튼(UIButton)이 있다면 그 버튼을 기준점으로 잡거나
self.view와 같이 현재 화면 전체를 기준점으로 잡을 수 있을 것이다. sender에 들어갈 수 있는 건UIView의 인스턴스라면 그 무엇이든 가능하다!
→ 보통 sender가 버튼이라면 그대로 사용하지만 SwiftUI라면 임의의 UIView() 혹은 window의 rootView 등으로 지정한다.
sourceRectsourceView내부에서 어느 좌표 혹은 영역을 기준점으로 잡을지 결정한다.CGRect로 표현되며sender.bounds를 대입하면 “버튼의 전체 사각형을 기준으로 팝 오버가 그 위 혹은 옆에 표시된다는 의미이다.
👀 UIActivityViewController 딥다이브!
UIActivityViewController는 excludedActivityTypes 라는 프로퍼티가 존재한다.

이는 ActivityView에서 제외하고 싶은 작업을 배열로 지정하는 프로퍼티다.
예를 들어 아래와 같이 기본으로 제공되고 있는 활동(Activity)에 대해 표출되지 않도록 설정할 수 있다.
activityViewController.excludedActivityTypes = [
.copyToPasteboard,
.addToReadingList
]

지울 수 있는 항목은 아래 링크에서 확인할 수 있다.
페이스북.. 웨이보.. 트위터.. 신기한게 많이 있다!
UIActivity.ActivityType | Apple Developer Documentation
또한 Activity View가 닫힌 이후 실행하고 싶은 코드가 존재한다면 CompletionWithItemsHandler 를 통해 completion 클로저 형태로 로직을 담을 수 있다.

2. SwiftUI에서 ShareLink를 사용해 공유하기

UIKit에서 UIActivityViewController를 사용해서 구현했다면 iOS 16 이상의 SwiftUI에선 ShareLink 라는 네이티브 공유 버튼이 존재한다.
SharedLink는 일종의 버튼처럼 동작하며 내부적으로 공유 시트를 자동으로 호출한다.
→ 이때 아이패드는 자동으로 처리된다!!
한 번 직접 코드를 작성해보자.
import SwiftUI
struct ContentView: View {
var body: some View {
if let image = UIImage(named: "mong"),
let _ = image.pngData() {
ShareLink(
item: Image(uiImage: image),
preview: SharePreview(
"몽이",
image: Image(uiImage: image)
)
) {
HStack {
Image(systemName: "square.and.arrow.up")
Text("에어드랍")
}
}
}
}
}
위 코드를 실행하면 아래와 같이 실제 기기에서 표시된다.

오.. 신기하다.
한 번 ShareLink의 사용 형태를 살펴보자.
ShareLink는 여러 생성자를 기본적으로 제공하는데, 그 중 가장 기본적인 형태는 아래와 같다.
실제 공식 문서를 확인해보면 ShareLink는 크게 2가지 형태로 사용할 수 있음을 알 수 있습니다.
https://developer.apple.com/documentation/swiftui/sharelink
- 기본 형태 (텍스트만 공유하고 싶을 때)
ShareLink(item: URL(string: "https://naver.com")!)
- 커스텀 레이블 형태 (위에서 사용한 형태)
ShareLink(
item: Image(uiImage: image),
preview: SharePreview("몽이", image: Image(uiImage: image))
) {
HStack {
Image(systemName: "square.and.arrow.up")
Text("에어드랍")
}
}
이때 각 매개변수가 어떤 역할을 수행하는지 하나씩 뜯어보자!
item: 이는 공유하고자 하는 실제 데이터를 의미한다.- ⭐ 이때 중요한 점은, 해당 Item은 반드시
Transferable프로토콜을 준수해야한다는 점이다. - iOS 16+ 에서 기본적으로
Transferable을 지원하는 타입은 아래와 같다. String,UIImage,Data,Image,AttributedStringpreview: 이는 공유 시트에 표시될미리보기를 의미한다.
→ 위쪽 이미지를 보면 공유 시트 상단에 몽이 사진과 몽이 이름이 그 역할을 수행한다.
-
preview는SharePreview타입으로, 공유 시트가 열려있을 때 사용자에게 보여줄미리보기 정보를 담는다. -
SharePreview의 생성자는 아래와 같다.SharePreview( _ titleKey: LocalizedStringKey, // 공유 항목의 제목 image: Image, // 미리보기 이미지 icon: Image // 작은 아이콘 (이건 선택사항이다.) )
⚠️ ShareLink의 주의사항
-
ShareLink는 기본적으로 iOS 16+ 에서만 동작하며 그 이하의 버전은
UIActivityViewController를 사용해야한다. -
커스텀 타입을 공유하고자 하면 반드시
Transferable을 채택해야한다.struct CustomData: Transferable { let title: String let content: String static var transferRepresentation: some TransferRepresentation { CodableRepresentation(contentType: .json) } } -
위에서 설명한 UIActivityViewController와 달리
ShareLink는 아이패드에서popover설정을 자동으로 처리해준다.
→ 즉, 별도의 sourceView나 sourceRect 세팅이 없어도 해당 버튼 위치를 기준으로 알아서 팝 오버가 출력된다.
3. SwiftUI의 sheet를 이용해보기 (커스텀)
UIActivityViewController를 UIViewControllerRepresentable 로 래핑한다면 .sheet로 감싸서 드래그 제스처나 애니메이션을 활용할 수 있다!
stackoverflow.com/questions/58286344/presenting-uiactivityviewcontroller-from-swiftui-view
// MARK: - ShareSheet (재사용 컴포넌트)
struct ShareSheet: UIViewControllerRepresentable {
let activityItems: [Any]
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: nil
)
// iPad 대비 (SwiftUI가 자동 처리하지만 그래도 명시한다.)
if let popover = controller.popoverPresentationController {
popover.sourceRect = CGRect(x: UIScreen.main.bounds.midX,
y: UIScreen.main.bounds.midY,
width: 0, height: 0)
}
return controller
}
func updateUIViewController(_ controller: UIActivityViewController, context: Context) {}
}
// MARK: - 사용 예시
struct ContentView: View {
@State private var showSheet = false
@State private var selectedImage = UIImage(named: "sample")!
var body: some View {
VStack(spacing: 20) {
Image(uiImage: selectedImage)
.resizable()
.scaledToFit()
.frame(height: 200)
Button("에어드롭 공유") {
showSheet = true // 바인딩으로 제어한다.
}
.buttonStyle(.borderedProminent)
}
.sheet(isPresented: $showSheet) {
ShareSheet(activityItems: [selectedImage])
}
}
}
참고 문서
[UIActivityViewController] 우리 앱에 "공유하기" 기능 (Share Sheet) 구현하는 방법
Adding AirDrop File Sharing Feature to Your iOS Apps
AirDrop으로 근처 Apple 기기에 항목 보내기 - Apple 지원 (KR)
UIActivityViewController | Apple Developer Documentation
airDrop | Apple Developer Documentation
UIPopoverPresentationController | Apple Developer Documentation
ShareLink | Apple Developer Documentation