[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-09 21:08:02 +09:00
parent f0befc887a
commit 93ec7e9056
363 changed files with 68333 additions and 64 deletions
@@ -0,0 +1,136 @@
---
id: ios-universal-links-deep-linking
title: iOS Universal Links — 웹 URL → 앱 진입
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [ios, deep-link, universal-link, vibe-coding]
tech_stack: { language: "Swift", applicable_to: ["iOS"] }
applied_in: []
aliases: [associated domains, AASA, custom URL scheme, deferred deep link]
---
# iOS Universal Links
> `https://example.com/order/123` → 앱 설치되어 있으면 앱이 처리, 아니면 웹. **AASA 파일 + Associated Domains capability** 필수. 옛 custom scheme (`myapp://`) 보다 안전.
## 📖 핵심 개념
- AASA (apple-app-site-association): 도메인이 어떤 앱과 연결.
- Associated Domains capability: 앱에 도메인 등록.
- onContinueUserActivity: SwiftUI / SceneDelegate.
- Deferred deep link: 앱 설치 후 첫 실행에 의도 복원 — 별도 SDK (Branch, Adjust).
## 💻 코드 패턴
### 1. 서버 — AASA
```json
// https://example.com/.well-known/apple-app-site-association
{
"applinks": {
"details": [
{
"appIDs": ["TEAMID.com.example.MyApp"],
"components": [
{ "/": "/order/*" },
{ "/": "/user/*", "?": { "ref": "?*" } },
{ "/": "/admin/*", "exclude": true }
]
}
]
}
}
```
응답 헤더: `Content-Type: application/json`. 인증 / redirect X.
### 2. 앱 — Associated Domains
- Xcode → Signing & Capabilities → Associated Domains.
- `applinks:example.com` 추가.
### 3. SwiftUI — handle
```swift
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
guard let url = activity.webpageURL else { return }
handle(url)
}
.onOpenURL { url in
// Custom scheme (myapp://) system
handle(url)
}
}
}
func handle(_ url: URL) {
let comps = URLComponents(url: url, resolvingAgainstBaseURL: false)
guard let path = comps?.path else { return }
if path.hasPrefix("/order/") {
let id = String(path.dropFirst("/order/".count))
navigationStore.navigate(.order(id))
}
}
}
```
### 4. UIKit — SceneDelegate
```swift
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL { handle(url) }
}
func scene(_ scene: UIScene, openURLContexts contexts: Set<UIOpenURLContext>) {
if let url = contexts.first?.url { handle(url) }
}
```
### 5. Custom URL scheme (legacy)
```xml
<!-- Info.plist -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array><string>myapp</string></array>
</dict>
</array>
```
`myapp://order/123` — but Universal Links 권장 (위조 가능 / 사용자 confusion).
### 6. Deferred deep link
- 앱 미설치 → App Store 이동 → 설치 후 첫 실행 시 원래 의도 복원.
- iOS 자체 지원 X — Branch.io / Adjust / AppsFlyer SDK.
## 🤔 의사결정 기준
| 상황 | 도구 |
|---|---|
| 외부 URL → 앱 진입 | Universal Links |
| OS 가 호출 (인증 callback) | Universal Link 또는 Custom scheme |
| Push notification 안 deep link | userInfo + handle() |
| 위젯 → 앱 deep link | widgetURL |
| 앱 설치 전 URL 의 의도 복원 | Deferred (외부 SDK) |
| QR / NFC | App Clip + invocation URL |
## ❌ 안티패턴
- **AASA 가 redirect 또는 401**: 앱 인식 못 함. CDN / 로컬 호스팅 정확히.
- **AASA 너무 큼 (1MB+)**: 다운로드 안 됨. 작게.
- **Universal Link 가 같은 도메인의 사파리에서 호출**: 앱 안 열림. 다른 앱에서 들어와야.
- **handle 가 navigation 만 — 인증 검증 누락**: 권한 우회 가능.
- **스킴만 의존**: 위조 가능, 다른 앱이 같은 scheme 등록 가능.
- **path / params 검증 없이 직접 사용**: SQL/XSS 인젝션 가능.
## 🤖 LLM 활용 힌트
- AASA + Associated Domains + onContinueUserActivity 3종.
- path / params 검증 (zod-like) 후 navigation.
## 🔗 관련 문서
- [[iOS_App_Clips]]
- [[iOS_Push_Notifications]]