iOS 공유하기 LPLinkMetadata (UIKit, SwiftUI) 디자인패턴 적용
iOS 시스템 공유하기 기능을 개발
SwiftUI에서 지원하는 ShareLink도 있지만, 더 범용성있게 사용하기 위해 해당 형태로 구현
예제코드
히스토리
- 2021.06.17.: 초기버전
- Swift 5, Xcode 12, iOS minimum Target 9.0
- 2024.12.11: 개선버전
- Swift 5.10, Xcode 16.1, minimum Target 13.0
- 빌더 패턴 적용
- Mixin and Trait 개념 적용
- 모듈화
- LinkMetaData 적용
코드 구현에 사용된 개념
목차
- 결과 스크린샷
- 구현부 전체 코드
- Usage
- SwiftUI
- UIKit + MVC
- UIKit + MVVM
결과 스크린샷
액티비티 아이템에는 빌더 패턴을 통해 여러 값들을 넣어줄 수 있음. 시뮬레이터 예제로 실기기에서 테스트하면 더 다양한 옵션이 존재.
- 왼쪽이미지
- URL만 넣어준 경우 웹에서 세팅된 값을 자동으로 설정
- 오른쪽 이미지
- URL 뿐만아니라 썸네일, 타이틀 등 내가 필요한 것들을 명시적으로 세팅
구현부 전체 코드
구현에 대한 전체 코드
public protocol SystemSharingFeature {
func show(_ activityViewController: UIActivityViewController)
}
public extension SystemSharingFeature {
func show(_ activityViewController: UIActivityViewController) {
if let keyWindow = UIWindow.keyWindow {
let topViewController = keyWindow.rootViewController?.topMostViewController()
topViewController?.present(activityViewController, animated: true, completion: nil)
}
}
}
// MARK: - SystemSharingBuildable
public protocol SystemSharingBuildable {
func excludedActivityTypes(_ excludedActivityTypes: [UIActivity.ActivityType]) -> Self
func activityItemSource(_ activityItemSource: ActivityItemSource) -> Self
func activityItem(_ activityItems: [Any]) -> Self
func build() -> UIActivityViewController
}
public final class SystemSharingBuilder: SystemSharingBuildable {
var excludedActivityTypes: [UIActivity.ActivityType]?
var activityItemSource: ActivityItemSource?
var activityItems: [Any]?
public init() {}
public func excludedActivityTypes(_ excludedActivityTypes: [UIActivity.ActivityType]) -> Self {
self.excludedActivityTypes = excludedActivityTypes
return self
}
public func activityItemSource(_ activityItemSource: ActivityItemSource) -> Self {
self.activityItemSource = activityItemSource
return self
}
public func activityItem(_ activityItems: [Any]) -> Self {
self.activityItems = activityItems
return self
}
public func build() -> UIActivityViewController {
let activityViewController = UIActivityViewController(
activityItems: activityItems ?? [activityItemSource as Any],
applicationActivities: nil
)
activityViewController.excludedActivityTypes = excludedActivityTypes
return activityViewController
}
}
Usage
사용법 정리
- SwiftUI
- UIKit + MVC
- UIKit + MVVM
빌더 패턴을 적용하여 필요한 것들을 붙여서 사용할 수 있음
- 윈도우 위에 띄우는 구현 방식으로 View가 없는 객체에서도 사용할 수 있음
- 즉, RIBs에서 View가 없는 RIB에서 해당 행위 호출 가능하다는 의미
// MARK: - Usage (SwiftUI)
struct SwiftUIView: View, SystemSharingFeature {
var body: some View {
Button("공유하기 버튼 실행") {
let builder = SharingFeatureBuilder()
.activityItemSource(.init(title: "당근", urlString: "https://rldd.tistory.com/720", image: .init()))
.build()
show(builder)
}
}
}
// MARK: - Usage (UIKit - MVC)
final class ViewController_MVC: UIViewController, SystemSharingFeature {
init() {
super.init(nibName: nil, bundle: nil)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var shareButton: UIBarButtonItem = {
let btn = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(didTapShareButton))
return btn
}()
@objc private func didTapShareButton() {
let builder = SharingFeatureBuilder()
.activityItemSource(.init(title: "당근", urlString: "https://rldd.tistory.com/720", image: .init()))
.build()
show(builder)
}
private func configureUI() {
navigationItem.rightBarButtonItem = shareButton
}
}
// MARK: - Usage (UIKit - MVVM)
final class ViewModel: SystemSharingFeature {
enum Action {
case shareButtonTapped
}
func send(action: Action) {
switch action {
case .shareButtonTapped:
let builder = SharingFeatureBuilder()
.activityItemSource(.init(title: "당근", urlString: "https://rldd.tistory.com/720", image: .init()))
.build()
show(builder)
}
}
}
final class ViewController_MVVM: UIViewController {
private let viewModel: ViewModel
init() {
self.viewModel = .init()
super.init(nibName: nil, bundle: nil)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private lazy var shareButton: UIBarButtonItem = {
let btn = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(didTapShareButton))
return btn
}()
@objc private func didTapShareButton() {
viewModel.send(action: .shareButtonTapped)
}
private func configureUI() {
navigationItem.rightBarButtonItem = shareButton
}
}
'project > 개발 업무' 카테고리의 다른 글
SwiftUI로 Placeholder가 존재하는 TextField 설계 팁 (UIKit호환) (1) | 2024.09.29 |
---|---|
[Xcode 16 Beta] Could not download and install iOS 18.0 Simulator runtime with Xcode 16.0 beta (0) | 2024.06.12 |
[XCode 15.0 beta] Preview Macro Bug (0) | 2023.06.08 |
Lottie 리소스 문제로 앱이 초기화되는 현상 (0) | 2022.07.12 |
iOS RxDelegateProxy 만들어보기 #2 (StarScream + RxProxy) (0) | 2022.01.12 |