アプリ間データ連携、URL Schemeについて (社内勉強会向け)


## URL Scheme とは


iOSの場合、アプリ固有の Custom URL Scheme(例:moffapp://)を使ってアプリを識別して起動できる。
//以降のURL部分で、アプリに対して情報を受け渡すことが可能。

いわゆるアプリ起動専用のURLのことと捉えればよいかと思います。


iOSでは、似たような機能として、Universal Links というのがあります。(iOS9以降に登場)
Universal Linksでは、URL Schemeと違って、Webサイトやアプリを一意に特定するもので、アプリが存在すれば直接アプリを開き、アプリがなければWebサイトを開くといった事が可能になりました。
(参考: https://qiita.com/mono0926/items/2bf651246714f20df626)


## なぜ、いまさら URL Schemeか？

最初にURL Schemeを社内で使おうと思った時期は、2014年に遡るのですが、「社内でリリースしたアプリが増えてきた」という状況で、アプリ間で云々という課題が出てきたときの解決策の一つとして周知しておこうと思ったからです。

あと、外部機器と連携したデータ取得の際に使われてる仕組みだったので、リマインドの意味を込めて。




## 単純に送受信を行うサンプルアプリ

例として、アプリ1とアプリ2を相互に起動するアプリを作ります。

### URL Schemeの決定

Custom URL Schemeを使って、
iOSアプリ間でデータ連携ができる簡単なサンプルアプリを作ってみたいと思います。

まずは、URLスキームをそれぞれ決めます。
アプリ1とアプリ2にそれぞれ下記のURLを設定します。

- moffurlschemesample1://
- moffurlschemesample2://

### 各開発アプリのディレクトリ

次に、それぞれのアプリの開発ディレクトリを決めて、Xcodeで新規アプリを作成します。

アプリ1. DevTest/moffurlschemesample1
アプリ2. DevTest/moffurlschemesample2


### XcodeでのURL Scheme設定

info.plistに下記項目を追加します。

例: アプリ1

```
<array>
	<dict>
		<key>CFBundleURLName</key>
		<string>mobi.moff.moffurlschemesample1</string>
		<key>CFBundleURLSchemes</key>
		<array>
			<string>moffurlschemesample1</string>
		</array>
	</dict>
</array>

<array>
	<string>moffurlschemesample2</string>
</array>
```

アプリ2は、`moffurlschemesample2`に書き換えて下さい。

### サンプルコード


`AppDelegate.swift`


```
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    
    let message = url.host?.removingPercentEncoding
    let alertController = UIAlertController(title: "Incoming Message", message: message, preferredStyle: .alert)
    let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
    alertController.addAction(okAction)
    
    window?.rootViewController?.present(alertController, animated: true, completion: nil)
    
    return true
}
```


`ViewController.swift`に下記のように追加し、画面にButtonを追加して、launchApp()をTouchDownに紐付けます。

```
    let url:String = "moffurlschemesample1://"

    @IBAction func launchApp() {
        let alertPrompt = UIAlertController(title: "Open App", message: "You're going to open \(self.url)", preferredStyle: .actionSheet)
        let confirmAction = UIAlertAction(title: "Confirm", style: UIAlertActionStyle.default, handler: { (action) -> Void in
            
            if let url = URL(string: self.url) {
                if UIApplication.shared.canOpenURL(url) {
                    UIApplication.shared.open(url, options: [:], completionHandler: nil)
                }
            }
        })
        
        let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
        
        alertPrompt.addAction(confirmAction)
        alertPrompt.addAction(cancelAction)
        
        present(alertPrompt, animated: true, completion: nil)
    }
```


### 実行

![urlscheme-sample.gif](https://qiita-image-store.s3.amazonaws.com/0/15008/a5cb244c-9985-a572-44ea-b65be684e9d3.gif)



### データの送受信

アプリ1の`ViewController.swift`のurlを下記のように変更します。


```
    let url:String = "moffurlschemesample2://transfer?userHeight=170"
```

アプリ2の`AppDelegate.swift`を下記のように変更します。

```
        let arr = url.query!.components(separatedBy:"&")
        var data = [String:Any]()
        for row in arr {
            let pairs = row.components(separatedBy:"=")
            data[pairs[0]] = pairs[1]
        }
        let userHeight = data["userHeight"]
        
        let message = "url : \(url.absoluteString)\n"
                + "scheme : \(url.scheme!)\n"
                + "host : \(url.host!)\n"
                + "query : \(url.query!)\n"
                + "userHeight : \(userHeight!)\n"

```

hostには、transfer
queryには、userHeight=170
などが表示されます。


---

### 制約

URL Schemeの制約として、下記の制約があります。

- [Apple予約名](https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007899)
- スキーム名の重複
- アプリがインストールされていること
など

ですので、インストールされてない場合にストアに誘導したい場合は、冒頭で書いたUniversal Linksを使うのがよいです。


## 体験タイム

社内では、下記のリンク先の血圧計や体組織計を使ってアプリ間データ連携を体験しました。

https://www.healthcare.omron.co.jp/corp/news/2018/0725.html

## 最後に

この記事を書いた日の、社内のエンジニアの状況ですが、

iOSアプリやUnityのiOSプラグインなど開発はしているものの、ほとんどのアプリをUnityで開発していてiOS専業のエンジニアがいないという状況から、もっと新しいiOSの便利な機能は時々勉強会などでキャッチアップしていこうということをやっています。

ということで、Moffでは、ヘルスケア関連のプロダクトやサービスを開発するエンジニアを募集しています。

https://www.wantedly.com/projects/240435