반응형

iOS 위젯에서 앱 특정 화면으로 이동하는 방법

iOS에서 Widget을 탭했을 때 앱이 열리며 특정 화면으로 이동하게 하려면, URL Scheme + 딥링크 라우팅 + Widget Link 구성의 세 가지 핵심을 이해해야 합니다.

이 문서는 iOS 17~18 최신 SwiftUI / WidgetKit 구조에 맞춰 정리된 실전 예제입니다.

1. 개념 요약

구성 요소설명

Widget 앱의 일부 정보를 홈 화면/잠금화면에 표시
URL Scheme myapp://home, myapp://detail?id=3 형태로 앱 특정화면 호출
Router URL을 파싱해 SwiftUI NavigationStack으로 연결
Link / AppIntent Widget 내부에서 앱을 여는 공식 방법

 

2. URL Scheme 등록 (딥링크 설정)

Info.plist 파일에 아래 항목 추가:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

→ 이제 myapp://home, myapp://detail?id=3 등을 인식할 수 있습니다.


 

3. SwiftUI App에서 onOpenURL 처리

import SwiftUI

@main
struct MyApp: App {
    @StateObject var router = AppRouter()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(router)
                .onOpenURL { url in
                    router.handleDeepLink(url)
                }
        }
    }
}

 

4. AppRouter 구현

import Foundation

final class AppRouter: ObservableObject {
    @Published var destination: Destination?

    enum Destination {
        case home
        case detail(id: Int)
        case settings
    }

    func handleDeepLink(_ url: URL) {
        guard url.scheme == "myapp" else { return }
        switch url.host {
        case "home":
            destination = .home
        case "detail":
            if let idString = url.queryItems?["id"], let id = Int(idString) {
                destination = .detail(id: id)
            }
        case "settings":
            destination = .settings
        default:
            break
        }
    }
}

extension URL {
    var queryItems: [String: String]? {
        URLComponents(string: absoluteString)?.queryItems?
            .reduce(into: [String: String]()) { $0[$1.name] = $1.value }
    }
}

 

5. ContentView에서 라우팅 반영

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var router: AppRouter

    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink("홈으로", value: AppRouter.Destination.home)
                NavigationLink("상세로", value: AppRouter.Destination.detail(id: 1))
            }
            .navigationDestination(for: AppRouter.Destination.self) { destination in
                switch destination {
                case .home: HomeView()
                case .detail(let id): DetailView(id: id)
                case .settings: SettingsView()
                }
            }
        }
    }
}

 

6. Widget에서 딥링크로 앱 열기

import WidgetKit
import SwiftUI

struct MyWidgetEntryView: View {
    var entry: SimpleEntry

    var body: some View {
        VStack {
            Link(destination: URL(string: "myapp://detail?id=3")!) {
                HStack {
                    Image(systemName: "bell.fill")
                    Text("3번 상세화면 열기")
                }
            }

            Link(destination: URL(string: "myapp://settings")!) {
                Text("설정 화면 열기")
            }
        }
        .padding()
    }
}

Link(destination:)은 위젯에서 앱을 여는 공식 API이며, 지정한 URL로 앱이 실행됩니다.


 

7. AppIntent 기반 (iOS 17+)

struct OpenDetailIntent: AppIntent {
    static var title: LocalizedStringResource = "상세화면 열기"

    func perform() async throws -> some IntentResult {
        guard let url = URL(string: "myapp://detail?id=10") else { return .result() }
        await UIApplication.shared.open(url)
        return .result()
    }
}

Widget 내부에서:

struct MyWidget: Widget {
    var body: some WidgetConfiguration {
        AppIntentConfiguration(kind: "MyWidget", intent: OpenDetailIntent.self) { entry in
            VStack {
                Button(intent: OpenDetailIntent()) {
                    Label("상세 화면 열기", systemImage: "doc.text.magnifyingglass")
                }
            }
        }
    }
}

 

8. 데이터 공유 (App Group)

let defaults = UserDefaults(suiteName: "group.com.mycompany.myapp")
defaults?.set("hello", forKey: "widgetMessage")

앱에서 읽기:

let message = UserDefaults(suiteName: "group.com.mycompany.myapp")?.string(forKey: "widgetMessage")

 

9. 테스트 명령어

시뮬레이터에서 직접 딥링크 실행:

xcrun simctl openurl booted "myapp://detail?id=3"

→ 해당 화면으로 이동하면 정상 동작입니다.


 

10. 문제 해결 FAQ

증상원인해결

앱은 열리지만 화면이 안 바뀜 .onOpenURL 누락 App 구조에 추가
위젯 터치 작동안함 Link 영역 누락 명시적으로 감싸기
SwiftUI 이동 안됨 @Published destination미갱신 Router에서 상태 변경 확인

 

11. 결론

단계설명

1단계 Info.plist에 URL Scheme 등록 (myapp://)
2단계 .onOpenURL 로 URL 수신
3단계 AppRouter로 라우팅
4단계 Widget에서 Link() 또는 AppIntent로 호출
5단계 필요 시 App Group으로 데이터 공유

→ 이 과정을 적용하면 위젯 → 앱 → 특정 화면 자동 이동이 완벽하게 구현됩니다.

반응형
Posted by 까칠코더
,