# iOS Universal Links 調査
# Universal Links 技術フロー(関連付け~実行)
@startuml
title Universal Links 技術フロー(関連付け~実行)
actor User
participant "Link Source\n(メッセージ/メール/SNS/ブラウザ等)" as Source
participant "iOS" as SWCD
participant "Apple CDN" as CDN
participant "Web Server\n(Origin)" as Web
participant "Safari"
participant "iOS App" as App
participant "App Store" as Store
== 初回の関連付け(アプリ導入直後/初回起動時など) ==
note right of App
Associated Domains に
applinks:example.com
(開発時は ?mode=developer 併用可)
end note
alt 開発モード(?mode=developer)
SWCD -> Web: GET /.well-known/apple-app-site-association
Web --> SWCD: 200 application/json(AASA)
else 本番(リリース)
SWCD -> CDN: GET /a/v1/example.com
CDN -> Web: (必要に応じてオリジン取得/更新)
CDN --> SWCD: 200 application/json(AASA)
end
SWCD -> SWCD: AASA検証(appID / paths, components 等)
SWCD --> App: 関連付け結果を登録
== ユーザーがユニバーサルリンクをタップ ==
User -> Source: タップ https://example.com/...
Source -> SWCD: ユニバーサルリンク解決要求
SWCD -> SWCD: ドメイン/パスをAASAで照合
alt アプリがインストール済み かつ パス一致
SWCD --> App: open via NSUserActivityTypeBrowsingWeb
App -> App: DeepLinkRouter.handle(url)
note right of App
SceneDelegate / AppDelegate / SwiftUI の
onContinueUserActivity で受信し画面遷移
end note
else 未インストール または パス不一致
SWCD --> Safari: https://example.com/... を開く
opt Web側でSmart App Banner等を表示(任意)
Safari -> Store: Appページを開く(ユーザー操作)
end
end
== ユーザーの既定動作 ==
opt ユーザーが以前「Safariで開く」を選択
note right of Safari
同一リンクは以後Safari優先
(長押しで「Appで開く」を再選択可)
end note
end
== 厳守事項(AASA配信) ==
note over Web
・HTTPS必須、200直返し、拡張子なし
・Content-Type: application/json
・AASAパスでのリダイレクト禁止
end note
@enduml
# 呼び出しアプリ→ユニバーサルリンク→受け入れアプリ 呼び出しフロー
@startuml
title 呼び出しアプリ→ユニバーサルリンク→受け入れアプリ 呼び出しフロー
actor User
participant "呼び出しアプリ" as B
participant "iOS (System / swcd)" as iOS
participant "AASA配信\n(Apple CDN / Web)" as AASA
participant "受け入れアプリ" as A
participant "Safari" as Safari
participant "App Store" as Store
User -> B: ボタンタップ「受け入れアプリを開く」
B -> B: ユニバーサルリンクURL生成(https://example.com/...)
note right of B #FFEECC
A.要アプリ実装(ボタン処理とURL生成)
end note
B -> iOS: UIApplication.open(url)
note right of B #FFEECC
B.要アプリ実装(open呼び出しとフォールバック)
end note
iOS -> iOS: AASA照合(キャッシュ)
iOS -> AASA: ※必要時のみAASA取得(簡易)
AASA --> iOS: 200 JSON(簡易)
note over AASA #FFDDE0
F.要サーバー実装(AASAをHTTPS/200 JSONで配信)
end note
alt 受け入れアプリがインストール済み + パス一致
iOS --> A: NSUserActivityTypeBrowsingWeb(url)
note right of A #FFEECC
C.要アプリ実装(URL受信と保留)
end note
A -> A: DeepLinkRouter.handle(url)
note right of A #FFEECC
D.要アプリ実装(URL解析とルーティング)
end note
A --> User: 対象画面を表示
note right of A #FFEECC
E.要アプリ実装(UI遷移の実行)
end note
else 未インストール or パス不一致(またはSafari優先)
iOS --> Safari: https://example.com/... を開く
opt Web側でApp誘導(任意)
Safari -> Store: Appページ表示(ユーザー操作)
end
end
@enduml
# Universal Links 関係図(起点修正+分岐理由)
@startuml
title Universal Links 関係図(起点修正+分岐理由)
left to right direction
actor "User" as USER
rectangle "Web Server" as WEB
node "Apple CDN" as CDN
node "iPhone" as PHONE
cloud "Webサイト" as SITE
component "iPhone(受け入れアプリ)" as APP
' 事前フロー(番号外)
note left of WEB
事前:AASA配置
end note
' 実行フロー(起点)
USER --> PHONE : ① リンクをタップ
' AASA参照(キャッシュ/必要時取得)
WEB --> CDN : ②
CDN --> PHONE : ③
' 開き先(④の分岐理由を注記)
PHONE --> APP : ④
note on link #FFEECC
条件:アプリインストール済み +
AASA一致(ドメイン/パス等)
→ 受け入れアプリ起動
end note
PHONE --> SITE : ④
note on link #FFEECC
条件:未インストール/AASA不一致/
ユーザーがSafari優先を選択/
.open(universalLinksOnly)失敗時
→ Webサイトを開く
end note
@enduml
# 参考URL
- https://tech.yappli.io/entry/universal-links
- https://qiita.com/omo_taku/items/4fd439fb8c02e999cc94
- https://sg.wantedly.com/companies/wantedly/post_articles/305303
- https://zenn.dev/hsylife/articles/72edd8b6234576
- https://qiita.com/m-komatsu/items/2ca7cfa6426802ea9cb5
- https://zenn.dev/ryodeveloper/articles/kame_ga_15_hiki