Xamarin.iOS の Swift バインディングライブラリを作る

Xamarin Advent Calendar 2016 17日目です。

以前から僕はネイティブバインディング関連の記事をいろいろ書いてきました。

今回もネイティブバインディングです!

Swift が使われるようになってしばらくたち、CocoaControlsに新しく出てきている今風の UI ライブラリはほとんど Swift 製になりつつあります。 Swift を使う場合、Swift のサポートライブラリ(ランタイム?)が必要となり、Swift で作ったアプリはデバッグ実行やアドホック配布はできるけどストアに上がらない…!というのはネイティブでもよくあります。

Xamarin でのネイティブバインディングObjective-C と異なる手順が必要となります。どのようにすればいいかをご紹介します。

今回は Objective Sharpie で TTSegmentedControl (CocoaControls, GitHub) のバインディングをやってみましょう。

Simulator Screen Shot 2016.12.17 18.27.52.png

Swift と Xcode のバージョンを確認する

使用したいライブラリが決まったら、そのライブラリが対象としている Swift のバージョンを確認しましょう。これにより、この後使用する SwiftSupport の NuGet も変わってきます。

また、異なるバージョンの Swift で書かれたライブラリの混在はできない(ネイティブでもできない)ので、注意しましょう。

Swift バージョン Xcode バージョン iOS SDK のバージョン SwiftSupport の NuGet
Swift 2.2 Xcode 7.3.1 iphoneos9.3 Xamarin.SwiftSupport
Swift 2.3 Xcode 8.x iphoneos10.x Xamarin.Swift23.Support
Swift 3.0 Xcode 8.x iphoneos10.x (現段階で存在せず)

もし、対応する Xcode を持っていない場合、Downloads for Apple Developers というページからダウンロードしましょう。 このページには過去の Xcodeアーカイブされています。

複数の Xcode を環境中に共存することもできます。Xcodeの複数バージョンを共存をご覧ください。

TTSegmentedControl は GitHub の README.md にもあるとおり、Swift 3.0 と 2.x のどちらでも使えますが、2.x を使う場合は 0.1.1 を指定する必要があります。 今回はこれを Swift 2.3 で使うこととし、Xcode 8.2 を使って作業していきます。

Objective Sharpie でネイティブライブラリを作る

使った Objective Sharpie のバージョンは以下の通りです。

$ sharpie -v
3.3.0p 3d8547f 3d8547fcf1f7c2c2337b122c8d1fe893eb6a1ad3 2016-08-03 04:49:53

まず、sharpie pod init でソースコードをダウンロードします。

$ sharpie pod init iphoneos10.2 TTSegmentedControl

プロジェクトが作成され、ライブラリのソースコードがダウンロードされますが、バージョン指定して Swift 2.x で使えるバージョンをダウンロードし直します。 できた Podfile を編集して、0.1.1 指定にします。

platform :ios, '10.2'
install! 'cocoapods', :integrate_targets => false
target 'ObjectiveSharpieIntegration' do
  use_frameworks!
end
pod 'TTSegmentedControl', '0.1.1'

Podfile を編集したら、pod install で反映します。

$ pod install

次に、できた Pods/Pods.xcodeproj を確認します。

目的とするライブラリのターゲットの Enable Bitcode を No にします。(これをしておかないと、ストア申請時に Invalid Bitcode エラーとなります)

スクリーンショット 2016-12-17 13.46.34.png

Xcode 8.x で Swift 2.3 を使うときは Swift Use Legacy Swift Version を明示指定する必要があります。

スクリーンショット 2016-12-17 13.49.39.png

Deployment Target も必要とするバージョンまで下げておきましょう。

スクリーンショット 2016-12-17 13.48.10.png

あとは、sharpie pod bind でビルドします。

$ sharpie pod bind

Mac インストール直後で requires Xcode といわれてしまう場合は、xcode-select コマンドを実行してコマンドラインで使用する Xcode を指定してやりましょう。

$ sudo xcode-select --switch /Applications/Xcode.app

できた Binding フォルダの中にある .framework と .cs が必要になります。

スクリーンショット 2016-12-17 13.59.48.png

が、これだけではシミュレーターで実行できない状態となります。 Objective Sharpie 実行時点では実機用のバイナリしか生成されないため、シミュレーター向けのビルドは別途手動で実行し、実機向けのビルドと合成させる必要があります。

まずは、シミュレーター向けの .framework を作ります。Pods フォルダに移動して、xcodebuild コマンドでビルドしましょう。 最後の codesign の結果で、どのパスに生成されたかがわかります。

$ cd Pods
$ xcodebuild -project Pods.xcodeproj -target TTSegmentedControl -sdk iphonesimulator10.2 -configuration Release build
(中略)
Signing Identity:     "-"

    /usr/bin/codesign --force --sign - --timestamp=none /Users/***/Projects/TTSegmentedControlSample/Native/build/Release-iphonesimulator/TTSegmentedControl/TTSegmentedControl.framework

できたシミュレータ用の Framework のバイナリと、Objective Sharpie が生成したネイティブのライブラリを合成します。 lipo コマンドを使い合成した結果で Objective Sharpie の出力を置き換えます。

  • lipo コマンドの引数
    • lipo -create [合成するバイナリ] [合成するバイナリ] -output [出力先のバイナリ]
    • lipo -info [確認するバイナリ]
$ cd ..
$ ls
Binding     Podfile     Podfile.lock    Pods        build
$ lipo -create Binding/TTSegmentedControl.framework/TTSegmentedControl /Users/***/Projects/TTSegmentedControlSample/Native/build/Release-iphonesimulator/TTSegmentedControl/TTSegmentedControl.framework/TTSegmentedControl -output TTSegmentedControl
$ ls
Binding         Podfile         Podfile.lock        Pods            TTSegmentedControl  build
$ lipo -info TTSegmentedControl
Architectures in the fat file: TTSegmentedControl are: i386 x86_64 armv7 arm64 
$ mv TTSegmentedControl Binding/TTSegmentedControl.framework/TTSegmentedControl

また、i386x86_64 の SwiftModule も必要となるので、あわせてコピーします。

$ cp /Users/***/Projects/TTSegmentedControlSample/Native/build/Release-iphonesimulator/TTSegmentedControl/TTSegmentedControl.framework/Modules/TTSegmentedControl.swiftmodule/* Binding/TTSegmentedControl.framework/Modules/TTSegmentedControl.swiftmodule/
$ ls Binding/TTSegmentedControl.framework/Modules/TTSegmentedControl.swiftmodule/
arm.swiftdoc        arm.swiftmodule     arm64.swiftdoc      arm64.swiftmodule   i386.swiftdoc       i386.swiftmodule    x86_64.swiftdoc     x86_64.swiftmodule

バインディングライブラリを作る

あとは、新しくなったObjective SharpieでCocoaPodsのバインディングライブラリを作る とほとんど手順は同じなのですが、1つだけ異なる点があります。

.framework のライブラリは「ネイティブ参照」のところに追加する必要があります。右クリックして、Add Native Reference を選択します。

スクリーンショット 2016-12-17 14.01.50.png

.framework を選択すると、中身が見えてしまいますが、.framework がアクティブになった状態のまま Open をクリックして追加します。

スクリーンショット 2016-12-17 14.06.35.png

プロパティを開いて Force Load のチェックを入れ、使用する Framework を指定しておきます。(Dark スキームだとちょっと見づらいです)

スクリーンショット 2016-12-17 18.32.47.png

次は ApiDefinition の作成です。まず、Swift のクラスはネイティブライブラリ内でも名前が変わってしまうので、別名指定を BaseTypeAttribute に追加する必要があります。 .framework の中にできた Headers/*-Swift.h の中にクラスの名前が SWIFT_CLASS マクロで書かれています。

SWIFT_CLASS("_TtC18TTSegmentedControl18TTSegmentedControl")
@interface TTSegmentedControl : UIView

これを、BaseType の Name に追加してやります。

[BaseType(typeof(UIView), Name = "_TtC18TTSegmentedControl18TTSegmentedControl")]
interface TTSegmentedControl

また、Swift 側でプライベートのエクステンションを多く使っている場合は空のインターフェイスが大量に生成されるのと、UIKit の override など、BaseType が持っているようなメソッドの定義は削除しておきましょう。

今回の ApiDefinition.cs は以下のようになりました。

using System;

using UIKit;
using Foundation;
using ObjCRuntime;
using CoreGraphics;

namespace TTSegmentedControl
{
    // @interface TTSegmentedControl : UIView
    [BaseType(typeof(UIView), Name = "_TtC18TTSegmentedControl18TTSegmentedControl")]
    interface TTSegmentedControl
    {
        // @property (nonatomic, strong) UIFont * _Nonnull defaultTextFont;
        [Export("defaultTextFont", ArgumentSemantic.Strong)]
        UIFont DefaultTextFont { get; set; }

        // @property (nonatomic, strong) UIFont * _Nonnull selectedTextFont;
        [Export("selectedTextFont", ArgumentSemantic.Strong)]
        UIFont SelectedTextFont { get; set; }

        // @property (nonatomic, strong) UIColor * _Nonnull defaultTextColor;
        [Export("defaultTextColor", ArgumentSemantic.Strong)]
        UIColor DefaultTextColor { get; set; }

        // @property (nonatomic, strong) UIColor * _Nonnull selectedTextColor;
        [Export("selectedTextColor", ArgumentSemantic.Strong)]
        UIColor SelectedTextColor { get; set; }

        // @property (nonatomic) BOOL useGradient;
        [Export("useGradient")]
        bool UseGradient { get; set; }

        // @property (nonatomic, strong) UIColor * _Nonnull containerBackgroundColor;
        [Export("containerBackgroundColor", ArgumentSemantic.Strong)]
        UIColor ContainerBackgroundColor { get; set; }

        // @property (nonatomic, strong) UIColor * _Nonnull thumbColor;
        [Export("thumbColor", ArgumentSemantic.Strong)]
        UIColor ThumbColor { get; set; }

        // @property (copy, nonatomic) NSArray<UIColor *> * _Nullable thumbGradientColors;
        [NullAllowed, Export("thumbGradientColors", ArgumentSemantic.Copy)]
        UIColor[] ThumbGradientColors { get; set; }

        // @property (nonatomic, strong) UIColor * _Nonnull thumbShadowColor;
        [Export("thumbShadowColor", ArgumentSemantic.Strong)]
        UIColor ThumbShadowColor { get; set; }

        // @property (nonatomic) BOOL useShadow;
        [Export("useShadow")]
        bool UseShadow { get; set; }

        // @property (nonatomic) CGSize padding;
        [Export("padding", ArgumentSemantic.Assign)]
        CGSize Padding { get; set; }

        // @property (nonatomic) CGFloat cornerRadius;
        [Export("cornerRadius")]
        nfloat CornerRadius { get; set; }

        // @property (copy, nonatomic) NSArray<NSString *> * _Nonnull itemTitles;
        [Export("itemTitles", ArgumentSemantic.Copy)]
        string[] ItemTitles { get; set; }

        // @property (copy, nonatomic) void (^ _Nullable)(NSInteger, NSString * _Nullable) didSelectItemWith;
        [NullAllowed, Export("didSelectItemWith", ArgumentSemantic.Copy)]
        Action<nint, NSString> DidSelectItemWith { get; set; }

        // @property (nonatomic) BOOL allowDrag;
        [Export("allowDrag")]
        bool AllowDrag { get; set; }

        // @property (nonatomic) BOOL allowChangeThumbWidth;
        [Export("allowChangeThumbWidth")]
        bool AllowChangeThumbWidth { get; set; }
    }


    // @interface TTSegmentedControl_Swift_157 (TTSegmentedControl)
    [Category]
    [BaseType(typeof(TTSegmentedControl))]
    interface TTSegmentedControl_TTSegmentedControl_Swift_157
    {
        // -(void)selectItemAtIndex:(NSInteger)index animated:(BOOL)animated;
        [Export("selectItemAtIndex:animated:")]
        void SelectItemAtIndex(nint index, bool animated);
    }
}

プロジェクトに Swift ライブラリを追加する

今回は同一ソリューションに iOS の Single View アプリケーションを追加したので、プロジェクト参照でバインディングライブラリを参照しました。

また、Swift を使ったライブラリを使う場合、SwiftSupport の NuGet ライブラリが必要となりますので、パッケージを追加しておきます。

スクリーンショット 2016-12-17 14.09.42.png

実機で署名が合わないとき

さて、このまま AdHoc ビルドをすると署名が合わずインストールができなくなる場合があります。これは、SwiftSupport のパッケージがビルドの終盤でシミュレータ用のバイナリも一度まとめてコピーして、シミュレータじゃなかった場合は消すという動きをするので、署名が微妙に合わなくなるのです。

このようなときはシミュレータ向けのバイナリを消すタイミングを変更することで回避できます。.csproj を開き、以下の Target を追加してください。

  <Target Name="RemoveSimulatorSymbols" AfterTargets="_CopyResourcesToBundle" Condition="'$(Platform)' == 'iPhone'">
    <Exec Command="rm -rf $(OutputPath)/$(AssemblyName).app/SwiftFrameworksSimulator" />
  </Target>

ストアに申請する ipa を作る

さて、いよいよストアに申請という段になっても、また別の作業が必要です。Swift を使うアプリの場合、ipa の中に SwiftSupport というものを持っておく必要があります。 Xcode の中に入っている Swift のライブラリを追加するという作業になるのですが、この作業をするためのスクリプト auto-ipa-packager が公開されています。

Xamarin Studio から出力されたストアビルドに対して、このスクリプトを実行することで、ストア申請用の SwiftSupport を追加することができます。

このコマンドを使うとき、使用している Swift ライブラリにあった Xcode の中にある Developer ディレクトリを DEVELOPER_DIR 環境変数として指定する必要があります。(Xcode とライブラリの Swift バージョンが一致しない場合は Invalid Swift Support となってしまいます)

$ DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer sh auto_package_ipa.sh Hoge.ipa

また、この作業でもパッケージの署名がおかしくなる場合があります。このような場合は、fastlane の sigh コマンドを使って再署名してやります。

$ sigh resign ./Hoge.ipa --signing_identity 'iPhone Distribution: Nobuhiro Ito (XXXXXXXXXX)' -p net.iseteki.hoge=EbiSoft_Hoge_for_AppStore.mobileprovison

Swift のライブラリを使うのはまだまだ大変ですが、今のおしゃれな UI コンポーネントは Swift が多いので、ぜひ覚えておいて損はないかと思います。

ただ、こんな手のかかることやりたくないというのも事実ですので、Xamarin 自体の Swift サポートが強化されるのを願いたいと思います。

Project Rider EAP で Xamarin がサポートされました (あと .NET Core RTM も)

f:id:iseebi:20160508090917p:plain

先日 Project Rider EAP で Xamarin を書けるか とかいう記事を書いたわけですが、ついに公式に Xamarin がサポートされました。

公式にはどちらかというと、.NET Core RTM やデバッグサポートのほうが先に来てたのですが、かなり待ってただけに当初 Xamarin Support の字しか目に入りませんでした!(ぁ)

とりあえず軽く触ってみた感想を書いてみます。

  • Xamarin.iOS / Xamarin.Android 共に開ける。
  • かなり大きなプロジェクトを開いても全然快適だった。(きちんと見たわけではないけど)
  • 実行するのは Unhandled Exception になって無理だった。
  • Storyboard などのリソースものは開けなかった。
  • IdeaVim がちゃんと動く! Vi モードなくなってアドインになった Xamarin Studio より圧倒的に快適。

というわけで、コード書く分には ReSharper 相当の支援機能や Android Studio 同様のアドインが使えるのでかなり快適です。Xamarin Studio と Rider の両方で開きながら、リソース編集や実行を Xamarin Studio でやれば今のところはいけるのだろうか。しばらく使ってみようと思います。

Xamarin Studio 6.0 から Vi モードがなくなって死活問題になってる方へ

f:id:iseebi:20160609020828p:plain

Xamarin の Cycle 7 アップデートが Stable になり、Xamarin Studio 6.0 が Stable に降ってきました。

Cycle 7 全体については、Stable Release: Cycle 7 | Xamarin Releases からリンクをたどれますが、Xamarin Studio 6.0 のリリースノート の Text Editor のところを見たら、Alpha のときから消えなかった "This release does not include the VI mode." という一文があります。Stable になるときに消えるかなーと思ってたのですが、ついになくなってしまったようです。

Vimmer は多少実装が残念だったとしても、hjkl でカーソルが動かないとコード書くことができません。これでは仕事にならない…と途方に暮れてしまっていたのですが、id:atsushieno さんにコミュニティアドインがあることを教えていただきました。

続きを読む

Project Rider EAP で Xamarin を書けるか。

Project Rider といえば、JetBrains が開発中のIDEです。内部にはReSharperが入っているということもあり、大きく期待されています。

f:id:iseebi:20160508090917p:plain

まだ EAP (Early Access Preview) なので、いろいろ動かない部分があるのですが、我々 Xamarin ユーザーとしては Xamarin の開発に使えるかどうかが最大の興味でしょう。

Xamarin のプロジェクトをロードしたときのエラーメッセージから、なんとなくいける気がしたので試してみました。

続きを読む

Xamarin 用の Injection フレームワーク Saitama を使ってみた

NuGet のフィードを眺めていたら、Saitama.Android というフレームワークが更新されたという情報が流れてきました。

どういう経緯でこの名前がついたのかわかりませんが、日本人的にはすぐにこの AA を思い浮かべるでしょう。*1

f:id:iseebi:20160406064936g:plain

名前はともかく、紹介記事を読んでみると、MvvmCross がつらいけど MvvmCross のインジェクションっぽいのが欲しくて作ったみたいなことが書いてあって気になったので試してみました。

*1:その後 @atsushieno さんに指摘いただいたのですが、ワンパンマンのほうじゃないかとのこと。そういえばあっちもサイタマだった…。

続きを読む

新しくなったObjective SharpieでCocoaPodsのバインディングライブラリを作る

この記事はXamarin Advent Calendar 2015の7日目の記事です。

Xamarin.iOSCocoa バインディングライブラリを作る際、Objective Sharpieというツールを使いますが、このツールは対象となるライブラリのヘッダファイルを自分で読み込ませた上で、Xcodeを使って.aをビルドして…等、各種ツールを使った手間がありました。

Objective Sharpieを使いたい場面というのは、だいたいは CocoaPods を使うときでしょう。CocoaPods には様々なライブラリが揃っており、特に UI 周りのライブラリは Xamarin を使った開発でも Xcode を使った開発同様にとても有用です。

11月にリリースされた、Objective Sharpie 3.0 には CocoaPods のネイティブバインディングを簡単に生成する機能が追加されました。これを使ってバインディングライブラリを作成する方法を紹介します。

Objective Sharpie のダウンロード

Objective Sharpie は Xamarin のドキュメントページ からダウンロードできます。

ただし、すでに Objective Sharpie をダウンロードしたことのある方は、sharpie updateを実行するだけで更新することができます。

Objective Sharpie を使った CocoaPods バインディング

まず、CocoaPodsCocoaControlsで使いたいライブラリの名前を探しましょう。今回は MBProgressHUD をバインディングしてみます。

まずは適当にフォルダを作りましょう。

$ mkdir MBProgressHUD
$ cd MBProgressHUD

使用できるSDK を確認します。だいたいの場合は最新の iphoneos を使えば良いでしょう。

$ sharpie xcode -sdks
sdk: appletvos9.0    arch: arm64   
sdk: iphoneos9.1     arch: arm64   armv7   
sdk: iphoneos9.0     arch: arm64   armv7   
sdk: iphoneos8.4     arch: arm64   armv7   
sdk: macosx10.11     arch: x86_64  i386    
sdk: macosx10.10     arch: x86_64  i386    
sdk: watchos2.0      arch: armv7  

では、ライブラリのプロジェクトを生成します。sharpie pod initを使います。最初は CocoaPods のマスターリポジトリを取得するため時間がかかります。

$ sharpie pod init iphoneos9.1 MBProgressHUD
** Setting up CocoaPods master repo ...
   (this may take a while the first time)
Setting up CocoaPods master repo
-- このあたりに Master repo の取得状態が出る --
Setup completed
** Searching for requested CocoaPods ...
** Working directory: 
**   - Writing Podfile ...
**   - Installing CocoaPods ...
**     (running `pod install --no-integrate --no-repo-update`)
Analyzing dependencies
Downloading dependencies
Installing MBProgressHUD (0.9.1)
Generating Pods project
Sending stats
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
** 🍻  Success! You can now use other `sharpie pod` commands.

Podfile と Pods ディレクトリができます。

f:id:iseebi:20151207024856p:plain

もし、Deployment Target を下げたライブラリが必要であれば、ここで、Pods/Pods.xcodeproj を開き、Deployment Target を下げておきます。

f:id:iseebi:20151207024930p:plain

Deployment Targets を下げるなど、ビルドの準備ができたらsharpie pod bindコマンドを実行します。

$ sharpie pod bind
User defaults from command line:
    IDEDerivedDataPathOverride = /Users/ito/Working/MBProgressHUD/Pods/sharpie-build

=== BUILD TARGET MBProgressHUD OF PROJECT Pods WITH CONFIGURATION Release ===

-- 長々と xcodebuild のログが出ます --

** BUILD SUCCEEDED **

Starting project evaluation for target: 'Pods', configuration: 'Release'
    Computing dependencies
    Target MBProgressHUD (FE2F1D7B9D9FCEA148517E4657B243F4):
        Phase PBXSourcesBuildPhase
        Phase PBXFrameworksBuildPhase
        Phase PBXHeadersBuildPhase
    Target Pods (CA6B6247C232863826EA479E552E6B28):
        Phase PBXSourcesBuildPhase
        Phase PBXFrameworksBuildPhase
Parsing 1 header files...

Binding...
  [write] ApiDefinitions.cs
  [write] StructsAndEnums.cs

Binding Analysis:
  Automated binding is complete, but there are a few APIs which have been flagged with [Verify] attributes. While the entire binding should be audited for best API design practices, look more closely at APIs with the following Verify attribute
  hints:

  StronglyTypedNSArray (1 instance):
    A native NSArray* was bound as NSObject[]. It might be possible to more strongly type the array in the binding based on expectations set through API documentation (e.g. comments in the header file) or by examining the array contents through
    testing. For example, an NSArray* containing only NSNumber* instances can be bound as NSNumber[] instead of NSObject[].

  Once you have verified a Verify attribute, you should remove it from the binding source code. The presence of Verify attributes intentionally cause build failures.
  
  For more information about the Verify attribute hints above, consult the Objective Sharpie documentation by running 'sharpie docs' or visiting the following URL:

    http://xmn.io/sharpie-docs

Submitting usage data to Xamarin...
  Submitted - thank you for helping to improve Objective Sharpie!

Done.

ビルドした結果、build/Release-iphoneos/lib*.a に必要なファイルが作られます。また、Objective Sharpieの定義変換結果もあわせて出力されます。あとはこれらのファイルを使ってバインディングライブラリプロジェクトを用意すれば OK です。

f:id:iseebi:20151207024947p:plain

ここまでやってくれるんだったら、csprojの生成までやってくれたらよかったのに!としか思えないのですが、これだけでもバインディングの手間がかなり省けると思います。Xamarin.iOS 開発のお供にぜひ活用してみてください。

MvvmCross 4.0 beta について

いま、MvvmCross は 4.0 リリースに向けてβ版のリリースが続いています。

最近のウォッチしてる様子をご紹介します。

MvvmCross 4.0

公式の受け売りですが、だいたいこんな感じのようです。

  • Universal Windows Platform 対応
  • NuGet パッケージの Visual Studio 2015 対応
  • Xamarin.Forms サポート (のサンプルプロジェクトが公式化した)
  • Fragments のバックスタックハンドリング (Fragment 使ってるときに戻れるようになった)
  • テキストフォーカスにバインディングできるようになった
  • Roslyn Analyzer が追加されて VS2015 ではクイックフィックスみたいなのが出るようになった。
    • まだ 1 つしか指摘してくれないみたいだけどそのうち色々追加されてくるはず。
  • MvxPageViewController UIPageViewController の MvvmCross 版で、IMvxPageViewModel を実装すると各ページの ViewModel を返せるようになる。
    • iOS のことしか考えられてない系の実装になっており、使う前に要検討かも。

その他いろいろ変更が入っています。その他のあたりは マイルストーン4.0のPR みるといいかも。

Android のプロジェクトで "No more copying MvxAttributes.xml on Android" とか書いてあったので、もしかしたらプロジェクトの作り方も変わるかも。かも、というのは Xamarin Studio (Mac) でまだプレリリース版 NuGet パッケージのインストールがうまくいったことがないからです。

リポジトリが分かれた

今まで MvvmCross は Github の MvvmCross/MvvmCross に全部入っていましたが、以下のように分かれました。

  • MvvmCross/MvvmCross
    • ライブラリ本体
  • MvvmCross/MvvmCross-AndroidSupport
  • MvvmCross/MvvmCross-Plugins
  • MvvmCross/MvvmCross-Forms
    • '''Xamarin.Forms サポート'''。Xamarin.Froms で動作するときのための Presenter など。
  • MvvmCross/MvvmCross-Samples
    • もろもろサンプル。Contribution よろしくみたいな感じになってるみたいなので、ちっさいサンプルアプリとか作ったら送ってみるといいかも。

巨大になってたので当然の分割という感じでしょうか。

公式サイトができた

公式のニュースページができました。最新の概要情報はこのページだけでざっくりわかるようになりました。

mvvmcross.com