Unified API の概要
Xamarin の Unified API を使うと、Mac と iOS の間でコードを共有し、同じバイナリで 32 ビットと 64 ビット アプリケーションをサポートできます。 Unified API は、新しい Xamarin.iOS プロジェクトと Xamarin.Mac プロジェクトで既定で使用されます。
重要
Unified API より前の Xamarin Classic API は非推奨になりました。
- Classic API (monotouch.dll) をサポートする Xamarin.iOS の最後のバージョンは Xamarin.iOS 9.10 でした。
- Xamarin.Mac では引き続き Classic API がサポートされていますが、更新は行われません。 非推奨であるため、開発者はアプリケーションを Unified API に移行する必要があります。
Classic API ベースのアプリの更新
お使いのプラットフォームに関連する手順に従います。
コードを Unified API に更新する場合のヒント
移行するアプリケーションに関係なく、Unified API に正常に更新するために役立つこれらのヒントを確認します。
ライブラリの分割
この時点から、API は次の 2 つの部分に分かれます。
- Classic API: 32 ビット (のみ) に制限されており、
monotouch.dll
とXamMac.dll
アセンブリで公開されます。 - Unified API:
Xamarin.iOS.dll
アセンブリとXamarin.Mac.dll
アセンブリで使用できる 1 つの API を使って、32 ビットと 64 ビットの両方の開発をサポートします。
これは、Enterprise の開発者 (App Store を対象としていない) の場合、既存の Classic API は永続的に保守され続けるため、引き続き使うことも、新しい API にアップグレードすることもできることを意味します。
名前空間の変更
Mac と iOS の製品間でコードを共有するための衝突を軽減するために、製品での API の名前空間を変更します。
データの種類のプレフィックス "MonoTouch" を iOS 製品から削除し、Mac 製品から "MonoMac" を削除します。
これにより、条件付きコンパイルに頼ることなく Mac と iOS プラットフォーム間でコードを共有することが簡単になり、ソース コード ファイルの先頭にある雑情報が軽減されます。
- Classic API: 名前空間は、
MonoTouch.
またはMonoMac.
プレフィックスを使います。 - Unified API: 名前空間プレフィックスはありません
ランタイムの既定値
Unified API は既定で、オブジェクトの所有権を追跡するために SGen ガベージ コレクターと新しい参照カウント システムを使います。 これと同じ機能が Xamarin.Mac に移植されています。
これにより、開発者が古いシステムで直面していた多くの問題が解決され、メモリ管理も容易になります。
Classic API でも新しい Refcount を有効にすることはできますが、既定値は保守的であり、ユーザーが変更を加える必要がないことに注意してください。 Unified API では、既定値を変更する機会を得て、開発者がコードをリファクタリングして再テストすると同時に、すべての機能強化を行いました。
API の変更内容
Unified API では非推奨のメソッドが削除されており、Classic API の元の MonoTouch 名前空間と MonoMac 名前空間にバインドされているときに API 名に入力ミスがあったインスタンスがいくつかあります。 これらのインスタンスは新しい Unified API で修正されているため、コンポーネント、iOS、Mac アプリケーションで更新する必要があります。 以下に、直面する可能性のある最も一般的なものの一覧を示します。
Classic API メソッド名 | Unified API メソッド名 |
---|---|
UINavigationController.PushViewControllerAnimated() |
UINavigationController.PushViewController() |
UINavigationController.PopViewControllerAnimated() |
UINavigationController.PopViewController() |
CGContext.SetRGBFillColor() |
CGContext.SetFillColor() |
NetworkReachability.SetCallback() |
NetworkReachability.SetNotification() |
CGContext.SetShadowWithColor |
CGContext.SetShadow |
UIView.StringSize |
UIKit.UIStringDrawing.StringSize |
Classic API から Unified API に切り替えるときの変更点の完全な一覧については、Classic (monotouch.dll) と Unified (Xamarin.iOS.dll) の API の違いに関するドキュメントを参照してください。
Unified への更新
Classic のいくつかの古い/破損した/非推奨の API は、Unified API では使用できません。 (手動または自動) アップグレードを開始する前に CS0616
警告を修正する方が簡単な場合があります。これは、適切な API を案内する [Obsolete]
属性メッセージ (警告の一部) が表示されるためです。
プロジェクトの更新前または更新後に使用できる、Classic API と Unified API の変更点の "差分" が公開されていることに注意してください。 Classic で廃止された呼び出しを修正すると、多くの場合、時間の節約になります (ドキュメントの検索が少なくなります)。
以下の手順に従って、既存の iOS アプリまたは Mac アプリを Unified API に更新します。 コードの移行に関するその他の情報については、このページの残りの部分と、これらのヒントを確認してください。
NuGet
以前は Classic API 経由で Xamarin.iOS をサポートしていた NuGet パッケージは、Monotouch10 プラットフォーム モニカーを使ってアセンブリを公開していました。
Unified API では、互換性のあるパッケージの新しいプラットフォーム識別子 Xamarin.iOS10 が導入されました。 このプラットフォームのサポートを追加するには、Unified API に対して構築することで、既存の NuGet パッケージを更新する必要があります。
重要
アプリケーションを Unified API に変換した後、"Error 3 Cannot include both 'monotouch.dll' and 'Xamarin.iOS.dll' in the same Xamarin.iOS project - 'Xamarin.iOS.dll' is referenced explicitly, while 'monotouch.dll' is referenced by 'xxx, Version=0.0.000, Culture=neutral, PublicKeyToken=null'" という形式のエラーが表示されたら、通常は、Unified API に更新されていないコンポーネントまたは NuGet パッケージがプロジェクト内に存在することが原因です。 既存のコンポーネントまたは NuGet を削除し、Unified API をサポートするバージョンに更新し、クリーン ビルドを実行する必要があります。
64 ビットへの道
32 ビットおよび 64 ビット アプリケーションのサポートの背景とフレームワークに関する情報については、「32 ビットおよび 64 ビット プラットフォームの考慮事項」を参照してください。
新しいデータ型
違いの中心となるのは、Mac と iOS API はどちらも、32 ビット プラットフォームでは常に 32 ビット、64 ビット プラットフォームでは 64 ビットであるアーキテクチャ固有のデータ型を使うことです。
たとえば、Objective-C では、NSInteger
データ型を 32 ビット システムでは int32_t
に、64 ビット システムでは int64_t
にマップします。
この動作に対応するために、Unified API では、以前使用されていた int
(.NET では常に System.Int32
と定義されています) を新しい System.nint
データ型に置き換えます。 "n" は "native (ネイティブ)" を意味するため、そのプラットフォームのネイティブ整数型と考えることができます。
nint
、nuint
、nfloat
が導入され、必要に応じてそれらの上に構築されたデータ型が提供されます。
これらのデータ型の変更の詳細については、ネイティブ型のドキュメントを参照してください。
iOS アプリのアーキテクチャを検出する方法
アプリケーションが 32 ビットか、64 ビットのどちらの iOS システムで実行されているのかを知る必要がある状況が考えられます。 次のコードを使ってアーキテクチャを確認できます。
if (IntPtr.Size == 4) {
Console.WriteLine ("32-bit App");
} else if (IntPtr.Size == 8) {
Console.WriteLine ("64-bit App");
}
配列と System.Collections.Generic
C# インデクサーは int
の型を想定しているため、コレクションまたは配列内の要素にアクセスするには、nint
の値を int
に明示的にキャストする必要があります。 次に例を示します。
public List<string> Names = new List<string>();
...
public string GetName(nint index) {
return Names[(int)index];
}
64 ビットでは int
から nint
へのキャストに不可逆性があり、暗黙的な変換は行われないため、これは予期された動作です。
DateTime から NSDate への変換
Unified API を使う場合、DateTime
値から NSDate
値への暗黙的な変換は実行されなくなりました。 これらの値は、ある型から別の型に明示的に変換する必要があります。 次の拡張機能メソッドを使って、このプロセスを自動化できます。
public static DateTime NSDateToDateTime(this NSDate date)
{
// NSDate has a wider range than DateTime, so clip
// the converted date to DateTime.Min|MaxValue.
double secs = date.SecondsSinceReferenceDate;
if (secs < -63113904000)
return DateTime.MinValue;
if (secs > 252423993599)
return DateTime.MaxValue;
return (DateTime) date;
}
public static NSDate DateTimeToNSDate(this DateTime date)
{
if (date.Kind == DateTimeKind.Unspecified)
date = DateTime.SpecifyKind (date, /* DateTimeKind.Local or DateTimeKind.Utc, this depends on each app */)
return (NSDate) date;
}
非推奨の API と入力ミス
Xamarin.iOS の Classic API (monotouch.dll) 内では、[Obsolete]
属性が 2 つの異なる方法で使われていました。
- 非推奨の iOS API: これは、新しいものに置き換えられるため、Apple が API の使用を中止するよう示唆する場合です。 Classic API はまだ問題がなく、しばしば必要になります (以前のバージョンの iOS をサポートしている場合)。
このような API (および
[Obsolete]
属性) は、新しい Xamarin.iOS アセンブリに含まれています。 - 不正な API 一部の API の名前に入力ミスがありました。
元のアセンブリ (monotouch.dll と XamMac.dll) については、互換性を確保するために古いコードが残されていましたが、Unified API アセンブリ (Xamarin.iOS.dll と Xamarin.Mac) からは削除されています
NSObject サブクラス .ctor(IntPtr)
すべての NSObject
サブクラスには、IntPtr
を受け入れるコンストラクターがあります。 これは、ネイティブ ObjC ハンドルから新しいマネージド インスタンスのインスタンスを作成する方法です。
Classic では、これは public
コンストラクターでした。 ただし、この機能はユーザー コードで簡単に誤用されていました。たとえば、1 つの ObjC インスタンスに対して複数のマネージド インスタンスを作成する、"または"、想定されるマネージド状態 (サブクラスの場合) が不足するマネージド インスタンスを作成する、などです。
この種の問題を回避するために、IntPtr
コンストラクターは Unified API で protected
になり、サブクラス化のみに使われます。 これにより、ハンドルからマネージド インスタンスを作成するために正しい/安全な API が使われることが保証されます。
var label = Runtime.GetNSObject<UILabel> (handle);
この API は、既存のマネージド インスタンスを返す (既に存在する場合) か、新しいマネージド インスタンスを作成 (必要な場合) します。 これは、Classic API と Unified API の両方で既に使用できます。
.ctor(NSObjectFlag)
も protected
になりましたが、これはサブクラス化以外ではほとんど使われなかったことに注意してください。
NSAction の Action への置き換え
Unified API では、標準の .NET Action
を優先して、NSAction
が削除されました。 NSAction
は Xamarin.iOS に固有のものであるのに対し、Action
は一般的な .NET 型であるため、これは大きな改善です。 どちらもまったく同じことを行いますが、異なる型で互換性がないため、同じ結果を得るためにより多くのコードを記述する必要がありました。
たとえば、既存の Xamarin アプリケーションに次のコードが含まれているとします。
UITapGestureRecognizer singleTap = new UITapGestureRecognizer (new NSAction (delegate() {
ShowDropDownAnimated (tblDataView);
}));
これは、単純なラムダ式に置き換えることができます。
UITapGestureRecognizer singleTap = new UITapGestureRecognizer (() => ShowDropDownAnimated(tblDataView));
以前は、Action
を NSAction
に割り当てることができないためコンパイラ エラーでしたが、現在では UITapGestureRecognizer
は NSAction
ではなく Action
を受け取るので、Unified API では有効になっています。
Action<T> に置き換えられたカスタム デリゲート
Unified では、いくつかの単純な (たとえば、1 つのパラメーターの) .net デリゲートが Action<T>
に置き換えられました。 例:
public delegate void NSNotificationHandler (NSNotification notification);
は、Action<NSNotification>
として使用できるようになりました。 これにより、コードの再利用が促進され、Xamarin.iOS と独自のアプリケーションの両方でのコードの重複が軽減されます。
Task<bool> の Task<Boolean,NSError>> への置き換え
Classic には、Task<bool>
を返す非同期 API がいくつかありました。 ただし、それらの一部は、NSError
がシグネチャの一部である場合に使われるものでした。つまり、bool
が既に true
であり、NSError
を取得するには例外をキャッチする必要がありました。
いくつかのエラーは非常に一般的であり、戻り値が有用ではなかったので、このパターンは Unified で Task<Tuple<Boolean,NSError>>
を返すように変更されました。 これにより、成功と、非同期呼び出し中に発生した可能性のあるエラーの両方をチェックすることができます。
NSString と文字列
いくつかのケース、たとえば UITableViewCell
では、いくつかの定数を string
から NSString
に変更する必要がありました
クラシック
public virtual string ReuseIdentifier { get; }
Unified
public virtual NSString ReuseIdentifier { get; }
一般に、.NET System.String
型を使います。 ただし、Apple のガイドラインにもかかわらず、一部のネイティブ API は定数ポインター (文字列自体ではなく) を比較しており、これは定数を NSString
として公開する場合にのみ機能します。
Objective-C プロトコル
元の MonoTouch は、ObjC プロトコルを完全にはサポートしておらず、最も一般的なシナリオをサポートするために、いくつかの最適化されていない API が追加されました。 この制限はもう存在しませんが、下位互換性のために、いくつかの API が monotouch.dll
と XamMac.dll
内に維持されています。
これらの制限は、Unified API で削除され、クリーンアップされました。 ほとんどの変更は次のようになります。
クラシック
public virtual AVAssetResourceLoaderDelegate Delegate { get; }
Unified
public virtual IAVAssetResourceLoaderDelegate Delegate { get; }
I
プレフィックスは、Unified が ObjC プロトコルの特定の型ではなくインターフェイスを公開することを意味します。 これにより、Xamarin.iOS が提供する特定の型をサブクラス化しない場合が容易になります。
また、一部の API がより正確で使いやすくなりました。例:
クラシック
public virtual void SelectionDidChange (NSObject uiTextInput);
Unified
public virtual void SelectionDidChange (IUITextInput uiTextInput);
このような API は、ドキュメントを参照することなく簡単に使用できるようになり、IDE コード入力候補により、プロトコル/インターフェイスに基づいてより有用な提案が提供されます。
NSCoding プロトコル
元のバインドには、NSCoding
プロトコルをサポートしていない場合でも、すべての型の .ctor(NSCoder) が含まれていました。 オブジェクトをエンコードするための 1 つの Encode(NSCoder)
メソッドが NSObject
に存在していました。
ただし、この方法はインスタンスが NSCoding プロトコルに準拠している場合にのみ機能します。
Unified API ではこれを修正しました。 型が NSCoding
に準拠している場合、新しいアセンブリには .ctor(NSCoder)
のみが含まれます。 また、そのような型には、INSCoding
インターフェイスに準拠する Encode(NSCoder)
メソッドが追加されました。
低影響: 削除された古いコンストラクターは使用できないため、ほとんどの場合、この変更はアプリケーションには影響しません。
その他のヒント
注意すべきその他の変更点は、アプリを Unified API に更新するためのヒントに関するページに記載されています。