์ ๋๋ฒ์ ๋งํฌ(Univeral Link) ๋?
iOS์์ ๋ฅ๋งํฌ*๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ ์ค ํ๋๋ก ์น์์ ์ฑ์ ํธ์ถํ๋ ๊ธฐ๋ฅ์ด ํ์ํ ๋ ์ฌ์ฉ!
*๋ฅ๋งํฌ(Deep Link) : ํน์ ์ฃผ์๋ ๊ฐ์ ์ ๋ ฅํ๋ฉด ์ฑ์ด ์คํ๋๊ฑฐ๋ ์ฑ ๋ด ํน์ ํ์ด์ง์ ๋๋ฌํ ์ ์๋๋ก ํ๋ ๋งํฌ
- URI ์คํด ๋ฐฉ์ : ์ฑ์ URI ์คํด(scheme) ๊ฐ์ ๋ฑ๋กํ์ฌ ๋ฅ๋งํฌ ์ฌ์ฉ
- ์ฑ๋งํฌ(App Link) : Android ์ ๊ณต - ๋๋ฉ์ธ ์ฃผ์๋ฅผ ์ด์ฉํ ๋ฅ๋งํฌ ์ฌ์ฉ
- ์ ๋๋ฒ์ ๋งํฌ (Universal Link) : iOS ์ ๊ณต - ๋๋ฉ์ธ ์ฃผ์๋ฅผ ์ด์ฉํ ๋ฅ๋งํฌ ์ฌ์ฉ
*Dynamic Link : ํ์ด์ด๋ฒ ์ด์ค์์ ์ ๊ณตํ๋ ๋ฅ๋งํฌ์ ์ผ์ข
์ ๋๋ฒ์ ๋งํฌ๋ฅผ ์ด์ฉํ๋ฉด, ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด
์ฌ์ฉ์๊ฐ ๋งํฌ ์ ๊ทผ ์, ์ฑ์ด ์ค์น๋์ด ์๋ ๊ฒฝ์ฐ ์ฑ์ผ๋ก ์ด๋ํ๊ณ ์์ผ๋ฉด ์ฑ์ ์ค์นํ ์ ์๋ ์ฑ์คํ ์ด๋ก ์ด๋ํ๊ฒ ๋ฉ๋๋ค.
์ ๋๋ฒ์ ๋งํฌ(Univeral Link) ํ์ฉ
- QR code, NFC ์ธ์ ์ ์ฑ์ผ๋ก ์ฐ๊ฒฐ
- ์น ๊ด๊ณ ๋ฐฐ๋ ํด๋ฆญ์ ์ฑ ์ด๋์ ํตํ ์ฑ ํ๋ณด
์ ๋๋ฒ์ ๋งํฌ(Univeral Link) ํน์ง
- ip ๋ฐ http์์ ๋์ํ์ง ์์
- SSL ์๋ ๋ฐฑ์๋ ํ์
- ํ์ฉ ์ฌ๋ถ๋ฅผ ๋ฌป์ง ์์
- ์ง์ iOS ๋ฒ์ : iOS 9 ์ด์
์ ๋๋ฒ์ ๋งํฌ(Univeral Link) Setting Process
- ์น์๋ฒ ์์ - Web Server์ AASA(apple-app-site-association) ํ์ผ ์ถ๊ฐ
- ์ฑ ์์ - Associated Domains ์ถ๊ฐ
HOW TO USE ?
1. Web Server์ AASA ํ์ผ ์ถ๊ฐํ๊ธฐ
Universal link๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์์ ํ ๋๋ฉ์ธ์ด ์์ด์ผ ํฉ๋๋ค.
๋ฐ๋ผ์ ๋๋ฉ์ธ ์ ๋ณด๊ฐ ํฌํจ๋ JSONํ์ผ์ธ apple-app-site-association(AASA)ํ์ผ์ ์น์๋ฒ์ ์ ๋ก๋ํด์ผ ํฉ๋๋ค.
์ฌ์ฉ์๊ฐ ์ฑ ์ค์น์, ์ด ํ์ผ์ ๋ฑ๋ก๋ ๋๋ฉ์ธ์ผ๋ก apple-app-site-association ํ์ผ์ ๋ํ ์์ฒญ์ ๋ณด๋ ๋๋ค.
๋, ์ฌ์ฉ์๊ฐ ์ด๋ฏธ ์ฑ์ ์ค์นํ๋ค๋ฉด ์ฑ์ ๋ฐ๋ก ์คํ์ฃผ๋ ๋งํฌ์ ๋๋ฉ์ธ์์ ํธ์คํ ๋ฉ๋๋ค.
์ฃผ์ํ ์ !
โ๏ธredirection ์ด ์์ด์ผ ํ๊ณ
โ๏ธhttps ์ง์
โ๏ธ128KB๋ณด๋ค ์์์ผ ํจ
[ apple-app-site-association ํ์ผ ]
ํ์ผ๋ช : apple-app-site-association (ํ์ฅ์ ์์)
์์น: .well-known ๋๋ ํฐ๋ฆฌ ๋๋ root ๋๋ ํฐ๋ฆฌ์ ์ถ๊ฐ
[For iOS13 and later]
{
applinks: {
details: [
{
appIDs: ["<TEAM_DEVELOPER_ID>.<BUNDLE_IDENTIFIER>"],
components: [
{
/: “*”
}
]
}
]
}
}
[For iOS12 and earlier]
{
"applinks": {
"apps": [],
"details": [{
"appID": "<TEAM_DEVELOPER_ID>.<BUNDLE_IDENTIFIER>",
"paths": ["*"]
},
{
"appID": "<TEAM_DEVELOPER_ID>.<BUNDLE_IDENTIFIER>",
"paths": ["/files"]
}]
}
}
[ apple-app-site-association ํ์ผ ๋ด์ฉ ๊ตฌ์ฑ]
apps : ์ ๋๋ฒ์ ๋งํฌ์์๋ ์ฌ์ฉํ์ง ์์ง๋ง ๋น ๋ฐฐ์ด๋ก๋ผ๋ ๋ฐ๋์ ๋ช ์๋์ด ์์ด์ผ ํจ.
details : ์น์ฌ์ดํธ์์ ํธ๋ค๋ง๋๋ ์ฑ๋ค์ ๋ชฉ๋ก์ ๋๋ค. ์ฆ ํ ์น์ฌ์ดํธ์์ ์ ๋๋ฒ์ ๋งํฌ๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋ฌ ์ฑ ์ฐ๋์ด ๊ฐ๋ฅ.
appId : ์ฑ์ ์๋ณ ๊ฐ์ผ๋ก ํ ์์ด๋์ ๋ฒ๋ค id๋ฅผ ์ฌ์ฉ. ํ์:<TEAM_DEVELOPER_ID>.<BUNDLE_IDENTIFIER>
paths : ์ฑ์์ ์ง์ํ๋ ์น์ฌ์ดํธ ๊ฒฝ๋ก(*: ์ ์์น ๊ฐ๋ฅ)
*appId >>
*TEAM_DEVELOPER_ID ํ์ธํ๊ธฐ >>
Apple Developer ์ฌ์ดํธ(https://developer.apple.com/) > Account > Certificates, IDs & Profiles > Identifiers
์ฐ์ธก ์๋จ์์ ํ์ธ ๊ฐ๋ฅ
*BUNDLE_IDENTIFIER ํ์ธํ๊ธฐ >>
์ฐ๊ฒฐํ ์ฑ Xcode Project > Target > General > Identity
2. iOS ์ฑ์ Capability - Associated Domains ์ถ๊ฐ
์ ๋๋ฒ์ ๋งํฌ๋ฅผ ์ง์ํ๋ ค๋ฉด, ๋จผ์ ์ฑ์์ ์ ๋๋ฒ์ ๋งํฌ๋ฅผ ํ์ฉํ ๋๋ฉ์ธ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
์ถ๊ฐํ ๋๋ฉ์ธ ์ฃผ์๋ฅผ ํตํด ์ฑ์ด ์ฐ๊ฒฐ๋ฉ๋๋ค!
Project > Target > Signing&Capabilities> Associated Domains์ Domains ์ถ๊ฐ
applinks ํ์ ์์)
//applinks:your_dynamic_links_domain
applinks:example.com
applinks:*.example.com
์ด๋ ๊ฒ ํ๋ฉด, ์ฌ์ฉ์๊ฐ ์ฑ ์ค์น ์, ์ฌ๊ธฐ์ ๋ฑ๋ก๋ ๋๋ฉ์ธ์ ํด๋น url์ .well-known/apple-app-site-association ๊ฒฝ๋ก์ ๊ฐ์ AASA ํฌ๋งท์ ํ์ธํ๋ผ๊ณ ํ์ผ์ ๋ํ ์์ฒญ์ ๋ณด๋ด๊ฒ ๋ฉ๋๋ค.
* ๋ฑ๋ก์ด ์ ๋๋์ง entitlements๋ก ํ์ธํ๊ธฐ!
------------- ์ฌ๊ธฐ๊น์ง ์ค์ ํ๋ฉด ์ฑ๊ณผ ์น์ด ์ฐ๊ฒฐ๋์ด ์๋ก์ ์ ๋ณด๋ฅผ ์๊ณ ์์! -------------
* ํ ์คํธ ๋ฐฉ๋ฒ : ๋ฉ๋ชจ์ฑ์์ ์ค์ ํ ๋๋ฉ์ธ์ฃผ์๋ฅผ ์ ๋ ฅ ํ ํด๋ฆญ์ ์ฐ๊ฒฐํ ์ฑ์ด ์คํ๋๋ฉด ์ฐ๊ฒฐ ์ฑ๊ณต!๐ป
3. ์ฑ์์ ๋์ ๋งํฌ URL handle ํ๊ธฐ
์ ๋๋ฒ์ ๋งํฌ๋ก ๋ค์ด์ค๋ ์์ฒญ์ SceneDelegate์ scene:continueUserActivity ๋ฉ์๋์์ ์ ๋ฌ๋ฐ์ NSUserActivity ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.๐
*NSUserActivitys :
- ํน์ ์์ ์ ์ฑ ์ํ๋ฅผ ๋ํ๋ด๋ ํํ
- user activity ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ content๋ณด๊ธฐ, ๋ฌธ์ ํธ์ง, ์นํ์ด์ง ๋ณด๊ธฐ ๋๋ ๋น๋์ค ์์ฒญ๊ณผ ๊ฐ์ด ์ฌ์ฉ์๊ฐ ์ํํ ์์ ์ ๋ํ ์ ๋ณด๋ฅผ ์บก์ณ
์ ๋ฌ๋ userActivity์ path์์๋ฅผ ๊ฒ์ถํ์ฌ ์คํจํ๋ฉด return false๊ตฌ๋ฌธ์ด ์คํ๋๊ณ ์ฑ์ ์ ๋ฌ๋ activity๋ฅผ ํธ๋ค๋งํ์ง ๋ชปํ๊ฒ ๋๋ค.
NSURLComponents * components = [NSURLComponents componentsWithURL:userActivity.webpageURL resolvingAgainstBaseURL:YES];
path = components.path;
[์ ์ฒด์ฝ๋ - iOS13 and later]
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
//์ ๋ฌ๋ userActivity์ ๊ฐ์ ๊ฒ์ฆ
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let components = URLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let path = components.path else {
return
}
//๋๋ฉ์ธ ์ฃผ์์ ์ฟผ๋ฆฌ๊ฐ์ ๋ฐ์
let params = components.queryItems ?? [URLQueryItem]()
print("path = \(incomingURL)")
print("params = \(params)")
// ๊ฒ์ถํ path๋ฅผ ์ด์ฉํ์ฌ ์ด๋ค computer๋ฅผ ๋ณด์ฌ์ฃผ์ด์ผ ํ๋์ง ๊ฒฐ์
if let computer = ItemHandler.sharedInstance.items.filter({ $0.path == components.path}).first {
self.presentDetailViewController(computer)
return true
}
// ๋ณด์ฌ์ฃผ์ด์ผํ computer๊ฐ ๊ฒฐ์ ๋์ง ๋ชปํ๋ฉด, application์ URL์ openํ๋๋ก ์์ฒญํ์ฌ ์ฌํ๋ฆฌ ์ฐ๊ฒฐ
let webpageUrl = URL(string: "http://rw-universal-links-final.herokuapp.com")!
application.openURL(webpageUrl)
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for urlContext in URLContexts {
let urlToOpen = urlContext.url
// ...
}
}
[For iOS9 and later]
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
NSURL *url = userActivity.webpageURL;
// ...
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
// ...
}
Reference Site
developer.apple.com/ios/universal-links/
https://developer.apple.com/videos/play/wwdc2020/10098/