DeepLink
딥링크에 대해 정리하고 간단한 예제로 이해해봅니다.
딥링크는 "하이퍼링크의 모바일판"이라고 할 수 있습니다.
예를 들어볼까요?
쿠팡 상품의 광고를 클릭하면 "쿠팡앱실행 -> 해당상품" 이동
카톡 메세지를 클릭하면 메세지 화면으로 바로이동 "카카오톡실행 -> 해당 메세지 화면"
카톡에서 카카오맵의 특정 위치를 클릭하면 "카카오맵실행 -> 해당위치화면"
이렇게 링크를 클릭해 앱을 실행하고 원하는 화면으로 이동할 수 있게 할 수 있습니다.
iOS에서 딥링크를 구현하는 방법은 두 가지가 있습니다.
- URLScheme
- Universal Link
여기서 Universal Link는 특징만 보고 넘어가고. URLScheme을 집중적으로 보겠습니다.
URLScheme
- 어플리케이션간의 통신이나 특정 앱을 실행시켜주는 매커니즘입니다.
- iOS는 샌드박스 환경이므로 다른 앱에 정보를 전달하기 어렵습니다.
- XCode에서 Scheme을 정해두면 해당 Scheme으로 시작하는 다른 앱을 실행하며 정보 전달이 가능합니다.
- 시작은 앱 이름으로 시작하여 Path와 query부분은 URL과 같이 설정합니다. 아래와 같은 식으로.
[scheme]://[host]/[path]?[query]
scheme: 앱을 구별하는 고유한 문자열입니다(myApp)
host, path, query: 옵셔널로 특정 화면이나 기능을 나타내기 위해 사용됩니다
예를 들어
youtube://path/deeplink?scene=page1
이런 링크가 만들어질 수 있겠네요
Universal Link를 다루진 않지만 URLScheme과의 차이정도는 보고 넘어가겠습니다.
사용법
자 이제 이론은 끝났으니 사용을 해봅시다. 간단한 예시입니다.
일단 URLScheme을 설정해주어야 합니다.
제가 생성한 프로젝트의 이름은 DeepLink이므로 URL Schemes를 deeplink로 설정했습니다.
설정이 끝난 후 알아야할 개념이 하나 있습니다.
딥링크로 앱이 띄워진 경우 AppDelegate만 있는경우엔 application(app:url:options:)으로 url이 넘어오게 됩니다.
근데 iOS개발엔 대부분 SceneDelegate가 있죠? SceneDelegate가 있는 경우 Scene(scene:URLContext:)로 넘어오게 됩니다.
화면은 이렇게 설정했으나, 실제는 코드로 작성해뒀습니다. 이해를 돕기위해 스토리보드로 그려봤습니다.
이 후 딥링크로 앱을 띄워봅시다.
카카오톡으로 아래 메세지를 보내고 클릭해봤습니다.
그럼 처음엔 앱을 실행하겠습니까? 라는 알럿이 나오고 클릭하면 앱이 띄워지며 mainViewController(root)가 띄워지게 됩니다.
목표는 SecondViewController를 띄우는 것입니다.
그리고, 앱이 XCode가 앱을 running상태로 링크를 클릭해보면 아까 설명한 SceneDelegate에서 URLContexts를 print해보겠습니다.
이런 print가 출력됩니다 controllet는 오타이니.. 감안하고 보시면 URL로 deeplink로 설정한 URL이 들어왔습니다.
이제 이걸 활용해 SecondViewController를 띄워보겠습니다.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
// 윈도우를 생성, 씬이 연결된 경우 rootViewController를 띄워줍니다.
window = UIWindow(windowScene: windowScene)
let viewController = ViewController()
let navigationController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
// 딥링크로 url이 들어오면 url을 분석.
handleDeepLink(url)
}
func handleDeepLink(_ url: URL) {
// component로 변환하고 query의 Item중 controller인것을 찾음. 여기선 deeplink://scene/page?controller<< 얘를 찾고 얘의 value인 second가 controller가됨
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)
if let controller = components?.queryItems?.first(where: { $0.name == "controller" })?.value {
switch controller {
case "second":
navigationToSecondController()
default:
fatalError("unknownController")
}
}
}
func navigationToSecondController() {
if let navigationController = window?.rootViewController as? UINavigationController {
let secondViewController = SecondViewController()
navigationController.pushViewController(secondViewController, animated: true)
}
}
}
하지만 이렇게 작성하면 앱이 메모리에 없을경우 링크로 이동 한 경우엔 SecondVC로 이동하지 않고 mainViewController가 띄워집니다.
이유는 이렇습니다. scene(_:openURLContexts:) 는 앱이 이미 메모리에 있을 때 외부에서 딥링크를 통해 앱을 열 때 호출되는 메서드라고 합니다.
만약 앱이 실행중이지 않았다면, 즉 앱이 백그라운드에도 없어 메모리에서 완전히 제거된 상태였다면 scene(_:willConnecTo:option) 내부의 connectionOptions을 통해 딥링크 정보를 얻을 수 있다고 합니다.
정리해보자면
⭐️⭐️⭐️⭐️⭐️⭐️
앱이 이미 실행중이거나 백그라운드에 있다면
scene(_:openURLContexts:)
앱이 실행중이지 않다면(메모리에 없다면)
scene(_:willConnecTo:option:)
로 url을 받을 수 있다고 하네요.. 맞나?
그럼 이렇게 코드를 수정하면 되겠네요?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
// 윈도우를 생성
window = UIWindow(windowScene: windowScene)
let viewController = ViewController()
let navigationController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
if let url = connectionOptions.urlContexts.first?.url {
handleDeepLink(url)
}
}
수정하니 올바르게 작동합니다.
코디네이터 패턴이랑 같이 사용하면 좋을것같네요
좋은코드가 아닌것은 알고있습니다. 단지 이해를 위해
틀린점이 있다면 알려주시기 바랍니다.