はじめに
.NET MAUIでiOS向けアプリを開発中,WebアプリからiOSアプリを呼び出す必要がでてきたため,ユニバーサルリンクを実装しました.その際にWebサーバーをAzure Blob Storageの静的サイトでできないかなと思いました.
ユニバーサルリンクとは
ユニバーサルリンク(Universal Links)は,Webサイトへのリンクをクリックしたときに,そのリンクに対応するiOSアプリがインストールされていれば直接アプリを起動し,インストールされていなければWebサイトを表示するという機能です1.
仕組み
- WebサーバーにApple App Site Association (以下: AASA)ファイルを置いておきます
- Apple CDNが定期的にWebサーバーのAASAファイルをクロールします
- iPhoneが,アプリインストール時にAppleCDNからAASAファイルを取得します
- Webサイト上で特定のURLを踏むとAASAファイルを使ってアプリを立ち上げる
簡単に図示すると以下のような感じになります.
WebサーバーをAzure Blob Storageでやってみる
上記の通り,AppleCDNがアクセスできるWebサーバを準備する必要があるのですが,今回はお手軽に(?)用意できるであろうAzure BlobStorageの静的サイト機能を使っていきたいと思います.
モチベーション
Azure Storageで安価に静的サイトを公開できます2.
UniversalLink用のAASAファイルを設置する目的でWebサーバが必要となるのですが,他の用途では使わないので,Azure Blob Storageの静的サイト機能で代替できそうだと思いました.
Azure Storage構築
ストレージアカウントの作成
まずはストレージアカウントを作成します.
ストレージアカウントは基本的にデフォルトで大丈夫だと思いますが,必要に応じて値を変更します.基本情報のプライマリサービスは 「Azure Blob Storage または Azure Data Lake Storage Gen 2」 とします.
作ったものがこちらです。アカウントの種類が StorageV2 (汎用 v2)
であることを確認します(上位のものでも大丈夫です).
静的サイトを有効化
概要の下の方の機能タブから静的Webサイトを選択します.
ここで有効化します.プライマリエンドポイントが今回のUniversalLinkのドメインになります.
hogehoge.z11.web.core.windows.net
のような感じになると思います.
※ カスタムドメインを使用することも可能ですが,今回はスキップします.
静的サイトを有効化すると,$Web
フォルダができます.
AASAファイルを配置
AASAファイルを準備します3.
内容は JSON ですが,ファイルに拡張子はつけないで保存します.ファイル名は apple-app-site-association
です.
今回はリンクURLによる制限はつけずにどんなURLでも呼び出し可能とします.
{
"webcredentials": {
"apps": [
"アプリのID"
]
},
"applinks": {
"apps": [],
"details": [
{
"appIDs": [
"アプリのID"
],
"components": [
{
"/": "*"
}
]
}
]
}
}
$Web
コンテナを開いてアップロードします.
右下のアップロード先のフォルダは .well-known
にします.フォルダが存在しない場合でも,自動的に作成してくれます.
アップロードしたファイルを確認します.
階層が $Web/.well-known/apple-app-site-association
となっていればOKです.
最後に ContentType
を指定します.
ファイルをクリックするとプロパティが開くので編集します.
application/json; charset=utf-8
にします.application/json
は必須だと思います. charset=utf-8はなくても大丈夫ですが,念の為に指定します.
$WebをPublic化
Apple CDNがアップロードしたAASAファイルを取得できるようにします. コンテナを選択してアクセスレベルを変更します. デフォルトだとプライベートになっていると思うので,コンテナしておきます.
AASAファイルを確認します.
先程のプライマリエンドポイントに .well-known/apple-app-site-association
つけてURLバーに入れて,AASAファイルが見えることを確認します.
https://{ドメイン}/.well-known/apple-app-site-association
Apple CDNを確認
アップロード後,しばらくするとAASAファイルをApple CDNが回収してくれます.
そのキャッシュを確認できます.
https://app-site-association.cdn-apple.com/a/v1/{アップロードした値}.z11.web.core.windows.net
以上で環境構築は完了です.
Mauiアプリの設定
MauiプロジェクトのPlatformフォルダのIOSフォルダ直下に Entitlements.plist
を作成します.
内容は以下の通りです.
先程のプライマリエンドポイントのを記載します.「https://」は不要です.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:hogehoge.z11.web.core.windows.net</string>
</array>
</dict>
</plist>
Entitlements.plistを作りたくない人はcsprjで設定することもできます.
MSドキュメントより引用4
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios' Or $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<!-- For debugging, use '?mode=developer' for debug to bypass apple's CDN cache -->
<CustomEntitlements
Condition="$(Configuration) == 'Debug'"
Include="com.apple.developer.associated-domains"
Type="StringArray"
Value="applinks:hogehoge.z11.web.core.windows.net?mode=developer" />
<!-- Non-debugging, use normal applinks:url value -->
<CustomEntitlements
Condition="$(Configuration) != 'Debug'"
Include="com.apple.developer.associated-domains"
Type="StringArray"
Value="applinks:hogehoge.z11.web.core.windows.net" />
</ItemGroup>
※ ?mode=developer
をつけておくとApple CDNをバイパスして,直接WebサーバからAASAファイルを取得するようになります.
UniversalLinkで起動されたときのハンドラーを書きます.
MauiProgram.csを編集します。
MSドキュメントより引用4
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Extensions.Logging;
namespace MyNamespace;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureLifecycleEvents(lifecycle =>
{
#if IOS || MACCATALYST
lifecycle.AddiOS(ios =>
{
// Universal link delivered to FinishedLaunching after app launch.
ios.FinishedLaunching((app, data) => HandleAppLink(app.UserActivity));
// Universal link delivered to ContinueUserActivity when the app is running or suspended.
ios.ContinueUserActivity((app, userActivity, handler) => HandleAppLink(userActivity));
// Only required if using Scenes for multi-window support.
if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsMacCatalystVersionAtLeast(13))
{
// Universal link delivered to SceneWillConnect after app launch
ios.SceneWillConnect((scene, sceneSession, sceneConnectionOptions)
=> HandleAppLink(sceneConnectionOptions.UserActivities.ToArray()
.FirstOrDefault(a => a.ActivityType == Foundation.NSUserActivityType.BrowsingWeb)));
// Universal link delivered to SceneContinueUserActivity when the app is running or suspended
ios.SceneContinueUserActivity((scene, userActivity) => HandleAppLink(userActivity));
}
});
#endif
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
#if IOS || MACCATALYST
static bool HandleAppLink(Foundation.NSUserActivity? userActivity)
{
if (userActivity is not null && userActivity.ActivityType == Foundation.NSUserActivityType.BrowsingWeb && userActivity.WebPageUrl is not null)
{
HandleAppLink(userActivity.WebPageUrl.ToString());
return true;
}
return false;
}
#endif
static void HandleAppLink(string url)
{
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out var uri))
App.Current?.SendOnAppLinkRequestReceived(uri);
}
}
HandleAppLinkメソッドから.
AppクラスのOnAppLinkRequestReceivedを呼んでくれるのでOverrideすることで受け取ったURLのハンドルができます.
クエリパラメータもらうときなどはここで処理するとよいと思います.
protected override void OnAppLinkRequestReceived(Uri uri)
{
//uriの処理が必要であれば
}
Mauiアプリの修正は以上です.
Apple DeveloperアカウントのアプリIDに、関連ドメインの使用を許可
UniversalLinkを使うにはApple上のアプリに使用を許可する必要があります.
手順は以下の通りです4.
- Web ブラウザでApple Developer アカウントにログインし、 「証明書、ID、およびプロファイル」ページに移動します
- 「証明書、識別子、プロファイル」ページで、[識別子]タブを選択します
- 「識別子」ページで、アプリに対応するアプリ ID を選択します
- 「App ID 構成の編集」ページで、「関連ドメイン」機能を有効にし、「保存」ボタンを選択します
- profileに反映させます.Editから先程設定したApp IDを選択します
Enabled CapacitiesにAssociated DomainがあればOKです
- 最後にVisual Studioのプロファイルを更新します
アプリをリリースする
あとはアプリをリリースするだけです.デバッグでも大丈夫です.
今回の場合のユニバーサルリンクのURLはブロブストレージのプライマリエンドポイントになります.
https://hogehoge.z11.web.core.windows.net/
これをWebサイトに埋め込んでおきます.
アプリがインストールされている状態であれば,iPhoneのメモ帳でも確認ができます.
リンク用のURLを打ち込んで長押しするとアプリを開くという選択肢がでてきます.
最後に
Azure Blob Storageの静的サイト機能で簡単にUniversalLink用のWebサーバを立ち上げられました.Azure環境がある場合は選択肢に入れてもいいかなと思います.