SwiftUI 에서 URL Schemes 과 Deeplink

최근 SwiftUI를 공부하며 개인적으로 문제가 되고 있던것 중에 하나가 과거에 하던 방식으로 문제를 해결하려는 버릇인데 URLSchames 문제를 해결하려다 또 그런일이 있었다. 프레임워크가 변하면 생각을 바꿔야 하는데 그게 쉽지 않다.

URL Schemes

URL Schemes와 그에따른 처리방법은 아이폰앱에서 데이터를 전달할 수 있는 규격을 등록하고 등록된 규격의 데이터를 수신 했을때의 처리를 위한 일련의 처리를 말하는데 거의 초기버전의 iOS부터 지원한 방법이다.

내가 알고 있던 이 문제를 해결하는 방법은 AppDelegate와 SceneDelegate 에 아래와 같은 함수를 정의하고 URLContext의 URL을 파싱해서 데이터의 내용에 따라 분기를 하고 데이터에 맞는 ViewController를 오픈하는 단계까지 가는 것이 문제의 해결이었다.

 func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {            
            print("url: \(url.absoluteURL)")
            print("scheme: \(url.scheme)")
            print("host: \(url.host)")
        }
    }

하지만 현재 XCode(V 12.5) 에서 기본적인 프로젝트 생성에서 Life Cycle을 SwiftUI App으로 만들면 AppDelegate, SceneDelegate를 만들어 주지 않는다. 이게 존재 하지 않으니 어디에서 url을 받아들이고 분기해야 할지 알 수가 없었다.

참고로 앱을 생성할때 아래의 Life Cycle을 UIKit App Delegate를 선택하면 이전에 쓰던 AppDelegate과 SceneDelegate 파일이 자동으로 생성된다.

삽질

과거의 방법으로 문제를 해결하려는 시도는 결국 며칠간의 삽질로 끝났다. AppDelegate와 SceneDelegate를 새로 만들었고 이런 해결방법도 가능하다는 결론에 도달했지만 검색중에 보게된 코드 때문에 그 코드들을 모두 버리고 말았다.

.onOpenURL()

문제의 해결 방법이 너무 간단해서 약간 허탈하긴 했지만 아래의 코드와 같이 View의 메소드를 호출하는 방법으로 URL Schemes에 등록된 url의 접근을 확인 할 수 있다.

@main
struct MyApplication: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
        .onOpenURL { url in
          print(url)
        }
    }
  }
}

DeepLink

위의 코드에서 보는것과 같이 모든 View 컨트롤은 .onOpenURL() 메소드를 가지고 있다. 이것은 위에 보이는 메인뷰에서도 url 이벤트를 확인해서 실제로 보여주고자 하는 상세 페이지까지 이동 했을 경우 그 뷰에도 동일한 함수(.onOpenURL())를 호출하면 url값을 얻을 수도 있다.

.onOpenURL { url in
            if (url.scheme! == "where-board" && url.host! == "invite") {
                if let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) {
                    for query in components.queryItems! {
                        print(query.name)
                        print(query.value!)                        
                    }
                }
            }            
        }

Sample 프로젝트

아래와 같이 신규 프로젝트를 생성해서 상세 페이지에서 URL Schames를 통해 전달된 데이터를 출력해 볼수 있다.

프로젝트의 Life Cycle은 SwiftUI App으로 선택한다.

URL Schemes을 생성하기 위해서는 프로젝트 – info – URL Types 를 선택하고 + 버튼을 눌러서 URL Schemes 항목에 유니크한 이름을 입력한다.

메인 프로젝트 App – Scene – View 의 구조에서 ContentView()에 아래의 코드와 같이 url을 확인 할 수 있다.

import SwiftUI

@main
struct DeepLink_TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL(perform: { url in
                    print(url)
                })
        }
    }
}

실제 콘텐츠뷰의 내부에서도 url 을 전달 받을 수 있다

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
            .onOpenURL(perform: { url in
                print(url)
            })

    }
}

아이폰에 빌드하고 앱이 실핼된 상태에서 사파리 브라우저에서 위와 같이 주소를 입력하고 앱으로 전환되어 아래와 같이 url이 전달되는 것을 확인할 수 있었다. 두번 프린트 되었는데 첫번째는 위의 코드에서 두번째는 아래의 코드에서 전달된 데이터이다.

my-deeplink-test://share?id=1234
my-deeplink-test://share?id=1234

대략 아래와 같은 코드를 이용해서 url을 파싱할 수 있다.

        .onOpenURL { url in
            if (url.scheme! == "my-deeplink-test" && url.host! == "share") {
                if let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) {
                    for query in components.queryItems! {
                        print(query.name)
                        print(query.value!)                        
                    }
                }
            }            
        }

20210515 박병일