๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐ŸŽ iOS/iOS Programming

[iOS/Swift] ์œ ๋‹ˆ๋ฒ„์…œ ๋งํฌ(Universal Link) ์ ์šฉํ•˜๊ธฐ, ๋™์  ๋งํฌ ์ˆ˜์‹ ํ•˜๊ธฐ

728x90
์œ ๋‹ˆ๋ฒ„์…œ ๋งํฌ(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

  1. ์›น์„œ๋ฒ„ ์ž‘์—… - Web Server์— AASA(apple-app-site-association) ํŒŒ์ผ ์ถ”๊ฐ€
  2. ์•ฑ ์ž‘์—… - 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/

 

Universal Links - Apple Developer

Universal Links for Developers Seamlessly link to content in your app or on your website. With universal links, you can always give users the most integrated mobile experience, even when your app isn’t installed on their device.

developer.apple.com

https://developer.apple.com/videos/play/wwdc2020/10098/

 

What's new in Universal Links - WWDC20 - Videos - Apple Developer

Universal Links help people access your content, whether or not they have your app installed. Get the details on the latest updates for...

developer.apple.com

 

๋ฐ˜์‘ํ˜•