일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- uitableview
- swiftUI
- SWIFT
- HIG
- Refactoring
- MVVM
- swift documentation
- 리펙터링
- Xcode
- rxswift
- 리펙토링
- 애니메이션
- uiscrollview
- RxCocoa
- combine
- collectionview
- tableView
- Protocol
- ribs
- UITextView
- Clean Code
- Human interface guide
- Observable
- map
- 리팩토링
- UICollectionView
- clean architecture
- 스위프트
- 클린 코드
- ios
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. DeepLink (딥 링크) - URL Scheme, Custom URL, Foreground, Background, Not Running, URLComponents 기초 개념 본문
[iOS - swift] 2. DeepLink (딥 링크) - URL Scheme, Custom URL, Foreground, Background, Not Running, URLComponents 기초 개념
jake-kim 2021. 10. 7. 22:421. DeepLink (딥 링크) - 앱 푸시, APNs (Apple Push Notification service ) 개념
2. DeepLink (딥 링크) - URL Scheme, URLComponents, Foreground, Background, Not Running 기초 개념
3. DeepLink (딥 링크) - FCM(Firebase Cloud Messaging) remote 푸시 사용 방법
4. DeepLink (딥 링크) -Dynamic Link (다이나믹 링크) 사용 방법 (Firebase, 공유하기 기능)
5. DeepLink (딥 링크) - URL Scheme과 Dynamick Link를 이용한 딥 링크 처리 방법
cf) Push Notification 처리 관련 메소드 총 정리 글은 이 포스팅 글 참고
URL Scheme의 개념
- URL(Uniform Resource Location)의 개념: 특정 리소스의 위치를 나타내는 값
- 구체적인 개념은 URL vs URI 개념글 참고
- Scheme의 의미: URL의 접두사
- ex) 웹 URL의 scheme은 "http"
- ex) 사용자가 정의한 deep link URL에서의 scheme은 "my-app"
Custom URL
- Custom URL이란 앱 내부의 리소스를 참조하는 방법을 제공하는 방법
- 예시) 이미 애플에서 지정한 URL
- mailto
- tel
- sms
- facetime
- 이밖의 Apple의 URL Scheme을 정해놓은 정보 참고
ex) safari 검색창에서 tel://010-1111-1111 입력 후 엔터를 누르면, 전화번호 앱에서 present되는 화면 표출
- 위와같이 xcode에서도 따로 URL을 만들어서 해당 앱만의 리소스를 참조할 수 있는 방법 제공이 가능
- ex) 카메라 앱이 있을 때, 특정 이미지를 열고 싶은 경우, `myphotoapp:albumname?name="albumname"`
Custom URL 등록
- Xcode에서 Push Notification 기능 활성화
Target > Signing & Capabilities > +Capability > Push Notification
- URL scheme 등록: Target > Info > URL Types에서 추가
들어오는 URL 처리
- SceneDelegate 파일이 존재하는 경우: scene(_:openURLContexts:) 사용
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
print(url)
}
- SceneDelegate 파일이 없고 AppDelegate파일만 존재하는 경우
- application(_:open:options:) 사용
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
}
- Firebase Dynamic link와 FCM을 같이 쓰는 경우에는 위 메소드 호출이 안되므로, AppDelegate의 userNotificationCenter(_:didReceive:)에서 구현
let center = UNUserNotificationCenter.current()
center.delegate = self
...
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
}
}
- safari 앱에서 테스트: "my-app://name=jake" 입력 후 이동
- 위 URLContexts.first?.url 값은 "my-app://navigation?name=jake"
- URLComponents를 이용하여 URL을 파싱
- URLComponents의 생성자에 url.absoulteStgring 전달
- URLComponents에서는 queryItem프로퍼티에 접근 가능
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
// 해당 scheme과 host를 가지고 있는지 파악
guard url.scheme == "my-app", url.host == "navigation" else { return }
// 원하는 query parameter가 있는지 파악
let urlString = url.absoluteString
guard urlString.contains("name") else { return }
let components = URLComponents(string: url.absoluteString
...
}
}
- URLComponents객체는 queryItems 객체에 접근 가능
- [String]과 같이 문자열 배열로 만들어 지는 것
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
// 해당 scheme과 host를 가지고 있는지 파악
guard url.scheme == "my-app", url.host == "navigation" else { return }
// 원하는 query parameter가 있는지 파악
let urlString = url.absoluteString
guard urlString.contains("name") else { return }
let components = URLComponents(string: url.absoluteString)
let urlQueryItems = components?.queryItems ?? [] // [name=jake]
var dictionaryData = [String: String]()
urlQueryItems.forEach { dictionaryData[$0.name] = $0.value }
guard let name = dictionaryData["name"] else { return }
print("이름 = \(name)")
}
- 만약 아래처럼 딥링크를 적용하는 경우, "my-app://navigation?name=jake&age=20"
- urlQueryItems:[name=jake, age=20]
- 배열로 되어있고 각 item의 값은 $0.name, $0.value로 접근 가능
- ex) "age": urlQueryItems[1].name, "20": urlQueryItems[1].value
Foreground에서는 푸시가 표출 안되도록 설정 방법
- Foreground 설정: AppDelegate의 UNUserNotificationCenterDelegate에 있는 userNotificationCenter(_:willPResent:withCompletionHandler)에서 설정
- 해당 델리게이트를 사용하기 위해서 NUUserNotificationCenter.current()로 객체 생성 후 이 객체의 delegate = self로 설정
// AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
registerRemoteNotification()
return true
}
private func registerRemoteNotification() {
let center = UNUserNotificationCenter.current()
center.delegate = self
let options: UNAuthorizationOptions = [.alert, .sound, .badge]
center.requestAuthorization(options: options) { granted, _ in
guard granted else { return }
DispatchQueue.main.async {
/// APNs에 device token 등록 요청
UIApplication.shared.registerForRemoteNotifications()
}
}
}
- Foreground에서는 url을 파싱하여 특정 url이면 푸시를 표출하지 않도록 설정
extension AppDelegate: UNUserNotificationCenterDelegate {
/// Foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if isShowAtForeground(url: notification) {
completionHandler([.alert, .badge, .sound])
}
}
private func isShowAtForeground(url notification: UNNotification) -> Bool {
let userInfo = (notification.request.content.userInfo as NSDictionary?) as? [String: Any]
// Braze 솔루션으로 예시
// braze uri인지 확인
guard let brazeURI = userInfo?["ab_uri"] as? String,
let urlComponents = URLComponents(string: brazeURI) else { return false }
let urlQueryItems = urlComponents.queryItems ?? []
var dictionaryData = [String: String]()
urlQueryItems.forEach { dictionaryData[$0.name] = $0.value }
// 이름이 jake만 foreground에서 표출
let name = dictionaryData["name"]
return name == "jake"
}
}
- 위와같이 설정해주면, name이 jake인 푸시가 들어올 경우 background 상태(앱이 화면이 안보이는 경우)와, Not Running(앱이 종료된 경우)에만 푸시가 표출
* 참고
- Foreground push: https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter
- Query Items: https://developer.apple.com/documentation/foundation/urlcomponents/1779966-queryitems
- Defining a Custom URL Scheme for Your App: https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
- URL Components: https://developer.apple.com/documentation/foundation/urlcomponents