Xamarin

モバイル開発クロスプラットフォームC#.NETMicrosoft終了サポート

モバイルプラットフォーム

Xamarin

概要

Xamarinは、Microsoftが買収した.NETベースのクロスプラットフォームモバイル開発フレームワークです。C#を使用してiOS、Android、Windows向けのネイティブアプリケーションを開発できます。2011年にMonoプロジェクトから派生し、2016年にMicrosoftに買収されました。2024年5月1日をもってサポートが終了し、後継として.NET MAUIへの移行が推奨されています。単一のC#コードベースから各プラットフォーム向けのネイティブアプリを構築でき、プラットフォーム固有のAPIにも完全にアクセスできました。

詳細

Xamarinは、.NETとC#を使用してクロスプラットフォームのモバイルアプリケーションを開発するための包括的なフレームワークでした。Xamarin.iOS、Xamarin.Android、Xamarin.Formsの3つの主要コンポーネントで構成され、それぞれが異なるアプローチでクロスプラットフォーム開発をサポートしていました。

Xamarin.iOSとXamarin.Androidは、各プラットフォームのネイティブAPIをC#でラップし、プラットフォーム固有の機能に完全にアクセスできるようにしていました。開発者は各プラットフォームのUIを個別に設計する必要がありましたが、ビジネスロジックやデータアクセス層は共有できました。

Xamarin.Formsは、より高いレベルの抽象化を提供し、単一のXAMLベースのUIコードから複数のプラットフォーム向けのUIを生成できました。これにより、コード共有率が大幅に向上し、開発時間の短縮が可能になりました。

開発環境として、Visual StudioやVisual Studio for Macが使用され、統合されたデバッグ、プロファイリング、デザイナーツールが提供されていました。また、Xamarin Test CloudやXamarin Insightsなどのクラウドサービスも利用可能でした。

現在、Xamarinのサポートは終了しており、新規プロジェクトでは.NET MAUIの使用が推奨されています。既存のXamarinアプリケーションは、.NET MAUIへの移行が必要となっています。

メリット・デメリット

メリット(過去形で記載)

  • C#とマネージコード: 既存の.NET開発者スキルを活用でき、型安全性とメモリ管理の恩恵を受けられた
  • ネイティブパフォーマンス: ネイティブコンパイルにより、高いパフォーマンスを実現していた
  • プラットフォームAPI完全アクセス: 各プラットフォームの全APIにアクセス可能で、制限がなかった
  • コード共有: ビジネスロジック、データ層、一部のUIコードを共有でき、開発効率が向上した
  • Visual Studio統合: 強力なIDEサポートにより、生産性の高い開発環境が提供されていた
  • 大規模エコシステム: NuGetパッケージや.NETライブラリの豊富なエコシステムを活用できた
  • エンタープライズ対応: 大規模アプリケーション開発に適した堅牢な機能を持っていた
  • Microsoftサポート: Microsoftによる公式サポートと継続的な改善が行われていた

デメリット

  • サポート終了: 2024年5月1日でサポートが終了し、新規開発には使用できない
  • アプリサイズ: ランタイムを含むため、アプリサイズが大きくなる傾向があった
  • 学習曲線: プラットフォーム固有の知識に加えて、Xamarin固有の概念の理解が必要だった
  • UIカスタマイズの制限: Xamarin.Formsでは、高度なUIカスタマイズに制限があった
  • サードパーティライブラリ: ネイティブライブラリのバインディング作成が複雑だった
  • パフォーマンスオーバーヘッド: 一部のシナリオでネイティブアプリより劣る場合があった
  • デザイナーツールの制限: XAMLデザイナーの機能が限定的だった

参考ページ

書き方の例

基本的なXamarin.Formsプロジェクト構造(レガシー)

// App.xaml.cs - アプリケーションエントリポイント
using Xamarin.Forms;

namespace MyXamarinApp
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new NavigationPage(new MainPage());
        }

        protected override void OnStart()
        {
            // アプリ起動時の処理
        }

        protected override void OnSleep()
        {
            // アプリがバックグラウンドに移行時の処理
        }

        protected override void OnResume()
        {
            // アプリが再開時の処理
        }
    }
}

Xamarin.Forms XAML ページ(レガシー)

<!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyXamarinApp.MainPage"
             Title="ホーム">
    
    <StackLayout Padding="20">
        <Label Text="Xamarinアプリケーションへようこそ!"
               FontSize="Large"
               HorizontalOptions="Center" />
        
        <Entry x:Name="nameEntry"
               Placeholder="お名前を入力してください"
               Margin="0,20,0,0" />
        
        <Button Text="送信"
                Clicked="OnSubmitClicked"
                BackgroundColor="#2196F3"
                TextColor="White"
                Margin="0,10,0,0" />
        
        <ListView x:Name="itemsList"
                  Margin="0,20,0,0">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Name}"
                              Detail="{Binding Description}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

コードビハインド(レガシー)

// MainPage.xaml.cs
using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;

namespace MyXamarinApp
{
    public partial class MainPage : ContentPage
    {
        private ObservableCollection<Item> items;

        public MainPage()
        {
            InitializeComponent();
            
            items = new ObservableCollection<Item>();
            itemsList.ItemsSource = items;
        }

        private async void OnSubmitClicked(object sender, EventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(nameEntry.Text))
            {
                await DisplayAlert("挨拶", $"こんにちは、{nameEntry.Text}さん!", "OK");
                
                items.Add(new Item
                {
                    Name = nameEntry.Text,
                    Description = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")
                });
                
                nameEntry.Text = string.Empty;
            }
        }
    }

    public class Item
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }
}

プラットフォーム固有のコード(レガシー)

// DependencyService を使用したプラットフォーム固有の実装
// IDeviceService.cs - 共有インターフェース
public interface IDeviceService
{
    string GetDeviceId();
    void ShowToast(string message);
}

// iOS実装 - iOS/DeviceService.cs
[assembly: Dependency(typeof(MyXamarinApp.iOS.DeviceService))]
namespace MyXamarinApp.iOS
{
    public class DeviceService : IDeviceService
    {
        public string GetDeviceId()
        {
            return UIDevice.CurrentDevice.IdentifierForVendor.ToString();
        }

        public void ShowToast(string message)
        {
            var alert = UIAlertController.Create(null, message, 
                UIAlertControllerStyle.Alert);
            
            UIApplication.SharedApplication.KeyWindow.RootViewController
                .PresentViewController(alert, true, null);
            
            NSTimer.CreateScheduledTimer(2.0, (timer) =>
            {
                alert.DismissViewController(true, null);
            });
        }
    }
}

// Android実装 - Android/DeviceService.cs
[assembly: Dependency(typeof(MyXamarinApp.Droid.DeviceService))]
namespace MyXamarinApp.Droid
{
    public class DeviceService : IDeviceService
    {
        public string GetDeviceId()
        {
            return Android.Provider.Settings.Secure.GetString(
                Android.App.Application.Context.ContentResolver,
                Android.Provider.Settings.Secure.AndroidId);
        }

        public void ShowToast(string message)
        {
            Toast.MakeText(Android.App.Application.Context, 
                message, ToastLength.Short).Show();
        }
    }
}

// 使用例
var deviceService = DependencyService.Get<IDeviceService>();
string deviceId = deviceService.GetDeviceId();
deviceService.ShowToast("デバイスID: " + deviceId);

.NET MAUIへの移行例

// .NET MAUI版の同等のコード
// MauiProgram.cs - 新しいエントリポイント
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;

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

#if DEBUG
            builder.Logging.AddDebug();
#endif
            // サービスの登録
            builder.Services.AddSingleton<IDeviceService, DeviceService>();

            return builder.Build();
        }
    }
}

// MainPage.xaml - .NET MAUI版
<?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"
             x:Class="MyMauiApp.MainPage"
             Title="ホーム">
    
    <VerticalStackLayout Padding="20" Spacing="10">
        <Label Text="NET MAUIアプリケーションへようこそ!"
               FontSize="Large"
               HorizontalOptions="Center" />
        
        <Entry x:Name="nameEntry"
               Placeholder="お名前を入力してください" />
        
        <Button Text="送信"
                Clicked="OnSubmitClicked"
                BackgroundColor="#2196F3"
                TextColor="White" />
        
        <CollectionView x:Name="itemsCollection">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Frame Padding="10" Margin="5">
                        <VerticalStackLayout>
                            <Label Text="{Binding Name}" 
                                   FontAttributes="Bold" />
                            <Label Text="{Binding Description}" 
                                   FontSize="Small" />
                        </VerticalStackLayout>
                    </Frame>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

移行の注意点コメント

// 重要: Xamarin.Forms から .NET MAUI への主な変更点
// 1. 名前空間の変更:
//    Xamarin.Forms.* → Microsoft.Maui.*
// 2. DependencyService → 依存性注入 (DI)
// 3. Device.BeginInvokeOnMainThread → MainThread.BeginInvokeOnMainThread
// 4. MessagingCenter → WeakReferenceMessenger
// 5. プロジェクト構造の統一(マルチターゲティング)
// 6. 新しいハンドラーアーキテクチャ

// 移行ツールの使用例:
// dotnet tool install -g upgrade-assistant
// upgrade-assistant upgrade <プロジェクトパス>