관리 메뉴

김종권의 iOS 앱 개발 알아가기

[Deeplink] 딥링크 (URL Scheme, Universal Link) 본문

iOS 응용 (swift)

[Deeplink] 딥링크 (URL Scheme, Universal Link)

jake-kim 2020. 11. 7. 10:37

딥링크란?

  • "Deep"한 "Link", 화면속의 특정 부분으로 이동 할 수 있는 링크를 의미
  • 사용자가 "새로운 메세지가 왔습니다"와 같은 push알림을 받고 그 부분을 탭했을 때, 메세지 화면으로 바로 이동  (딥링크가 없었다면 로그인 -> 메인화면 -> 메세지 화면으로 이동)
  • 카카오톡에서 카카오맵의 특정 위치 link를 탭한경우, 카카오맵이 켜지면서 바로 특정 위치로 이동
  • 원리: 서버에서 앱에 URL전송 -> 앱에서 URL을 가지고 문자열을 파싱하여, 특정 화면으로 화면전환

iOS에서 딥링크의 종류

  • URL Scheme를 이용한 방식
    • URL Scheme란? iOS는 기본적으로 샌드박스 환경이므로 다른 앱에 정보 전달하기가 어려움 (이 때 Xcode에서 Scheme을 정해두면, 해당 Scheme로 시작하는 다른 앱을 실행하면서 정보전달이 가능)
    • URL 컨벤션: 시작은 앱의 이름으로 시작하며 뒤 path와 query부분은 웹 URL과 같이 설정 (유튜브앱: youtube://path/deeplink?scene=page1)
  • Universal Link를 이용한 방식: 참고

URL Scheme과 Universal Link의 차이

 

   URL Scheme (= custom scheme) Universal Link
사용방법 Scheme을 XCode에 등록하여 사용 웹사이트의 Domain을 이용하여 등록
Uniqueness 중복 URL이 가능 (URL이 중복된다면, 아이폰에서 어떤 앱을 선택할지 물음) 웹사이트의 도메인으로 사용하기 때문에 중복 x
동작 링크 클릭 -> "앱스토어 설치"물음 -> 앱 설치가 되어있다면 앱으로, 안되어 있다면 앱스토어로 링크 클릭 -> 링크가 설치되어 있다면 앱으로, 안되어 있다면 앱스토어로

Universal Link가 장점이 많지만 원리는 동일하므로, 기본적인 URL Scheme 내용 정리

딥링크 구현

  • URL Types추가: AppTarget -> info -> URL Types -> "+"버튼을 눌러서 생성
  • identifier와 URL Schemes에 기입 (URL Schemes에는 일반적으로 앱 이름을 기입)
  • "{입력한 URL Schemes}://"으로 url 딥링크 사용 가능

  • 딥링크가 앱으로 넘어온 경우의 함수이용 (AppDelegate에 존재)
  • func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool
  • 딥링크를 받아서 화면전환예제
    • 앱으로 받을 URL은 임시로 사용: youtube://path/deeplink?scene=page1
    • 딥링크는 braze와 같은 서버에서 push와 함께 URl을 넣어주는 경우가 있고, 그 URL을 받는다고 생각하고 아래와 같이 구현
    • URL을 받으면 AppDelegate의 application(_ UIApplication, open: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool에서 처리

주의) SceneDelegate가 있는 경우, link동작은 아래 delegate에서 받으므로 주의

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {

    }
  1. 앱을 종료 했었지만 푸시로 인해 화면들이 이미 초기화된 경우: window에 새로운 root를 등록할 필요가 없음
  2. 웹으로 부터 받은 URL이 딥링크인지 확인
  3. 쿼리 확인
  4. 딥링크 처리

아래 로직은 참고용도이고, 베스트 로직은 AppDelegate에서 화면전환을 하지 않고 postTaskManager와 같은 곳에 테스크를 등록한 후 특정 화면으로 이동 했을 때 그 화면에서 딥링크가 처리되게끔 하는게 좋은 구조

(Coordinator관련 로직은 여기 참고)

// AppDelegate.swift

    var postTaskManager = PostTaskManager()
    lazy var navigationController: UINavigationController = {
        let navi = UINavigationController()
        navi.setNavigationBarHidden(true, animated: false)
        return navi
    }()
    lazy var router = SplashCoordinator(rootViewController: navigationController, postTaskManager: self.postTaskManager, initialRoute: .splash).strongRouter

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // 1. 앱을 종료 했었지만 푸시로 인해 화면들이 초기화 된 경우
        if !navigationController.viewControllers.isEmpty {
            return true
        }
        window = UIWindow(frame: UIScreen.main.bounds)
        router.setRoot(for: window!)
        window?.makeKeyAndVisible()
        
        return true
    }
    

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {

        // 2. 받은 URL이 딥링크인지 확인
        let deeplinkPath = "/deeplink/"
        guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
              let path = components.path,
              let params = components.queryItems else {
            return false
        }
        guard path == deeplinkPath else {
            return false
        }
	
    	// 3. 쿼리확인
        if let value = params.first(where: { $0.name == "scene" })?.value {
            processDeeplink(with: value)
            return true
        } else {
            print("index missing")
            return false
        }
    }

	// 4. 딥링크 처리 로직: 로그인이 되어있다면 홈화면을 베이스로 하며, 특정 화면으로 이동하게끔 설정
    private func processDeeplink(with scene: String) {

        // TODO - token정보 가지고 로그인 판단
        let isLoggedIn = true
        if !isLoggedIn {
            _ = SplashCoordinator(rootViewController: navigationController, postTaskManager: postTaskManager, initialRoute: .splash)
            return
        }

        // Home화면이 베이스가 되게끔 설정
        if navigationController.viewControllers.first as? HomeVC == nil {
            _ = HomeCoordinator(rootViewController: navigationController, postTaskManager: postTaskManager, initialRoute: .home)
        }

        switch scene {
        case "receipt":
            _ = UsageCoordinator(rootViewController: navigationController, postTaskManager: postTaskManager, initialRoute: .receipt(date: "2020/11/06 금요일", paymentState: .success))
        case "card":
            break
        default:
            break
        }
    }

딥링크 처리 로직

  • 딥링크 URL인지 확인
    application
    (_: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
      위 함수에서 화면을 초기화 하는 로직이 있다면, 딥링크로 인해서 이미 화면이 존재하는지를 체크해야 이중으로 초기화가 안됨
  • URL 확인:
    application(_: UIApplication, open: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool
      
    위 함수에서 deeplink URL인지 확인
  • 특정 화면으로 이동하기 전에 로그인이 되어있는지 체크
  • 특정 화면으로 이동하기 전에, home을 먼저 push후 이동
  • 만약 현재 home화면이, navigationController.viewControllers[0]에 위치한다면, 따로 home화면 push하지 않아도 됨

 

Comments