個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Xamarin に関する事を中心に書いています。 Microsoft MVP for Development Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Xamarin.iOS でメモリーリークをコントロールするために知っておきたいこと

はじめに


こんにちは、@hiro128_777です。


この記事は「Xamarin その1 Advent Calendar 2018」の21日目になります。


Xamarin.iOS は基本的には、Objective-C の薄い Wrapper であることは間違いありませんが、オブジェクトがマネージドの世界とネイティブの世界でそれぞれに存在し、互いに関係性を持ちながら、インスタンスが生成・破棄されているので、GC の挙動を正しく理解しておかないとメモリリークをコントロールするのが難しくなる弱点があります。今回はそこについてのお話です。


この記事の内容については Xamarin.iOSソースコードhttps://github.com/xamarin/xamarin-macios/blob/master/runtime/runtime.mに記載されています。


これは、Xamarin.iOSGC の挙動について調べるために、Xamarin.iOSソースコード を読んでいるときに偶然発見しました。これを読むと Xamarin.iOSGC の挙動のクセがわかると思います。


このような超重要な事が、そんなところに書いてあってもなかなか皆さんが目にする機会はないと思い、今回日本語に翻訳し説明を追加しました。
※わかりやすくするために色々補完して翻訳していますので、原文に忠実な翻訳ではありません。



GC の挙動にかかわる iOS アプリののアーキテクチャーについてはこちらの公式ドキュメントもご参照いただくとより理解が深まります。
docs.microsoft.com



また、より詳しい記事を書き始めましたのでこちらもご覧ください。
hiro128.hatenablog.jp


参照カウンタについての注意点

Wrapper type と User type


Wrapper type は、UIViewUIButton のような Objective-C の組み込み型をラップしたもので、マネージドの世界では、ネイティブオブジェクトのインスタンスへのハンドルだけを持っています。


一方、User type は、UIViewUIButton のような Wrapper type を継承し派生した型で、Objective-C に対応する型が無いものを指し、ネイティブオブジェクトのインスタンスへのハンドルの他に、マネージドな世界だけで管理されている、フィールド、プロパティやメソッドを持っています。


Xamarin.iOS における GC の挙動を考える上で、この2つの型の区別はとても重要となります。


Wrapper type の寿命について


これはとても簡単です。


Wrapper type のインスタンスの寿命は、ネイティブオブジェクトの寿命とはリンクしてません。


Wrapper type のインスタンスは、マネージドな世界にネイティブオブジェクトへのハンドル以外には何も保持していませんので、GC の判断によっていつでも自由に解放できます。もし、後の段階で再びそのオブジェクトが必要になった場合には Wrapper type のインスタンスが再度作成されるだけです。


User type の寿命について


こちらは簡単ではありません。


User type のオブジェクトは マネージドな世界にユーザーが定義した状態を含む可能性があるため GC の判断によって自由に解放することはできません。
よって、User type のオブジェクトを必要な間はずっと生かし続けることを保証する必要があります(マネージオブジェクトは強力な GCHandle によって保持されます)。


この場合の問題は「どうやって不要になったタイミングを判断するのか」ということになります。
これには、歴史的に2つのケースが存在します。

ケース1


ユーザーが Dispose を呼び出すと、マネージオブジェクトの参照が解放され、ネイティブオブジェクトとマネージオブジェクトの間のリンクが切断され、GC がそのオブジェクトを解放できるようになります。(そのマネージオブジェクトを保持している他のオブジェクトが何もなければ)


ケース2


参照カウンタが1に達すると、マネージオブジェクトからの参照が唯一の参照であり、ネイティブコードは当該オブジェクトを再び使用しないと安全に仮定できます。よって、リンクを解除し、ネイティブオブジェクトを解放して、GC がマネージオブジェクトを解放できるようにします。


ケース1で、ユーザーが Dispose を呼び出した後、ネイティブコードが何らかの理由でそのオブジェクトを使用しようとすると、問題が発生します。 Xamarin.iOS は対応するマネージオブジェクトが存在しないことを検出し、それを(再)作成しようとします。ですが、これは失敗し、プロセスを終了させる例外がスローされます(この時、スタックにはマネージドフレーム/例外ハンドラが存在しない可能性があります)。


解決策としては、Disposeを呼び出すときに、次のことを行う必要があります。

  • マネージオブジェクトの参照を解放する。
  • ネイティブオブジェクトの参照カウンタが0に達するまで、ネイティブオブジェクトと管理オブジェクトの間のリンクを切断しない。


これにより、ネイティブオブジェクトが存続している限り、マネージオブジェクトを引き続き参照することができます。

User type の挙動についての注意点

User type のオブジェクトは、次のいずれかの条件が発生したときにネイティブオブジェクトへの参照を解放するようになっています。

  • Dispose がオブジェクトに対して(手動で)呼び出されたとき。
  • マネージオブジェクトの Handle プロパティが変更されたとき。
  • GC がオブジェクトを解放したとき。これは、参照カウンタが1でその参照がマネージオブジェクトによる参照である場合にのみ発生します。

(それ以外の時は、マネージオブジェクトに強力な GCHandle があるため、GC の解放処理はブロックされます。)


User type のオブジェクトは、次のいずれかの条件が発生すると、ネイティブ <-> マネージオブジェクトのディクショナリーからオブジェクトが削除されます。

  • ネイティブオブジェクトが dealloc されたとき(参照カウンタは0になります)。
  • マネージオブジェクトの Handle プロパティが変更されたとき(変更前の Handle 値とマネージオブジェクトの間のリンクがディクショナリーから削除されます)。

 

User type のオブジェクトは、ネイティブの世界の2つの情報を追跡し続けています。

  • マネージオブジェクトへの GCHandle。
  • マネージオブジェクトのインスタンスが存在するかどうか。


User type のオブジェクトはすでに GCHandle を保存する Objective-C インスタンス変数を持っているので、別のインスタンス変数を作成しません。
User type のオブジェクトはマネージオブジェクトへの参照(MANAGED_REF_BIT)があるかどうかを GCHandle の1ビットで保存します。



まとめ


Wrapper type は単に、ラッパーですので難しいことは何もありません。
問題は、User type です。こちらは、マネージドな世界とネイティブの世界にそれぞれ状態を持つことになるので、GC の挙動が複雑になっています。


オブジェクトの解放がうまく行われない時には、今回の内容を確認してたいただくとヒントになると思います。実際私もこれを理解してからは、解放できなくて困ったり、強引な Dispose で Exception に悩まされることが無くなりました。


以上です。

とっても簡単なので、Microsoft Docs にプルリクエスト を送ってみませんか。

はじめに


みなさま、Microsoft Docs はご活用されていますでしょうか。


aka.ms


Microsoft Docs はマイクロソフトが提供している各技術カテゴリごとのドキュメントです。それぞれのカテゴリについて充実した詳しいドキュメントが提供されています。

Xamarin につきましても Xamarin.Forms, Xamarin.iOS, Xamarin.Android, Xamarin.Mac, クロスプラットフォームと、5カテゴリについてそれぞれ詳しいドキュメントが提供されています。

その内容ですが、各カテゴリごとに環境のセットアップから、最初のアプリを作成するまでといったチュートリアル的なものから、それぞれのライブラリの API 設計の概念といった高度な内容までしっかりドキュメントが存在します。

よって、ドキュメントを熟読すれば、未経験でも Xamarin を始められるようなドキュメントになっています。


ですが、このドキュメントにはひとつ残念な問題があります。それは、基本的に「機械翻訳である」という事です。もちろん機械翻訳いってもひと昔のものとは違い、AIなどの活用でかなり精度は上がっており、不自然ながらも意味は読み取れるような文章にはなっていますが、読みにくいのは事実です。

もちろん、読みやすい日本語で読めるならそれに越したことはありません。そうであれば、自分の手で読みやすい日本語に修正してしまえばいいと言うのが本日のお話です。



いやいや、私は英語に自信がないので日本語へのローカライズにコントリビュートするとか無理なんですけど・・・


そんなことありません、英語が苦手でも大丈夫です。日本語の知識だけでも何とかなる部分もたくさんあります。

例えば 「章 1 です」-> 「第1章」などの修正であれば、英語力など関係なく修正可能です。このような不自然な表現が少しでも減るだけで、ドキュメントはグッと読みやすくなります。

今後、自分が読むときのストレスを軽減する意味でもプルリクエストを送る意味があると思っています。



間違った翻訳を送ってしまうのが怖いので、ちょっと無理かな・・・


そのような心配も無用です。プルリクエストを受け付ける側の担当の方がきちんと修正した日本語をチェックしてくれて、「適切」と思われる部分且つ、マイクロソフトの表現規約に沿ったものだけを取り込んでくれます。

ですので、間違った翻訳が取り込まれてしまう可能性は稀です。もちろん、可能な限り正しい内容のプルリクエストを送る必要がありますが、間違いを過度に恐れる必要はありません。

気軽な気持ちでまずは一度プルリクエストを送って見てはいかがでしょうか。



Microsoft Docs にプルリクエスト を送るのはとても簡単です。

Xamarin のドキュメントの場合の例


  1. https://github.com/MicrosoftDocs/xamarin-docs.ja-jp を自分のリポジトリに Fork します。

  2. 修正用のトピックブランチを切ります。

  3. 修正用のトピックブランチで修正し、自分のリポジトリにコミットします

  4. Github のForkした自分のリポジトリWebブラウザでアクセスすると、プルリクエストをオープンできるようになっているので、そのままプルリクエストをオープンします。

    コミットメッセージ、コメントともに、「fixed inappropriate Japanese」とでも書いておけば十分通じます。(正直この英語表現が正しいかはよくわかっていませんが・・・)

  5. 数日でプルリクエストがレビューされてマージされ、さらにその数日後に共同作成者のアイコンにあなたのアイコンが追加されます!



おわりに


修正のプルリクエストは、心構えをしてわざわざ時間をとって取り組むと言うよりは、ちょっとした時間にササッと見て気になる1センテンスだけをプルリクエスト送る方が気楽に取り組めると思います。

基本は自分が読んで意味が分からなかった表現に対してプルリクエストを遅ればみんな幸せになれるのではないでしょうか!

というわけで、皆さんも是非 Microsoft Docs にプルリクエスト を送って、日本語ドキュメントの改善にチャレンジしてみてはいかがでしょうか。

Visual Studio App Center で、自動ビルド後に Android の自動実機UIテストを実行する方法

はじめに

こんにちは、@hiro128_777です。

12月11日(土)に、品川で Xamarin.iOS、Xamarin.AndroidVisual Studio App Center ハンズオンを開催いたします!

Visual Studio for Mac による Xamarin.iOS、Xamarin.Android のモバイル アプリの Visual Studio App Center を利用した自動ビルドおよび自動 UI テストの手順までを体験することができるハンズオンとなります。ご興味がある方はぜひご参加下さい!

jxug.connpass.com



では本題に入りましょう。


Visual Studio App Center で、自動ビルド後に Android の自動実機UIテストを実行する方法


Visual Studio App Center で、自動ビルドを設定したら当然ながら次は自動でテストを走らせたくなりますよね!

というわけで、今回は自動ビルド後に Android の自動実機UIテストを実行する手順についてご説明いたします。

今回の内容は下記リポジトリを利用しますので clone するか DL して下さい。
github.com


Visual Studio App Center のテスト設定に実機UIテストを走らせるデバイスの組み合わせのセットを登録します

Visual Studio App Center では、1回のテストで、複数の実機の自動UIテストを走らせることができますので、テストを走らせる実機を選択して登録しておきます。


Visual Studio App Center のテスト設定のデバイスセット設定を開きます。
f:id:hiro128:20181122180214p:plain


Set name を設定し、テストを実行するデバイスにチェックを入れ New device set で保存します。
f:id:hiro128:20181122180231p:plain


Device set が登録されていることを確認します。
f:id:hiro128:20181122180259p:plain


Visual Studio App Center にログインするキーを準備します。


自動実行されるシェルスクリプトが自動テストを実行するときに、事前に取得しておいたキーを使って App Center にログインします。そのキーをスクリプトから利用できるように環境変数に登録しておきます。



「Account settings」を開きます。
f:id:hiro128:20181030180748p:plain



「New API token」を開きます。
f:id:hiro128:20181030193140p:plain



APIの利用目的の説明とアクセス権を設定し保存します。
f:id:hiro128:20181030180958p:plain



キーが表示されるのでコピーしメモ(保管)しておきます。キーは画面を閉じると2度と表示されないのでご注意ください。
f:id:hiro128:20181030180945p:plain



キーが登録されていることを確認します。
f:id:hiro128:20181030180930p:plain



次にビルド設定を開きます。
f:id:hiro128:20181122180731p:plain



環境変数にキーを設定して、保存します。
f:id:hiro128:20181122180653p:plain



keystore ファイルを準備します。

実機での自動UIテストを行うには keystore が必要になりますので作成します。


Visual Studio for Mac で、Android のプロジェクトを Release に設定して、ビルド ->  発行のためのアーカイブ を押下します。
f:id:hiro128:20181122180757p:plain




「署名と配布」を押下します。
f:id:hiro128:20181122180814p:plain




アドホックを選択して「次へ」を押下します。
f:id:hiro128:20181122180843p:plain




「キーの新規作成」を押下します。
f:id:hiro128:20181122180853p:plain




項目を入力し、「OK」を押下します。
f:id:hiro128:20181122180916p:plain



エイリアス名を右クリックし、「エイリアス情報を表示」を押下します。
f:id:hiro128:20181122180925p:plain



keystore ファイルの場所が表示されるので、ファイルを取得します。
f:id:hiro128:20181122180935p:plain



App Center のビルド設定を開きます。
f:id:hiro128:20181122180950p:plain



keystore ファイルをアップします。
key alias, Key password は「新しい証明書を作成」画面で入力したものを入力します。
Keystore パスワードは Key password と同じものを入力してください。
入力したら保存します。
f:id:hiro128:20181122181002p:plain




build 後に自動実行されるシェルスクリプトを準備します。


Visual Studio App Center には ビルドするcspojと同じ階層に、appcenter-post-build.shという名前でシェルスクリプトを配置しておくと、自動認識し build 後に自動実行してくれる機能があります。
よって、appcenter-post-build.shに、自動実機UIテストを実行する処理を書きます。

/src/Finish/Droid/appcenter-post-build.sh

#!/usr/bin/env bash

# Post Build Script

# Exit immediately if a command exits with a non-zero status (failure)
set -e 

##################################################
# variables

appCenterLoginApiToken=$AppCenterLoginToken # this comes from the build environment variables
appName="TomohiroSuzuki128/XamAppCenterSample2018Droid"
deviceSetName="TomohiroSuzuki128/my-devices-android"
publishedAppFileName="com.hiro128777.XamAppCenterSample2018.apk"
sourceFileRootDir="$APPCENTER_SOURCE_DIRECTORY/src/Finish"
uiTestProjectName="UITests"
testSeriesName="all-tests-android"
##################################################

echo "##################################################################################################"
echo "Post Build Script"
echo "##################################################################################################"
echo "Starting Xamarin.UITest"
echo "   App Name: $appName"
echo " Device Set: $deviceSetName"
echo "Test Series: $testSeriesName"
echo "##################################################################################################"
echo ""

echo "> Build UI test projects"
find $sourceFileRootDir -regex '.*Test.*\.csproj' -exec msbuild {} \;

echo "> Run UI test command"
# Note: must put a space after each parameter/value pair
appcenter test run uitest --app $appName --devices $deviceSetName --app-path $APPCENTER_OUTPUT_DIRECTORY/$publishedAppFileName --test-series $testSeriesName --locale "ja_JP" --build-dir $sourceFileRootDir/$uiTestProjectName/bin/Debug --uitest-tools-dir $sourceFileRootDir/packages/Xamarin.UITest.*/tools --token $appCenterLoginApiToken 

echo ""
echo "##################################################################################################"
echo "Post Build Script complete"
echo "##################################################################################################"

このスクリプトがやっていることは、

  • UIテストプロジェクトをビルドする。
  • App Center に、自動UIテストのコマンドを発行し、実行させる。

の2つです。

このスクリプトを含んだリポジトリをプッシュすると、以下のように、App Center 側で認識されます。
f:id:hiro128:20181122181034p:plain


スクリプトデバッグ方法。

このようなスクリプトを自作するときに一番困るのが、

  • 「指定したファイルが見つからない」エラーが発生すること
  • 環境変数の中身がよくわからないこと

です。

よってスクリプトを自作するときには、下記のように環境変数ディレクトリの中身をコンソールに表示させながらスクリプトを書くことで効率よくデバッグできます。

# for test
echo $APPCENTER_SOURCE_DIRECTORY
echo ""
files="$APPCENTER_SOURCE_DIRECTORY/src/Finish/UITests/*"
for filepath in $files
do
  echo $filepath
done

ビルドを実行し、テスト結果を確認してテストが正しく実行されていることを確認します。


正しく実行されていれば、以下のようにテスト結果が確認できます。
f:id:hiro128:20181122181839p:plain



また、テストコード内に以下のように、app.Screenshot("<スクリーンショット名>")と記述することで、スクーンショットが自動で保存されます。

[Test]
public async void SucceedTranslate()
{
    await Task.Delay(2000);
    app.Screenshot("App launched");
    await Task.Delay(2000);
    app.Tap(c => c.Marked("inputText"));
    await Task.Delay(2000);
    app.EnterText("私は毎日電車に乗って会社に行きます。");
    await Task.Delay(2000);
    app.Screenshot("Japanese text entered");
    await Task.Delay(2000);
    app.DismissKeyboard();
    await Task.Delay(2000);
    app.Tap(c => c.Button("translateButton"));
    await Task.Delay(4000);
    var elements = app.Query(c => c.Marked("translatedText"));
    await Task.Delay(2000);
    app.Screenshot("Japanese text translated");
    await Task.Delay(2000);
    Assert.AreEqual("I go to the office by train every day.", elements.FirstOrDefault().Text);
}



保存されたスクリンショットは以下の手順で確認できます。

f:id:hiro128:20181122181854p:plain
f:id:hiro128:20181122181904p:plain
f:id:hiro128:20181122181916p:plain




これで、リポジトリにプッシュすると自動ビルドが走り、自動実機UIテストが実行されるようになりました!!


以上で、Visual Studio App Center で、Xamarin.Android アプリの自動ビルド後に自動実機UIテストを実行する方法は完了です。


今回は以上です。

11月3日(土)に、Xamarin.iOS, Xamarin.Android のハンズオンを開催いたしました

こんにちは、@hiro128_777です。

11月3日(土)に、Xamarin.iOS, Xamarin.Android のハンズオンを開催いたしました!

jxug.connpass.com



今回は15名の方々にご参加いただきました。



Android@mishi_csさんが担当していただきました。レポートはこちらです。
rksoftware.hatenablog.com


私は、iOSを担当しました。いつも通り躓きが多かったのが「実機デバッグの環境構築」でしたが、ノウハウがすでにあるので解決に困るようなことはありませんでした。

今回は、珍しかったトラブルとしては

  • Xamarin.iOS の最新版に、iPhone XS MaxiOS 12.0 用の実機転送プロファイルがなく iOS 12.1 にアップデートが必要だった。

というものがありました。

また、コーディングではあまり躓きはありませんでした。

アンケート結果

ハンズオンを最後まで完了できたか

以下のようになりました。

f:id:hiro128:20181108190431p:plain


難易度

f:id:hiro128:20181108190441p:plain

難易度についてですが、66.7%の方が「簡単」「ちょうどいい」とご回答され、33.3%の方が「難しい」とご回答されました。


Xamarin.iOSのハンズオンは題材的には多少難易度が高めですので、妥当な結果となりました。


確かにちょっと難しいですが、習得する価値がある内容ですので、今後も、実施していきたいと考えています。


時間

f:id:hiro128:20181108190453p:plain


時間ですが、全員が「ちょうどいい」とご回答されました。



役に立ったか

f:id:hiro128:20181108190500p:plain


なんとこちらも全員の方に「役に立った」とご回答を頂けました!
主催者としては非常にうれしい結果となりました!



その他のご感想

その他のご感想としては、以下のようなご意見がございました。

  • Swiftからの移植の重要なコツを教わることができました。
  • すごく楽しかったです
  • 資料も説明もわかりやすくて楽しかったです!
  • 講義内容が事前に公開されていたのが良かったです。(予習して臨んだのですが少しペースが早くて着いていくのが大変でした)
  • Buildは通りましたが、実機で動かなかったので帰ってコードを見直してみます。

Xamarinで開催してほしいハンズオンがありますか

今後開催してほしいハンズオンとしては、以下のようなご意見をいただきました。

皆様のご意見を参考に今後も有意義なハンズオンを開催できるように精進いたします。



次回のハンズオンとして、 App Center で自動ビルド、その後の自動UIテスト、テスターへの配布を体験できるハンズオンを企画しております。

最後にお忙しい中休日にお時間を作ってご参加頂いた皆様、本当にありがとうございました!


また、JXUGのイベントでお会いできるのを楽しみにしております!

Visual Studio App Center で、自動ビルド後にiOSの自動実機UIテストを実行する方法

はじめに

こんにちは、@hiro128_777です。

11月3日(土)に、品川でXamarin.iOS 、Xamarin.Androidのハンズオンを開催いたします!

Swift, Objective-C のコードを Xamarin.iOS に移植する際のポイントについてのハンズオンまたは、java のコードを Xamarin.Android に移植する際のポイントについてのハンズオンを選んで受講できますので、ご興味がある方はぜひご参加下さい!

jxug.connpass.com



では本題に入りましょう。


Visual Studio App Center で、自動ビルド後にiOSの自動実機UIテストを実行する方法


Visual Studio App Center で、自動ビルドを設定したら当然ながら次は自動でテストを走らせたくなりますよね!

というわけで、今回は自動ビルド後にiOSの自動実機UIテストを実行する手順についてご説明いたします。

今回の内容は下記リポジトリを利用しますので clone するか DL して下さい。
github.com


Visual Studio App Center のテスト設定に実機UIテストを走らせるデバイスの組み合わせのセットを登録します


Visual Studio App Center では、1回のテストで、複数の実機の自動UIテストを走らせることができますので、テストを走らせる実機を選択して登録しておきます。



Visual Studio App Center のテスト設定のデバイスセット設定を開きます。
f:id:hiro128:20181030180705p:plain


Set name を設定し、テストを実行するデバイスにチェックを入れ New device set で保存します。
f:id:hiro128:20181030180715p:plain


Device set が登録されていることを確認します。
f:id:hiro128:20181030180731p:plain


Visual Studio App Center にログインするキーを準備します。


自動実行されるシェルスクリプトが自動テストを実行するときに、事前に取得しておいたキーを使って App Center にログインします。そのキーをスクリプトから利用できるように環境変数に登録しておきます。



「Account settings」を開きます。
f:id:hiro128:20181030180748p:plain


「New API token」を開きます。
f:id:hiro128:20181030193140p:plain


APIの利用目的の説明とアクセス権を設定し保存します。
f:id:hiro128:20181030180958p:plain


キーが表示されるのでコピーしメモ(保管)しておきます。キーは画面を閉じると2度と表示されないのでご注意ください。
f:id:hiro128:20181030180945p:plain


キーが登録されていることを確認します。
f:id:hiro128:20181030180930p:plain


次にビルド設定を開きます。
f:id:hiro128:20181030181018p:plain


環境変数にキーを設定して、保存します。
f:id:hiro128:20181030181031p:plain


build 後に自動実行されるシェルスクリプトを準備します。


Visual Studio App Center には ビルドするcspojと同じ階層に、appcenter-post-build.shという名前でシェルスクリプトを配置しておくと、自動認識し build 後に自動実行してくれる機能があります。
よって、appcenter-post-build.shに、自動実機UIテストを実行する処理を書きます。

/src/Finish/iOS/appcenter-post-build.sh

#!/usr/bin/env bash

# Post Build Script

# Exit immediately if a command exits with a non-zero status (failure)
set -e 

##################################################
# variables

appCenterLoginApiToken=$AppCenterLoginToken # this comes from the build environment variables
appName="TomohiroSuzuki128/XamAppCenterSample2018iOS"
deviceSetName="TomohiroSuzuki128/my-devices"
publishedAppFileName="XamAppCenterSample2018.iOS.ipa"
sourceFileRootDir="$APPCENTER_SOURCE_DIRECTORY/src/Finish"
uiTestProjectName="UITests"
testSeriesName="all-tests"
##################################################

echo "##################################################################################################"
echo "Post Build Script"
echo "##################################################################################################"
echo "Starting Xamarin.UITest"
echo "   App Name: $appName"
echo " Device Set: $deviceSetName"
echo "Test Series: $testSeriesName"
echo "##################################################################################################"
echo ""

echo "> Build UI test projects"
find $sourceFileRootDir -regex '.*Test.*\.csproj' -exec msbuild {} \;

echo "> Run UI test command"
# Note: must put a space after each parameter/value pair
appcenter test run uitest --app $appName --devices $deviceSetName --app-path $APPCENTER_OUTPUT_DIRECTORY/$publishedAppFileName --test-series $testSeriesName --locale "ja_JP" --build-dir $sourceFileRootDir/$uiTestProjectName/bin/Debug --uitest-tools-dir $sourceFileRootDir/packages/Xamarin.UITest.*/tools --token $appCenterLoginApiToken 

echo ""
echo "##################################################################################################"
echo "Post Build Script complete"
echo "##################################################################################################"

このスクリプトがやっていることは、

  • UIテストプロジェクトをビルドする。
  • App Center に、自動UIテストのコマンドを発行し、実行させる。

の2つです。

このスクリプトを含んだリポジトリをプッシュすると、以下のように、App Center 側で認識されます。
f:id:hiro128:20181030181359p:plain

スクリプトデバッグ方法。

このようなスクリプトを自作するときに一番困るのが、

  • 「指定したファイルが見つからない」エラーが発生すること
  • 環境変数の中身がよくわからないこと

です。

よってスクリプトを自作するときには、下記のように環境変数ディレクトリの中身をコンソールに表示させながらスクリプトを書くことで効率よくデバッグできます。

# for test
echo $APPCENTER_SOURCE_DIRECTORY
echo ""
files="$APPCENTER_SOURCE_DIRECTORY/src/Finish/UITests/*"
for filepath in $files
do
  echo $filepath
done

ビルドを実行し、テスト結果を確認してテストが正しく実行されていることを確認します。


正しく実行されていれば、以下のようにテスト結果が確認できます。
f:id:hiro128:20181030181050p:plain


また、テストコード内に以下のように、app.Screenshot("<スクリーンショット名>")と記述することで、スクーンショットが自動で保存されます。

[Test]
public async void SucceedTranslate()
{
    await Task.Delay(2000);
    app.Screenshot("App launched");
    await Task.Delay(2000);
    app.Tap(c => c.Marked("inputText"));
    await Task.Delay(2000);
    app.EnterText("私は毎日電車に乗って会社に行きます。");
    await Task.Delay(2000);
    app.Screenshot("Japanese text entered");
    await Task.Delay(2000);
    app.DismissKeyboard();
    await Task.Delay(2000);
    app.Tap(c => c.Button("translateButton"));
    await Task.Delay(4000);
    var elements = app.Query(c => c.Marked("translatedText"));
    await Task.Delay(2000);
    app.Screenshot("Japanese text translated");
    await Task.Delay(2000);
    Assert.AreEqual("I go to the office by train every day.", elements.FirstOrDefault().Text);
}



保存されたスクリンショットは以下の手順で確認できます。

f:id:hiro128:20181030192206p:plain
f:id:hiro128:20181030192743p:plain
f:id:hiro128:20181030192238p:plain




これで、リポジトリにプッシュすると自動ビルドが走り、自動実機UIテストが実行されるようになりました!!


以上で、Visual Studio App Center で、自動ビルド後に自動実機UIテストを実行する方法は完了です。


今回は以上です。

Visual Studio App Center で、秘匿情報をビルド時にインサートする方法

はじめに

こんにちは、@hiro128_777です。

11月3日(土)に、品川でXamarin.iOS 、Xamarin.Androidのハンズオンを開催いたします!

Swift, Objective-C のコードを Xamarin.iOS に移植する際のポイントについてのハンズオンまたは、java のコードを Xamarin.Android に移植する際のポイントについてのハンズオンを選んで受講できますので、ご興味がある方はぜひご参加下さい!

jxug.connpass.com



では本題に入りましょう。

Visual Studio App Center で、秘匿情報をビルド時にインサートする方法


自動ビルドするときに API へのアクセスキーなどの秘匿情報などは、リポジトリにプッシュしてはいけません。でもそうすると、自動ビルド後のテストなどで、API にアクセスできないので自動テストで困ってしまいます。そういうときには、ビルドサーバがリポジトリから Clone した後か、ビルド前に秘匿情報をインサートする方法が便利です。

今回はその手順についてご説明いたします。

今回の内容は下記リポジトリを利用しますので clone するか DL して下さい。
github.com

Visual Studio App Center のビルド設定に秘匿情報を環境変数として登録する


Visual Studio App Center のビルド設定を開きます。
f:id:hiro128:20181017190427p:plain



Environment variables環境変数名とキーの値を登録します。


iOS

f:id:hiro128:20181017190444p:plain


ソースコード上に置き換え用の目印となる文字列を準備します。


/src/Finish/XamAppCenterSample2018/Variables.cs を確認してください。


/src/Finish/XamAppCenterSample2018/Variables.cs

using System;

namespace XamAppCenterSample2018
{
    public static class Variables
    {
        // NOTE: Replace this example key with a valid subscription key.
        public static readonly string ApiKey = "[ENTER YOUR API KEY]";
    }
}

上記の[ENTER YOUR API KEY]のように置き換えの目印になる文字列を設定しておきます。

clone 後に自動実行されるシェルスクリプトを準備します。


App Center には ビルドするcspojと同じ階層に、appcenter-post-clone.shという名前でシェルスクリプトを配置しておくと、自動認識し clone 後に自動実行してくれる機能があります。
よって、appcenter-post-clone.sh[ENTER YOUR API KEY]を本物のキーに置き換えを行う処理を書きます。


iOS

/src/Finish/iOS/appcenter-post-clone.sh


Android

/src/Finish/Droid/appcenter-post-clone.sh



#!/usr/bin/env bash

# Insert App Center Secret into Variables.cs file in my common project

# Exit immediately if a command exits with a non-zero status (failure)
set -e 

##################################################
# variables

# (1) The target file
MyWorkingDir=$(cd $(dirname $0); pwd)
DirName=$(dirname ${MyWorkingDir})
filename="$DirName/XamAppCenterSample2018/Variables.cs"

# (2) The text that will be replaced
stringToFind="\[ENTER YOUR API KEY\]"

# (3) The secret it will be replaced with
AppCenterSecret=$API_Key # this is set up in the App Center build config

##################################################


echo ""
echo "##################################################################################################"
echo "Post clone script"
echo "  *Insert App Center Secret"
echo "##################################################################################################"
echo "        Working directory:" $DirName
echo "Secret from env variables:" $AppCenterSecret
echo "              Target file:" $filename
echo "          Text to replace:" $stringToFind
echo "##################################################################################################"
echo ""


# Check if file exists first
if [ -e $filename ]; then
    echo "Target file found"
else
    echo "Target file($filename) not found. Exiting."
    exit 1 # exit with unspecified error code. Should be obvious why we can't continue the script
fi


# Load the file
echo "Load file: $filename"
apiKeysFile=$(<$filename)


# Seach for replacement text in file
matchFound=false # flag to indicate we found a match

while IFS= read -r line; do
if [[ $line == *$stringToFind* ]]
then
# echo "Line found:" $line
    echo "Line found"
    matchFound=true

    # Edit the file and replace the found text with the Secret text
    # sed: stream editior
    #  -i: in-place edit
    #  -e: the following string is an instruction or set of instructions
    #   s: substitute pattern2 ($AppCenterSecret) for first instance of pattern1 ($stringToFind) in a line
    cat $filename | sed -i -e "s/$stringToFind/$AppCenterSecret/" $filename

    echo "App secret inserted"

    break # found the line, so break out of loop
fi
done< "$filename"

# Show error if match not found
if [ $matchFound == false ]
then
    echo "Unable to find match for:" $stringToFind
    exit 1 # exit with unspecified error code.
fi

echo ""
echo "##################################################################################################"
echo "Post clone script completed"
echo "##################################################################################################"


このスクリプトがやっていることは、

  • キーを置き換えるファイルを探す。
  • ファイルの中から置き換え用の目印となる文字列を探し、本物のキーに置き換える。

それだけです。


このスクリプトを含んだリポジトリをプッシュすると、以下のように、App Center 側で認識されます。


iOS

f:id:hiro128:20181017190644p:plain


ビルドを実行し、ログを確認してシェルスクリプトが正しく実行されていることを確認。


正しく実行されていれば、以下のようにログで確認できます。
f:id:hiro128:20181017190653p:plain


以上で、Visual Studio App Center で、秘匿情報をビルド時にインサートする手順は完了です。




今回は以上です。




次回は、ビルド後に自動UIテストが走る設定についてご説明します。
hiro128.hatenablog.jp

Microsoft Most Valuable Professional (Microsoft MVP) を再受賞いたしました!

こんにちは、@hiro128_777です。


遅ればせながらご報告させていただきます。

この度、Microsoft Most Valuable Professional (Microsoft MVP) を再受賞いたしました。カテゴリは前回同様 Visual Studio and Development Technologies です。


おかげさまで、Microsoft MVP として2年目を迎えることができました。


これもひとえに、ハンズオンに参加して頂いた方々、スタッフとしてご協力を頂いた方々、ブログや twitter で交流させて頂いた方々、いつもお世話になっているJXUGの皆様のおかげです。改めてお礼申し上げます。本当にありがとうございました。


昨年は主にハンズオンを中心に活動してきました。というのも、Xamarin では、知識よりもまずは実際に手を動かし、様々な地雷を踏み、その解決方法を体験して頂くことが何よりも重要だと感じているからです。


特に今まで Windows 中心に開発されてきた開発者の方の場合、特に iOS 開発時の面倒なお約束を乗り越えるのは非常にストレスが溜まる作業です(私自身がそうでした)。そこをある程度ご体験頂くだけでもその後がかなり楽になります。


クロスプラットフォーム開発も、いろいろ選択肢が出てきましたので、少しでも多くの皆様に Xamarin を選択していただけるように、今後も積極的にハンズオンを実施していきます。


また、今後は Xamarin と Azure や App Center などの連携のハンズオンも積極的に実施していきたいです。


そこで、早速ですが、今月7月28日(土)に、品川で Xamarin.iOS & Xamarin.Android と App Center のハンズオンを開催いたします!

今回は、Visual Studio for Mac による Xamarin.iOS、Xamarin.Android を利用したモバイル アプリの作成方法から Visual Studio App Center を利用した自動ビルドおよび自動 UI テストの手順までを総括的に体験することができるハンズオンを開催いたします。ご興味がある方はぜひご参加下さい!

jxug.connpass.com



それでは、また、1年間よろしくお願いいたします。

私自身楽しみながらコミュニティ活動を続けていきたいです。

皆様とどこかの勉強会でお会いできること楽しみにしております。