大学の学園祭アプリをXamarin.Formsで作りました
今回は11/25〜27に開催される「第66回調布祭」のアプリをXamarinで作りました。
以下色々と書いて行きます。
なぜXamarinなのか
今回のアプリ開発はとにかく時間がありませんでした。
僕自身はアプリを作る編集局員ではあるのですが、 学園祭で使う資材の管理等を行う資材局でもあり、 本業は資材局の方です。
この資材局というのが結構な激務で、 夏休みの一部しか時間を使えないことが事前にわかっていました。
そのような中で使ったことのないSwiftやJavaを使い、 ネイティブのアプリを2つのプラットフォーム分作るというのは不可能でした。
Xamarinを選んだと言うより、 この状況ではC#だけでクロスプラットフォーム開発ができるXamarinを選ぶしかなかった という方が正しいでしょう。
コンセプト
とにかく時間がないので、オープンソースのコンポーネントやマネージドなサービスなどをできるだけ利用し、
車輪の再発明はしない
をコンセプトに作りました。
機能
アプリの開発を始めるときに局内で出た実装したい機能は以下の様なものがありました。
- 模擬店や展示の紹介
- ステージや講堂でのイベントのタイムテーブルと通知機能
- 学内のマップ機能
また、構成としてデータはサーバで保持し、アプリを更新している時間を待たずに、 掲載情報を追加・修正できるようにしようというのも提案されました。
通常の常時公開しているアプリと異なり、 調布祭期間の3日間のみ使われるアプリであることを考えると、 必須とも言える機能です。
バックエンドについて
「第66回調布祭アプリ」のバックエンドは以下のようになっています。
- サーバはConohaのVPSを利用。
- 普段から使っているというのもあるが、お金のない委員会には月額課金ではなく時間課金であることがとてもありがたい。
- サーバのミドルウェアとしてはdocker上にオープンソースのMBaaSであるdeploydのコンテナを立て利用。
- 簡単な構成であれば手も加えず使えるため重宝した。
- 参考: オープンソースMBaaS「deployd」を使ってみる - Qiita
- サーバのプロビジョニングにはAnsibleを使用。
- スマートフォンへのプッシュ通知にはAWSのSNSを利用。
- プラットフォームの違いや、無効となったトークンの処理などを気にせずにプッシュ通知が実装できる。
- 料金も100万件送って1ドルなのでありがたい。
フロントエンド(アプリ)について
タイトルの通り、アプリはXamarin.Formsを使って実装しました。
製作は1人ですが、調布祭が近くなってからのメンテナンスは他の人がやる可能性があるため、 設計・コードには読みやすさ・理解しやすさが必要でした。 全体で統一された設計にするために、MVVMライブラリのPrismを導入しました。
MVVMはデータバインディングとも相まって非常に強力ですが、 初めて触る人には少し難しいかもしれないので、 Prismを導入し、ある程度ルールに沿ったコードで実装できたのは、 僕自身を含めてメンテナンスのしやすさにつながりました。
一番大変だったのはタイムテーブルの実装です。 模擬店・展示の紹介ページなどはListViewそのままである程度行けましたが、 タイムテーブル用のViewなど用意されているわけもなく、 Gridを継承した自作コントロールを作ることで対応しました。
しかし、無理にデータバインディングを利用しようとしたせいか、 サーバとの通信の間、データがまだ無いため、何も表示されずに2,3秒経過する状態になってしまっていて、 現在、何かいい方法がないか模索中です。 (いいアイデアがあったら教えてください)
デザインに沿ったViewの配置についてはAbsoluteLayoutを使うことで デザイン担当の希望にある程度沿ったページが作れたかなといった感じです。
今後
ダウンロードしていただければわかると思いますが、 まだまだ荒削りの段階でのリリースとなりました。
というのも、これからの約2ヶ月間で実際にいろんな方々につかっていただいて、 我々では気づかないような点も含めてご指摘いただき、 アップデートを続けていけたらという気持ちでリリースをしたからです。
ご協力をお願い致します。
ダウンロード
最後に
11/25〜27の3日間、 電気通信大学の学園祭、「調布祭」が開催されます。
皆様のご来場、心からお待ちしております。
「診断ビューワー」の技術的な話
先日リリースした「診断ビューワー」の技術的な話です。
今回はXamarinでとりあえずなにか作ってみようということで軽めに1本作りました。
しかし、いろいろ調べながら作っていたため、技術的にはかなり大きな収穫があったと思います。
iOSとAndroidの見た目の違い
Xamarin.Formsでクロスプラットフォーム開発をして、いざiOSとAndroidでビルドして動かしてみると、その見た目の違いがかなり気になります。
というのも標準ではiOSは白背景、Androidは黒背景でコントロールもその色をベースに作られているので、Xamarin.Formsでコードを共通化しても、見た目が全然違います。
これは、Android側を白ベースにしてiOSに近づけることで解決しました。
具体的には、ソリューション内のAndroidプロジェクトの中にあるMainActivity.csの以下のように変更しました。
[Activity(Label = "診断ビューアー", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, Theme = "@android:style/Theme.Holo.Light")] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity { protected override void OnCreate(Bundle bundle) { ... } }
MainActivityクラスの属性の
Theme = "@android:style/Theme.Holo.Light"
が今回変更したところです。
これでAndroidアプリが白ベースになります。
非同期処理について
ただの知識不足だったんですが、Commandにasync/awaitが使えることがわかったので、
できるところはすべて非同期処理に書き換えて、UIをブロックしないようにしました。
new Command(async () => { await Task.Run(() => /*時間のかかる処理*/); };
このように書くことで既存の同期処理を非同期処理に書き換えることができます。
シングルトンなModel
今回はXamarinに慣れる意味もあって、極力MVVMパターンを守りました。
そんななかで設計上インスタンスが何個もあるとまずいModelがありました。
最初はstaticなクラスにしようと思ったのですが、
何となく気持ち悪かったのでteratailで質問したところ無事解決することができました。
teratail.com
(なんとC#とSQL Serverカテゴリで1位の方々に答えていただきました!)
MVVMにおいて共有インスタンスはシングルトン化するのが一般的だと思います。
とのことなので、シングルトンにて実装しました。
ObservableCollectionの更新処理
ViewModelのObservableCollectionをViewにバインドしているのですが、
データの更新処理でデータをすべて書き換えるときに最初は、
void UpdateData() { // ObservableCollection を初期化 this._items = new ObservableCollection<Model>(); // 更新処理 foreach (var d in GetDate()) { this._item.Add(d); } }
このようにしていたのですが、インスタンスを初期化してしまうとバインドが切れてしまう(?)ようでうまく動きませんでした。
そこで以下のようにしたところうまくいきました。
void UpdateData() { // ObservableCollection をクリア this._items.Clear(); // 更新処理 foreach (var d in GetDate()) { this._item.Add(d); } }
インスタンスを初期化せずに要素を全部消して、その上で一から追加していく方法です。
確かによくよく考えれば当たり前な気もしますが....
まとめ
他にも様々な発見がありましたが、ブログに載せるのはこれぐらいにしたいと思います。
また今回はViewからの画面遷移になってしまったので、
MVVMに忠実にViewModelからViewModelへの遷移を実装できたらと思います。
PrismというMVVMフレームワークを使えばできるらしいのですが.........。
Xamarin.iOSでAdMobの広告がReleaseビルドの実機環境でのみ表示されない
(追記:2016/06/03 08:36 無事に審査を通過しました)
(追記:2016/06/03 03:14 解決したのでページ下部に詳細を追記)
クロスプラットフォームでアプリを開発し、Play Storeには無事公開中なのですが、
App Storeへの申請中に「広告識別子を利用してるのに広告が貼られてないよ」とのことでリジェクトされました。
そんなはずはないと思いながら添付されたスクリーンショットを見ると確かに広告が表示されていません。
テストをしたところ、
Debug ビルド
- シミュレータ → ◯
- 実機 → ◯
Release ビルド
- シミュレータ → ◯
- 実機 → 表示されない
という結果に。
iOSから導入されたATSの設定をしたりしましたが、依然として状況は変わらず.....。
StackOverflowでも同様の症状が報告されていましたが、解決には至っていませんでした。
誰かわかる方いませんか..........
無事表示されました(追記:2016/06/03 03:14)
以下のNugetパッケージを使ってAdMobを表示していたのですが、
試しにパッケージに頼らず、Custom Rendererを使って自分でAdMob用のViewを実装したら無事表示されました!
www.nuget.org
このままApp Storeへと申請したいと思います。
WebAPIのモックアップ作りにjson-serverがすごい便利だった
現在、Xamarinを使って学祭のアプリを開発中なのですが、サーバサイドとクライアントサイドで友人と分担してて、クライアントサイド担当の私がAPIの仕様決めて友人がそれを作るみたいな感じでやってます。
何度も仕様を変更するのも悪いので、ガチガチに仕様を固めてから渡そうとローカルでAPIのモックアップを作ろうと思ったらこんなのを見つけました。
【個人メモ】JSON Serverを使って手っ取り早くWebAPIのモックアップを作る - Qiita
http://qiita.com/futoase/items/2859a60c8b240da70572
npmパッケージのjson-serverというもので、jsonファイルを元にWebAPIのモックアップを作ってくれるらしいです。
さっそく使ってみる。
インストール
ターミナルで以下のコマンドを入力して下さい。
>npm install -g json-server
npmコマンドが使える前提です。-gオプションを付けてシステムにインストールしています。
jsonファイルを用意する
初めは引用元のような下のjsonファイルを用意しました。
{ "users": [ { "id": 1, "name": "futoase" }, { "id": 2, "name": "hogehoge" } ], "limit": 100 }
すると、次のようなエラーが発生。
>json-server db.json \{^_^}/ hi! Loading db.json Done /usr/local/lib/node_modules/json-server/src/server/router/index.js:73 throw new Error(msg) ^ Error: Type of "limit" (number) in db.json is not supported. Use objects or arrays of objects. at /usr/local/lib/node_modules/json-server/src/server/router/index.js:73:11 at /usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:4389:15 at baseForOwn (/usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:2652:24) at /usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:4358:18 at Function.forEach (/usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:8561:14) at /usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:3877:28 at arrayReduce (/usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:639:21) at baseWrapperValue (/usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:3876:14) at LodashWrapper.wrapperValue (/usr/local/lib/node_modules/json-server/node_modules/lodash/lodash.js:8261:14) at LodashWrapper.<anonymous> (/usr/local/lib/node_modules/json-server/node_modules/lowdb/lib/index.js:77:19)
どうやらlimitのところでエラーが起きているようです。
原因はわからず.....。
とりあえずlimitのところを削ると無事動きました。
>json-server db.json \{^_^}/ hi! Loading db.json Done Resources http://localhost:3000/users Home http://localhost:3000 Type s + enter at any time to create a snapshot of the database
表示されているように、
http://localhost:3000/users
でユーザーの一覧を取得できるみたいです。
その他
引用元では他にもレコードの更新やスナップショットの取り方も公開されています。
まとめ
json-server、非常に便利ですね。
サーバと連携するアプリの開発が捗りそうです。