Developers Rock 'n' Roll High School

C#、.NET MAUI、Xamarin、などの技術の備忘録 Azureとかも書く予定

.NET MAUIアプリをPipeline Buildする(Github ActionsでAndroid編)

前書き

  • .NET MAUIのPipeline Buildについて、.NET Blogに記事が掲載されていたので実際に試してみました。
  • .NET Blogの記事では、Github ActionsとAzure DevOpsそれぞれで各プラットフォーム(Androd、iOSWindows、macCatalyst)のBuildのやり方が記載されていますが、ここではひとつずつ取り上げていきたいと思います。(果たして全部書ききれるのだろうか。。)
  • Github Actions初めて使うので、ちょっと説明がくどくなるかもしれません。
  • 試した範囲は、署名されたapkファイルとaabファイルを作るところまでです。その先、Google Play ConsoleにUploadする方法についてははたくさん先達の方々たちの記事が存在するのでここではやりません。
  • 2021年からGoogle Play Storeへの登録はapkではなくaabが必須となりました。ただし、例えばSprint Reviewなんかでステークホルダーの人に動きを見てもらう時なんかにはapkがあったほうがいいので両方作成して取得できるようにします。

参考情報

試した結果

  • ソースコードの全量はこちらです。
  • workflowのymlファイルはこちらです。
  • 作ったアプリは↓の感じです。動きは.NET MAUIのテンプレートそのままですね。なんとなく中身はViewModelとModelを別Projectに切り出してますが、これはまあ本題ではないです。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewModel="clr-namespace:PipelineSampleCore.ViewModels;assembly=PipelineSampleCore"
             x:Class="DotnetMauiApp.MainPage"
             x:DataType="viewModel:MainViewModel">

    <ScrollView>
        <VerticalStackLayout
            Spacing="25"
            Padding="30,0"
            VerticalOptions="Center">

            <Image
                Source="dotnet_bot.png"
                SemanticProperties.Description="Cute dot net bot waving hi to you!"
                HeightRequest="200"
                HorizontalOptions="Center" />

            <Label
                Text="Hello, World!"
                SemanticProperties.HeadingLevel="Level1"
                FontSize="32"
                HorizontalOptions="Center" />

            <Label
                Text="Welcome to .NET Multi-platform App UI"
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                FontSize="18"
                HorizontalOptions="Center" />

            <Button
                x:Name="CounterBtn"
                Text="{Binding CounterButtonText}"
                SemanticProperties.Hint="Counts the number of times you click"
                HorizontalOptions="Center"
                Command="{Binding CountUpCommand}" />

        </VerticalStackLayout>
    </ScrollView>

</ContentPage>
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PipelineSampleCore.Models;

namespace PipelineSampleCore.ViewModels;

[ObservableObject]
public partial class MainViewModel
{
    readonly ClickCounter _counter;

    public string CounterButtonText => _counter.CountText;

    public MainViewModel(ClickCounter counter) => _counter = counter;

    [RelayCommand]
    void CountUp()
    {
        _counter.CountUp();

        OnPropertyChanged(nameof(CounterButtonText));
    }
}
namespace PipelineSampleCore.Models;

public class ClickCounter
{
    int _counter = 0;

    public string CountText { get; private set; } = "Click me";


    public void CountUp()
    {
        _counter++;

        CountText = _counter == 1 ? $"Clicked {_counter} time" : $"Clicked {_counter} times";
    }
}

Setup

  • ymlファイルの内容を見ていきたいと思います。まずはBuildの前準備の箇所からです。ここはほぼ.NET Blogの記事通りです。
name: WindoesCI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

  workflow_dispatch:
  
env:
  DOTNETVERSION: 6.0.400
  BASE64_KYESTORE: ./android/release.keystore.base64
  DECRYPTED_KEYSTORE: ./android/release.decrypted.keystore
  DECRYPTED_KEYSTORE_BACKSLASH: ..\\android\\release.decrypted.keystore

jobs:
  buildAndroid:
    runs-on: windows-2022

    steps:
      - uses: actions/checkout@v3

      - name: Setup .NET SDK ${{env.DOTNETVERSION}}
        uses: actions/setup-dotnet@v2
        with:
          dotnet-version: '${{env.DOTNETVERSION}}'

      - name: List installed .NET info
        shell: pwsh
        run: dotnet --info
        
      - name: Install .NET MAUI
        shell: bash
        run: |
          dotnet nuget locals all --clear
          dotnet workload install maui --source https://aka.ms/dotnet6/nuget/index.json --source https://api.nuget.org/v3/index.json
          dotnet workload install android --source https://aka.ms/dotnet6/nuget/index.json --source https://api.nuget.org/v3/index.json
          
      - name: Restore nuget packages
        run: | 
          dotnet restore ./DotnetMauiApp/DotnetMauiApp.csproj
          dotnet restore ./PipelineSampleCore/PipelineSampleCore.csproj
          dotnet restore ./PipelineSampleTest/PipelineSampleTest.csproj

Unit Test

  • Unit Testについてもほぼ.NET Blogに書かれているとおりです。私はViewModelとModelを別Project(PipelineSampleCore)に切り出したので、そこだけちょっと違います。
      - name: Build and Run UnitTests
        shell: bash
        run: |
          dotnet build ./PipelineSampleCore/PipelineSampleCore.csproj
          dotnet build ./PipelineSampleTest/PipelineSampleTest.csproj
          dotnet test ./PipelineSampleTest/PipelineSampleTest.csproj --no-build --verbosity normal
  • 下記のstepは、Unit Testの結果をいい感じに可視化してくれるものです。Build完了後にActionsの画面から確認できます。
      - name: Test Report
        uses: dorny/test-reporter@v1
        if: success() || failure()
        with:
          name: Service Tests Summary
          path: ./PipelineSampleTest/TestResults/*.trx
          reporter: dotnet-trx
  • テストコードはとりあえずこんな感じに書きました。ここも本題ではないので適当です。
using PipelineSampleCore.Models;

namespace PipelineSampleTest;

public class UnitTest1
{
    [Fact]
    public void Test1()
    {
        var counter = new ClickCounter();
        Assert.Equal("Click me", counter.CountText);

        counter.CountUp();
        Assert.Equal("Clicked 1 time", counter.CountText);

        counter.CountUp();
        Assert.Equal("Clicked 2 times", counter.CountText);

        counter.CountUp();
        Assert.Equal("Clicked 3 times", counter.CountText);
    }
}

署名付きでビルド

  • アプリへの署名は、Google Play Storeに出すときに必要になります。逆に開発段階で検証用のスマホにインストールするだけとかだったら必要ないので、例えばBranch運用次第でdevelop branchの場合は署名なしでビルドするとかにしておいたほうがよりクイックになるかもしれません。
  • apkファイルとaabファイルに署名するには、あらかじめkeytstoreファイルを作成しておかなくてはいけません。keystoreファイルはそのアプリでずっと使い続けるもので、ローカルPCとかであらかじめ作成してどこかに保存しておくのですが、PublicなGitリポジトリの場合pushしてしまうとセキュリティ上よろしくないので事故らないように.gitignoreに記載しておいたほうが良いと思います。
  • PublicなリポジトリでBuildする際にどうやってkeystoreファイルを参照するかというと、GithubのSecret機能を利用します。ポイントとしてGithubのSecretには文字列しか登録できないので、あらかじめファイルをbase64文字列化して登録し、Pipelineの処理でそれをdecodeします。(Azure Key Vaultとかセキュアな場所にファイルを置くという手もありますが、Loginして取りに行くのは手間。)
  • base64文字列化する方法は、私の場合Windows PCのWSL2(Ubuntu)に入っているbase64コマンドを使いました。たぶん他のだいたいのディストリビューションでも入ってますし、GitBashにも入ってます。例えば「DotnetMauiApp.keystore」というkeystoreファイルをあらかじめ作成していたとした場合こんな↓感じでコマンド打つとbase64化された文字列が表示されます。
 base64 DotnetMauiApp.keystore
  • ↑の結果の文字列をコピってSecretに貼り付けます。keystoreのパスワードもここに入れておくとよいでしょう。(リポジトリの「Settings」タブからSecretの登録画面にいけます)
  • secretに登録した値を取り出し、decodeしてファイル化します。
      - name: Extract Android signing key from env
        shell: bash
        run: |
          mkdir -p android
          echo "${{ secrets.RELEASE_KEYSTORE }}" > "${{ env.BASE64_KYESTORE }}"
          base64 -d "${{ env.BASE64_KYESTORE }}" > "${{ env.DECRYPTED_KEYSTORE }}"
  • dotnet publish コマンドでBuildと署名をします。通常、MAUIのcsprojファイルにこんな感じでkeystoreへの参照を設定できます。
<PropertyGroup Condition="$(TargetFramework.Contains('-android')) and '$(Configuration)' == 'Release'">
    <AndroidKeyStore>True</AndroidKeyStore>
    <AndroidSigningKeyStore>myapp.keystore</AndroidSigningKeyStore>
    <AndroidSigningKeyAlias>key</AndroidSigningKeyAlias>
    <AndroidSigningKeyPass></AndroidSigningKeyPass>
    <AndroidSigningStorePass></AndroidSigningStorePass>
</PropertyGroup>
  • しかし、秘密にすべき情報を直書きはよろしくないので、↓のようにdotnet publishコマンドのパラメーターとして渡します。
      - name: Build Android App
        shell: bash
        run: |
          dotnet publish ./DotnetMauiApp/DotnetMauiApp.csproj -f:net6.0-android -c:Release //p:AndroidSigningKeyPass="${{ secrets.RELEASE_KEYSTORE_PASSWORD }}" //p:AndroidSigningStorePass="${{ secrets.RELEASE_KEYSTORE_PASSWORD }}" //p:AndroidSigningKeyStore="${{ env.DECRYPTED_KEYSTORE_BACKSLASH }}" //p:AndroidSigningKeyAlias=key
  • dotnet publish コマンドでapkとaabが作成されるので、あとはそれをDLできるようにするだけです。(どの場所にどんな名前でapkやaabが作成されるか確認したい場合は、Local PCでdotnet publishしてみるといいかと思います。)
      - uses: actions/upload-artifact@v3
        with:
          name: artifact-android
          path: |
            ./DotnetMauiApp/bin/Release/net6.0-android/publish/*.apk
            ./DotnetMauiApp/bin/Release/net6.0-android/publish/*Signed.aab
  • Pipelineが完走後、ここを押すとapkとaabが入ったzipをDLできます。

    署名の確認

  • 一応、署名がちゃんとされてるか確認してみます。いったんbase64したりしたのでちゃんと署名できているか不安になりますね。
  • 方法は、keystoreファイルのフィンガープリントとapkファイルのフィンガープリントが一致しているかを確認します。

    keystoreファイルの確認

keytool -v -list -keystore DotnetMauiApp.keystore

apkの確認

keytool -printcert -jarfile com.companyname.dotnetmauiapp-Signed.apk
  • こんな感じでそれぞれ表示されるはずなので、一致しているか確認します。
証明書のフィンガプリント:
         SHA1: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
         SHA256: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX

あとがき

  • 無事にGithub ActionsでAndroidアプリをビルドできるようになりました。この程度だと全然難しくはなかったですね。(dotnet publishのパラメーター渡すときのスラッシュの扱いにちょっとはまりましたが。。) Github Antions初めて書きましたが、単品のアプリをBuildする程度であればさくっと書けることがわかりました。おそらく本番プロダクトでは他のクライアントアプリやバックエンドもあってもっと壮大なPipelineを書かないといけなくなると思うので、今後もキャッチアップを進めて知見をつけていきたいと思います。次はiOSの記事を書くか、それともAndroidでAzure DevOpsをやってみるか考え中。。

Android SDKのインストール場所(Windows)

前書き

  • 新しいPCなんかでふとADBとか使いたくなるときに、どこにインストールされてるか一瞬わからなくなる時があります。私の場合Android StudioVisual Studio(.NET MAUI)両方使っていて、それぞれ入ってるのでなおさらです。という内容の薄い雑な自分用メモ書きです。

Android Studioに同梱されてるAndroid SDKインストール先

  • デフォルトのロケーションであれば下記の場所にインストールされてます。
C:\Users\username\AppData\Local\Android\Sdk
  • 確認方法としてはSettingsの↓のとこから、どこにインストールされているか確認できます。
  • adb.exeは下記の場所にありますので適宜環境変数に設定します。(Android StudioのほうでもVisual Studioのほうでもお好きなほうを)
C:\Users\username\AppData\Local\Android\Sdk\platform-tools

Visual Studioの.NET MAUIワークロードに同梱されているAndroid SDKインストール先

  • 下記の場所にインストールされています。
C:\Program Files (x86)\Android\android-sdk
  • 確認方法としてはメニューの「ツール > オプション」からXamarinの項目を見ます。(.NET MAUIワークロードしか入れてなくても「Xamarin」)
  • adb.exeは下記の場所にありますので適宜環境変数に設定します。(Android StudioのほうでもVisual Studioのほうでもお好きなほうを)
C:\Program Files (x86)\Android\android-sdk\platform-tools

Version

  • 一応それぞれのVersion情報を書いておきます

    Android Studio

Android Studio Chipmunk | 2021.2.1 Patch 2
Build #AI-212.5712.43.2112.8815526, built on July 11, 2022
Runtime version: 11.0.12+7-b1504.28-7817840 amd64
VM: OpenJDK 64-Bit Server VM by Oracle Corporation
Windows 10 10.0
GC: G1 Young Generation, G1 Old Generation
Memory: 1280M
Cores: 16
Registry: external.system.auto.import.disabled=true

Visual Studio(抜粋)

Microsoft Visual Studio Community 2022
Version 17.3.3
VisualStudio.17.Release/17.3.3+32825.248
Microsoft .NET Framework
Version 4.8.09032

インストールされているバージョン:Community

.NET MAUIの参考情報まとめ

前書き

  • 個人的に役に立つと思っているお役立ち情報リンクのまとめです。随時更新していく予定です。

リンク

.NET MAUI Workshop

  • MSの.NET MAUI開発者James MontemagnoさんによるWorkshop動画です。まずはこれを見ると雰囲気をつかみやすいかと思います。
  • 動画の中で使用しているソースコードGithubリポジトリこちらです。

MS Docs

  • 必ず目を通すべき公式Docsです。

.NET Blog

  • .NET BlogのMAUIだけの記事を集めたページです。

MS Learn

  • 一通りやってみるとよいと思います。

コードサンプル

  • MS公式のコードサンプルです

snppts

  • .NET MAUIで作られたきれいなUIサンプルがたくさんあります。

エンタープライズアプリケーションパターン

  • エンプラな開発を行う際にとても役立つことが記載されたebookです。MAUIを実践投入する際には必読です。
  • 使用されているソースコードこちらです。

CommunityToolkit

  • MAUIを使う上で役立つライブラリです。先日私が書いた記事↓にいろいろリンクが張ってあります。 yo-hoshino.hatenablog.com

開発者

あとがき

  • 思い出したり、新しく見つけたらまた書きます。

.NET MAUI開発の強力な味方「CommunityToolkit」の紹介

前書き

  • かなり久しぶりにブログを書きます。もともとほぼ書いていなくて放置していたのですが、いいかげんそろそろ真剣にアウトプットをしてスキルアップしなきゃいかんと思って再び書くに至った次第です。これまで雰囲気で実装していたものを、ちゃんと理解を深めたいのです。忙しくなったりモチベが下がったらまた書かなくなるかもですが、なるべく習慣化できるようにしたいと思っています。
  • .NET MAUIを使う上で強力な味方になるライブラリ「CommunityToolkit」の紹介というか、リファレンスをつらつらメモった備忘録になります。

背景

  • 先月(2022/08)に.NET MAUIのVisual Studio(Windows)のサポートがGAしました。あとはVisual Studio for Macのサポートが今年中を予定しているようですので、これがGAすれば一通りの開発環境が揃います。本家のGAとほぼ時を同じくして、.NET MAUI開発を強力にサポートしてくれるライブラリである「CommunityToolkit.Mvvm」もリリース(Ver8.0.0)されており、効率の良い開発を進めることができるようになっています。今回はこういったお役立ちライブラリを紹介します。

CommunityToolkitについて

  • .NET MAUIで使用できるCommunityToolkitは2種類、「CommunityToolkit.Mvvm」と「CommunityToolkit.Maui」というものがあります。それぞれ役割が違うので必要に応じてインストールします。
  • Xamarin.FormsのころはPrismが代表的なライブラリでしたが、.NET MAUIではどうやらCommunityToolkitが押されている雰囲気?です。公式で配信される動画コンテンツやサンプルコードでよく使われています。とはいうものの、ライブラリの選択基準は状況によって異なるので、事前に比較検討してから導入するようにしましょう。それぞれアプリ固有の事情は異なりますし、その時その時でトレンドの移り変わりもあります。

CommunityToolkit.Mvvm

  • 特定のUIプラットフォームに依存しないライブラリになります。MVVMをサポートしてくれるもので、これまでかなり面倒であった変更通知の記述を大幅に減らしたり、ICommandの実装などが提供されています。すでに基本的な使用方法を解説しているブログなどがいくつかあるので、ここでは公式リファレンスのリンクを記載しておくにとどめます。

    MS Docs

  • こちらに記載されています。

    .NET Blog

  • CommunityToolkit.Mvvm Ver8.0.0がリリースされた際に投稿された.NET Blog になります。このライブラリの開発者(たぶん中心的な人)であるMSのエンジニア、Sergio Pedriさんが執筆しています。この記事でライブラリの特徴やだいたいの使い方がわかります。

    サンプルアプリ

  • CommunityToolkit.Mvvmを使用したサンプルアプリがMicrosoft Storeにて提供されています。コードサンプルが記載されており、実際の動きを試すこともできます。素晴らしい!
    • ※Storeを「community toolkit」で検索すると似たものが2種類ヒットしますが、青いアイコンのアプリをDLインストールしてください。黒いアイコンのほうは古いものです。
  • ↑のサンプルアプリのソースコードです。

    動画コンテンツ

  • MSの.NET MAUIの開発者であるJames MontemagnoさんによるSergio Pedriさんへのインタビュー動画です。

CommunityToolkit.Maui

  • .NET MAUI向けに作られたライブラリになります。.NET MAUI向けのbehaviorなど役立つ機能が含まれています。

    MS Docs

  • こちらに記載されています。

    導入方法

  • Nugetパッケージ「CommunityToolkit.Maui」をインストールします。(すぐ下の「.Core」が気になりますが、気にしなくて大丈夫です。.Mauiと一緒にインストールされます。)

  • ライセンスに同意しインストールされるとreadmeが表示されます。使い方が記載されているのでそれに従います。

  • MauiProgram.csに2か所記載
using CommunityToolkit.Maui;  //←★追加

namespace CommunityToolkitSample;

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");
            });

        builder.UseMauiApp<App>().UseMauiCommunityToolkit(); //←★追加

        return builder.Build();
    }
}
  • 使いたいxamlファイルにxmlnsを記載すれば準備完了!
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    ★追加 → xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="CommunityToolkitSample.MainPage">

・
・
・

</ContentPage>

使用方法

  • 使い方は下記の感じです。個人的によく使う「EventToCommandBehavior」を例に挙げます。
  • 例えば素の状態では、↓のようにXamlでContentPageの「Appearing」(Pageが表示された)というイベントに対してイベントハンドラーを設定するしかありません。しかしMVVMで実装する場合、ViewModelのCommandにバインドをしたくなります。Appearingに対応するコマンドは.NET MAUIには用意されていないのでBehaviorを自作する必要があるのですが、CommunityToolkit.Mauiには「EventToCommandBehavior」があるのでそれを使用します。

  • 例えばViewModelにこんなCommand(CommunityToolkit.MvvmのRelayCommand)があったとして、、。

using System.Diagnostics;
using CommunityToolkit.Mvvm.Input;

namespace CommunityToolkitSample.ViewModel;
internal partial class MainViewModel
{
    [RelayCommand]
    public void WriteLog()
    {
        Debug.WriteLine("Appearing!!!!");
    }
}
  • View(xaml)ではこんな感じにするとAppearingイベントをコマンドに紐づけられます。<ContentPage.Behaviors>の「EventName」にイベントの名前を記載し対応するCommandを記載すればいいわけです。この例ではMainPageが表示された際にデバッグログを書き出します。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:viewModel="clr-namespace:CommunityToolkitSample.ViewModel"
             x:Class="CommunityToolkitSample.MainPage">
    <ContentPage.BindingContext>
        <viewModel:MainViewModel/>
    </ContentPage.BindingContext>
    <ContentPage.Behaviors>
        <toolkit:EventToCommandBehavior
            EventName="Appearing"
            Command="{Binding WriteLogCommand}" />
    </ContentPage.Behaviors>

・ 
・
・

</ContentPage>

あとがき

  • 長くなるので今回はここまでとします。今回はほんの導入部分だけ書きましたが、紹介したライブラリはたくさんの機能を持っているので今後もっと掘り下げたTipsなどを投稿できたらと思っています。

Visual Studio 2019 for Mac でAndroidプロジェクトからSystemとかのアセンブリが見えなくなったときの暫定対処法

※2019/6/24追記
下記のリリースで修正(Developer Community 514955)されたようですね。よかったよかった。

docs.microsoft.com

環境

OS: macOS Mojave
IDE: Visual Studio 2019 for Mac

事象

  • Androidプロジェクトの参照を見ると、SystemとかSystem.Coreがグレーアウトされてエラーメッセージが表示さている。
    (私の環境ではXamarin.FormsのAndroidプロジェクトでしたが、Xamarin.Android環境とかでも起きるのでしょうか?)

解決方法

タイトルでは得意げに対処法とか言っていますが、すでにVisual Studioのフォーラムで示されています。 https://developercommunity.visualstudio.com/comments/516437/view.html

Visual Studio 2019 for Mac のリリース直後にAndroid周りでバグがあってざわざわしていたのは知っていたのですが、 ゴールデンウィークあたりにパッチが当たっていたような気がしたので安心して本日(2019/05/14)アップデートしたらまだなおっていませんでした。

せっかくなので日本語で私の環境で実施したことを書いておきます。(ご自分でやる方は自己責任でお願いいたします。)
あと、バージョン情報などは会社の環境メモってくるの忘れたのであとで追記します。

上記リンクではコマンドラインで編集してますが、私はVisual Studio Code で編集しました。(保存するときにsudoで保存するか聞かれます)

1. 下記のファイルを編集するので、どこかにバックアップをとります。(9.2.0-5の名前は環境により差異があるようです)

/Library/Frameworks/Xamarin.Android.framework/Versions/9.2.0-5/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v1.0/RedistList/FrameworkList.xml

ちなみに中身はこんな感じです。

<FileList Redist="MonoAndroid" Name="Xamarin.Android Base Class Libraries">
<File AssemblyName="System.Buffers"    Version="4.0.99.0" />
<File AssemblyName="System.Memory"     Version="4.0.99.0" />
</FileList>

2. 中身を下記のように編集します。(File AssemblyName=..を削除します)

<FileList Redist="MonoAndroid" Name="Xamarin.Android Base Class Libraries">
</FileList>

3. sudoで保存します。(普通に保存するとVisual Studio CodeがSudoで保存するか聞いてきます)

4. 念のためにVisual Sutudio 2019 for Macを終了して、おなじみのbin/obj削除します。

5. Visual Sutudio 2019 for Macを再び起動すると、先ほどのエラーは無くなっているはずです。

手順1でバックアップしたファイルはパッチが当たってほとぼりが冷めるまでは保管しといたほうが良いのでしょうかね。

以上です。次のパッチでは直っているのでしょうか。

Visual Studio Code(Mac)のTerminalをzshに変更する。

Macの開発環境自分用メモです。

環境

手順

  1. Terminalの設定に移動
Preference > Settings > Features > Terminal
  1. ShellのPathを変更。
Terminal › Integrated › Shell: Osx  
/usr/local/bin/zsh
  1. フォントも変更。(powerlinは文字化けするので)
Integrated: Font Family  
Ricty for Powerline

備考

  • JetBrains Rider はShellのPathを /usr/local/bin/zsh にするだけでフォントも当たってくれた。

Xamarin.iOSのUIを作るうえであると便利な拡張メソッド

iOSのUIなので、本家Swiftの技術ブログなんかにはいくらでも情報があります。 が、C#の情報は比較的少ないです。
まあ、SwiftをC#に置き換えればいいだけなのですが、 本家iOSの開発やったことなくて、C#とXamarin.iOSを同時にやり始める人(私みたいな)なんかにはこの手のC#で書いた小技情報が貴重です。

※思いついたら追加していきます。

UIColorをUIImageに変換する

public static UIImage ToImage(this UIColor color)
{
    var rect = new CGRect(0, 0, 1, 1);
    UIGraphics.BeginImageContextWithOptions(rect.Size, false, 0f);

    var context = UIGraphics.GetCurrentContext();
    context.SetFillColor(color.CGColor);
    context.FillRect(rect);

    var image = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return image;
}

これを書いておくと何がうれしいかというと、UIButtonなんかの色を設定する時に便利になります。 UIButtonは、通常時や押された時の見た目をSetImageメソッドで指定できますが、メソッド名が示す通りUIImageしか登録できません。 「ちょっと色変えたいだけなのにUIImageが必要なの!!?」となります。 そういうときはこのメソッドを書いておけば、

button.SetImage(UIColor.Blue.ToImage(), UIControllState.Highlighted);

という感じに使えます。 UIGraphicsは、ちょっと見た目をこだわろうとすると使う機会が多いですね。

UIViewをUIImageに変換する。

public static UIImage ToImage(this UIView view)
{
    UIGraphics.BeginImageContextWithOptions(view.Bounds.Size, false, 0f);
    view.Layer.RenderInContext(UIGraphics.GetCurrentContext());
    var image = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();

    return image;
}

これはボタンとかアイコンとかをUIViewのDrawで作って部品化したいときに使います。 iOSのコントロールをそのまま使えれば一番楽なのですが、そうはいかないことが多々あります。 そういったものはUIViewのDrawで頑張って作ったものを、そのままでは使い勝手が悪いのでUIImageに変換します。

UIImageをリサイズする。

public static UIImage Size(this UIImage image, float width, float height)
{
    var size = new CGSize(width, height);
     UIGraphics.BeginImageContextWithOptions(size, false, 0f);

    image.Draw(new RectangleF(0, 0, width, height));
    var resultImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return resultImage;
}

UIImageのサイズを変えたいときに。