SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

シルバーウィークにXamarin.FormsでFacebookアプリを作成した

【 Xamarin 記事一覧 】

1.#6 JXUGC

10月3日、「第6回 Japan Xamarin User Group Conference 東京 事例スペシャル!」に参加させて頂きました。
第6回 Japan Xamarin User Group Conference 東京 - connpass

また、お時間を少し頂戴し、タイトルのお話しをさせて頂きました。

2.事例スペシャ

当初から、主催の田淵さん(@ytabuchi)に登壇のお誘いを頂いていたのですが、仕事柄、「お話しできるような ”事例” が無い」ということでお断りさせて頂きました。

しかし、いつまでたってもセッション枠は消えず、タイトル空欄の私の行が残っているので・・・いよいよ焦って事例を作ることにしましたw

そういう意味で、火がついたのは、約2週間前です・・・

丁度シルバーウィークで、まとまった時間が取れるので、「Xamarin.Formsを使用して短期間でどれぐらい作れるか」というコンセプトで作業してみました。

3.Facebookアプリ

時間も制限があるので、今回は、以前作成したことのあるWebアプリをネイティブアプリにリニューアルすることにしました。
結果的には、FacebookAPIの仕様が当時から比べて結構変わっており、内容はかなり違ったものになってしまいました。

「仕様どおり」ではなく、「時間どおり」です^^)

※V2.1以降、FQLは非推奨 scopeについてもだいぶ変わってました。

4.結論

内容については、スライドを見て頂きたいのですが・・・ここでは、結論だけ。

あくまで、個人的な感想ですが・・・

  • Xamarin.Formsを使用すると、短期間で結構動くものが作れる。(しかも、iOS,Android,WindowPhone対応)
  • 細かいところに、気が付きすぎると、作業量が大きく膨らむ(予感がする。)

という事で、

「Xamarin.Formsは、頑張りすぎないのが吉」

って事で。

※ぜんぜん、disっている訳ではありません。

【 Xamarin 記事一覧 】

Xamarin.FormsでAzureモバイルサービスによるToDoアプリを作成するには?

【 Xamarin 記事一覧 】

BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。

Xamarin.FormsでAzureモバイルサービスによるToDoアプリを作成するには? - Build Insider

使用したコードは、下記にあります。github.com


【 Xamarin 記事一覧 】

Xamarin.Formsでプラットフォームごとの微調整を行うには?

【 Xamarin 記事一覧 】

BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。

Xamarin.Formsでプラットフォームごとの微調整を行うには? - Build Insider

使用したコードは、下記にあります。


github.com


【 Xamarin 記事一覧 】

Xamarin.Forms PCLでのファイルIO ( PCL Storageを使用する )

【 Xamarin 記事一覧 】

1. PCL Storage

Xamarin.FormsのPCLでファイルのIOを書こうとしたとき、System.IOに File や Directory が無くて固まった経験はないでしょうか?
もともと、ファイルの扱いはプラットフォーム固有なので、当然といえば当然なのですが・・・

しかし、Xamarin.iOSやXamarni.Androidでは、ちゃんと、System.IO.FileやSystem.IO.Directoryが使えるので、それをラッパーすればいいだけです。
フォルダのパスがプラットフォーム固有になるので、そこだけ気を付ければいいって事でしょうか。


と言いながら、いろいろ検索していると、コンポーネントストアに「PCL Storage」なるものを見つけました。
f:id:furuya02:20150506131241p:plain:w350:left
f:id:furuya02:20150506131254p:plain:w350:left


2. インストール

同プログラムは、CodePlexGithubでコードも公開されています。
また、NuGetでのインストールも可能です。

f:id:furuya02:20150506131307p:plain:w350
コンソールでも可能ですが、今回は、「NnuGetパッケージの管理」から行いました。
※実は、ComponentsというフォルダがiOSAndroidのプロジェクトにしかなく、PCLプロジェクトが無かったので、右クリックでの導入要領が分かりませんでした・・・

f:id:furuya02:20150506131324p:plain
全プロジェクトにインストールされます。
f:id:furuya02:20150506131331p:plain

3. 動作確認

次のようなサンプルを書いて、動作確認してみました。
EntryコントロールとButtonコントロールを配置し、ボタンを押すと、Entryコントロールの内容をファイルに書き込むものです。

//App.cs
using PCLStorage;
using Xamarin.Forms;
namespace PCLStorageSample {
    public class App : Application {
        public App() {
            MainPage = new MyPage();
        }
    }

    internal class MyPage : ContentPage {
        public MyPage() {
            var entry = new Entry();
            var button = new Button() {
                Text = "OK",
                Command = new Command(async () => {
                    // ルートフォルダの取得
                    var folder = FileSystem.Current.LocalStorage;

                    // フォルダの作成
                    var subFolder = await folder.CreateFolderAsync("SubFolder", CreationCollisionOption.OpenIfExists);

                    // フォルダの作成
                    var file = await subFolder.CreateFileAsync("Text.txt", CreationCollisionOption.ReplaceExisting);

                    // Entryコントロール尾内容をの書き込む
                    await file.WriteAllTextAsync(entry.Text);
                    
                    // ファイルの内容を読み込む
                    var str = await file.ReadAllTextAsync();
                    str = "";
                })
            };
            Content = new StackLayout{
                Padding = new Thickness(0,Device.OnPlatform(20,0,0),0,0),
                Children = {
                    entry,button
                }
            };
        }
    }
}

(1) iOS

上記のコードを実行すると、下記のような画面になります。
f:id:furuya02:20150506131342p:plain:w200
Entryコントロールに「test-message」と入力して、OKボタンを押し、ファイルのIOを行ったところでブレークポイントで止めて、内容を確認してみました。
f:id:furuya02:20150506131351p:plain
ファイルから読み込んだ内容が、書き込んだものと同じになっているのが、確認できます。
また、この時の、各オブジェクトの状況は次のようになっています。
f:id:furuya02:20150506131356p:plain
PCLStorage.IFolder folder のパスは、「/User/username/Library/developer/CoreSimulator/Data/Application/A3297...C370/Document/..Library」となっており、そのサブフォルダ「SubFolder」とその中の
ファイル「Text.txt」が見て取れます。

※これは、シュミレータでの動作ですが、実機では「/var/mobile/Containers/Data/Application/53B38A17-AC94-49C9-A3E7-21A2B08F5706/Documents/../Library」のようになっています。

(2) Android

Androidでは、次のようになります。
f:id:furuya02:20150506131402p:plain:w200
Entryコントロールに「test-android」と入力して、iOSと同じ要領で確認してみました。
f:id:furuya02:20150506131410p:plain

今度は、PCLStorage.IFolder folder のパスが「/data/data/PCLStorageSample.Droid/files」となっているのが確認できます。

(3) WindowsPhone

最後に、WindowsPhoneです。
f:id:furuya02:20150506131435p:plain:w200
こちらの、PCLStorage.IFolder folder は、「/data/data/PCLStorageSample.Droid/files」です。
f:id:furuya02:20150506131420p:plain

4. ルートフォルダ

念のため、各プラットフォームごとのルートフォルダの取得について、Githubからコードを確認しておきます。
https://github.com/dsplaisted/PCLStorage/blob/master/src/PCLStorage.FileSystem.Desktop/DesktopFileSystem.cs

public IFolder LocalStorage{
            get{
                //  SpecialFolder.LocalApplicationData is not app-specific, so use the Windows Forms API to get the app data path
                //var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
#if ANDROID
                var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
#elif IOS
                var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                var localAppData = Path.Combine(documents, "..", "Library");
#else
                var localAppData = System.Windows.Forms.Application.LocalUserAppDataPath;
#endif
                return new FileSystemFolder(localAppData);
            }
        }

コードを確認すると、AndroidiOS、WindowsPhoneについては、ルートフォルダの取得以外やパスの扱いいがは、ほとんど共通のようです。
しかし、何と言っても、パッケージインストールだけで簡単に使えてしまう魅力は捨てがたいです。




【 Xamarin 記事一覧 】

Xamarin.Forms Mobile AppでToDoアプリを作成する(その1)

【 Xamarin 記事一覧 】

1. モバイルサービスからMobile Appsへ

f:id:furuya02:20150425084643p:plain:w200:leftf:id:furuya02:20150425084642p:plain:w200:leftMicrosoft Azureの新ポータル(2015年4月28日時点でPREVIEW版)では、従来の「モバイルサービス」が「Mobile Apps」となっています。

今回は、ちょっと気が早い?かも知れませんが・・・
新ポータルの綺麗な画面に誘われて、Xamarin.Formsで使ってみました。

現(PREVIEW)時点で・・・
・「モバイルサービス」で作成したものは、Mobile Appsから見えません・・・
・「モバイルサービス」では、サーバ側の処理をnodejsと.NETの2種類から選択できましたが、「Mobile Apps」では、.NETのみです。
・一応、モバイルサービスも残るみたいです。
「Mobile Services continue to be available as a standalone service and remain fully supported.」
http://azure.microsoft.com/en-us/documentation/articles/app-service-changes-existing-services/


2. Mobile Appの作成

以下、Mobile Appを作成する手順です。
(1)新ポータルでは、すべてのサービスは左下の(+新規)から行います。
f:id:furuya02:20150425084702p:plain:w450
(2)展開したウインドウから「Web + Mobile」>「Mobile App」と選択し「Name」の所にサービス名(ここでは「AzureToDoSample」とした)を入力します。
f:id:furuya02:20150425084716p:plain:w450
(3)続いてデータベースの設定を行います。
「PACKAGE SETTING」>「USERDATABASE」>「新しいデータベースの作成」を選択します。
ここで、「新しいデータベースの作成」ではなく「既存のデータベースの使用」を選択した場合、この先のDB関連の指定は必要なくなります。
f:id:furuya02:20150425084752p:plain:w450
(4)「名前」の所にデータベースの名前を入力し、「サーバー名」「サーバ管理者ログイン」「パスワード」を入力して「OK」「OK」で閉じていきます。
f:id:furuya02:20150425084802p:plain:w450
(5)設定が終わったら「作成」ボタンを押します。
f:id:furuya02:20150425084814p:plain:w450
(6)作成中の表示が出ますので、しばらく待ちます。(1分ぐらい?)
f:id:furuya02:20150425084822p:plain:w450
(7)出来上がると、作成したサービスを設定する画面になります。
ここで、「Add Client」を選択します。
f:id:furuya02:20150425084831p:plain:w450
(8)「Xamarin.Android」を選択し、「CREATE A NEW APP」で「DOWNLOAD」を押してひな形となるテンプレートをダウンロードします。
実は、Xamarin.Formsでプログラムを書く場合、テンプレートから使用するのは、サーバ側のMVCプロジェクトだけなので、「Windows(C#)」でも「Xamarin.iOS」でも、何をダウンロードしても関係ありませんw
f:id:furuya02:20150425084842p:plain:w450
(9)ダウンロードした、テンプレートは次のようになっています。
f:id:furuya02:20150425084853p:plain

3. Xamarin.Formsプロジェクトの作成

(1)VisualStudioで開いてみると、Xamarin.Android用のプロジェクト「AzureToDoSample」と、サーバ側のMVCプロジェクト「AzureToDoSampleService」の2つが含まれているのを確認することができます。
f:id:furuya02:20150425084908p:plain
(2)Xamarin.Android用のプロジェクトは要らないので、ばっさり削除します。
※実はXamarin.Android用のコードは、Xamarin.Formsでも利用可能なものが多いので、適当にフォルダ名を変えて置いておくと、色々吉です。
f:id:furuya02:20150425084913p:plain:w450
(3)続いて、このソリューションにXamarinFormsのプロジェクトを追加します。
ここでは、プロジェクト名を「AzureToDoSample」としました。
f:id:furuya02:20150425084924p:plain:w450
(4)プロジェクトの追加後、ソリューションに5つのプロジェクトが確認できます。(追加されたのは4つ)
AzureToDoSample(移植可能) : クラアントPCLプロジェクト
AzureToDoSample.Droid :Androidクラアントプロジェクト
AzureToDoSample.iOS :iOSクラアントプロジェクト
AzureToDoSample.WinPhone :WindowsPhone クラアントプロジェクト
AzureToDoSampleService :サーバ用MVCプロジェクト(これは、削除しないで残ったテンプレートのプロジェクト)
f:id:furuya02:20150425084932p:plain:w450

4. サーバ側MVCプロジェクトのコンパイルと登録

当初、サーバ側の構築を行います。
(1)コンパイルの後、ソリューションエクスプローラMVCプロジェクト「AzureToDoSampleService」を右クリックして「登録」を選択します。
f:id:furuya02:20150425085002p:plain:w450
(2)「Microsoft Azure Websites」を選択します。
f:id:furuya02:20150425085012p:plain:w450
(3)「既存のWebサイト」で、作成したサービスが選択できます。
サーバから検索するためには、Microsoftアカウントにサイインしている必要があります。
f:id:furuya02:20150425085020p:plain:w450
(4)「サーバー」「ユーザ名」「パスワード」など、全てがインポートされるので「発行」ボタンを押すだけです。
f:id:furuya02:20150425085027p:plain:w450


すいません、まだXamarin.Forms書いてませんが・・・
つづく・・・

5. 参考にさせて頂いたページ

Azure Mobile Apps と Mobile Services の違いについて - nnasakiのブログ
Azure App Service | ブチザッキ



【 Xamarin 記事一覧 】

Xamarin.Forms ローカルデータベース(SQLite)の利用

【 Xamarin 記事一覧 】

1. ローカルデータベース

Xamarin.Formsでは、SQLite.Netを使用することで、簡単にローカルのSQLiteデータベースが利用可能です。
今回は、このSQLite.NetをPCLで実装する方法について紹介します。

PCLで、このSQLite.Netを利用する場合、プラットフォーム固有の部分(最初の初期化)だけを、DependencyServiceで記述し、それ以外をPCLで実装する形になります。

※プラットフォーム固有の部分とは、主にデータファイルの場所を取得するあたりです。
※Xamarinのページでは、「Portable」と「Sharde」の2種類の実装方法が紹介されています。


2. NuGetパッケージのインストール

SQLite.NetはNuGetパッケージで簡単に利用が可能になっています。
「NuGet パッケージの管理」で 'SQLite.Net.PCL' で検索できます。
f:id:furuya02:20150427153746p:plain
f:id:furuya02:20150427153809p:plain

最初、うまく「SQLite.Net.Platform.Xamarin(Pratform)」が入らないな・・・と試行錯誤していたら、急にうまく行くようになって、よく見たら最終発行日が昨日になっていたw(途中で更新されたのですね)

f:id:furuya02:20150427153841p:plain
f:id:furuya02:20150427153853p:plain
f:id:furuya02:20150427153902p:plain
f:id:furuya02:20150427153909p:plain

※WindowsPhoneの場合は、'SQLite.Net-PCL'の他に依存関係から'sqlite-net-wp8 3.8.5'もインストールされます。

3. インターフェースの定義

プラットフォーム固有の部分をDependencyServiceで記述するためのインターフェースを定義します。
PCLプロジェクトにISQLite.csを追加しました。
これは、SQLiteを初期化して、SQLiteConnectionのインスタンスを取得するものです。

//ISQLite.cs
using SQLite.Net;
namespace SQLiteSample{
    public interface ISQLite{
        SQLiteConnection GetConnection();
    }
}

なお、このインターフェースの利用例は、次のようになります。

db = DependencyService.Get<ISQLite> ().GetConnection ();
db.CreateTable<T>();

4. 各プラットフォームの実装

DependencyService用に定義されたインターフェースを各プラットフォームで実装します。

iOS

iOSプロジェクトにSQLite_iOS.csを追加しました。

using System;
using System.IO;
using SQLite.Net;
using SQLite.Net.Platform.XamarinIOS;
using SQLiteSample.iOS;
using Xamarin.Forms;
[assembly: Dependency(typeof(SQLite_iOS))]
namespace SQLiteSample.iOS{
    public class SQLite_iOS : ISQLite{
        public SQLiteConnection GetConnection(){
            const string sqliteFilename = "TodoSQLite.db3"; //データベース名
            var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); //Documentsフ

ォルダ
            var libraryPath = Path.Combine(documentsPath, "..", "Library"); // ライブラリフォルダ
            var path = Path.Combine(libraryPath, sqliteFilename);//DBファイルのパス
            var plat = new SQLitePlatformIOS();
            var conn = new SQLiteConnection(plat, path);
            return conn;
        }
    }
}

Android

AndroidプロジェクトにSQLite_Droid.csを追加しました。

using System.IO;
using SQLiteSample.Droid;
using Xamarin.Forms;
[assembly: Dependency(typeof(SQLite_Android))]
namespace SQLiteSample.Droid{
    public class SQLite_Android : ISQLite{
        public SQLite.Net.SQLiteConnection GetConnection() {
            const string sqliteFilename = "TodoSQLite.db3"; //データベース名
            var documentsPath = System.Environment.GetFolderPath

(System.Environment.SpecialFolder.Personal);//Documentsフォルダ
            var path = Path.Combine(documentsPath, sqliteFilename);//DBファイルのパス
            var plat = new SQLite.Net.Platform.XamarinAndroid.SQLitePlatformAndroid();
            var conn = new SQLite.Net.SQLiteConnection(plat, path);
            return conn;
        }
    }
}

WindowsPhone

WindowsPhoneプロジェクトにSQLite_WinPhone.csを追加しました。

using System.IO;
using Windows.Storage;
using SQLite.Net;
using SQLite.Net.Platform.WindowsPhone8;
using SQLiteSample.WinPhone;
using Xamarin.Forms;
[assembly: Dependency(typeof(SQLite_WinPhone))]
namespace SQLiteSample.WinPhone {
    public class SQLite_WinPhone : ISQLite{
        public SQLiteConnection GetConnection(){
            const string sqliteFilename = "TodoSQLite.db3";//データベース名
            var path = Path.Combine(ApplicationData.Current.LocalFolder.Path, sqliteFilename);//DBファイルの

パス
            var plat = new SQLitePlatformWP8();
            var conn = new SQLiteConnection(plat, path);
            return conn;
        }
    }
}

5. データクラスとデータベースへのアクセスクラスの作成

データクラス

本サンプルのデータクラスとして、PCLプロジェクトにTodoItem.csを追加しました。

キーとなるIDのほかに、文字列を保存するText、作成日時のCreatedAt、そして、削除状態を表現するDeleteです。

using System;
using SQLite.Net.Attributes;

namespace SQLiteSample{
    public class TodoItem{
        [PrimaryKey, AutoIncrement]
        public int ID { get; set; }
        public string Text { get; set; }
        public DateTime CreatedAt { get; set; }//作成日時
        public bool Delete { get; set; }//削除フラグ(trueの時、表示しない)
    }
}

データベースへのアクセスクラス

データベースへのアクセスのための、PCLにTodoRepository.csを定義します。
実装したのは、一覧取得と、データの追加・更新だけです。

using System.Collections.Generic;
using SQLite.Net;
using Xamarin.Forms;

namespace SQLiteSample{
    class TodoRepository{
        static readonly object Locker = new object();
        readonly SQLiteConnection _db;

        public TodoRepository(){
			_db = DependencyService.Get<ISQLite> ().GetConnection ();//データベース接続
			_db.CreateTable<TodoItem>();//テーブル作成
		}
        //一覧
        public IEnumerable<TodoItem> GetItems(){
            lock (Locker){
                
                //Delete==falseの一覧を取得する(削除フラグが立っているものは対象外)
                return _db.Table<TodoItem>().Where(m => m.Delete == false);
            }
        }
        //更新・追加
        public int SaveItem(TodoItem item){
            lock (Locker){
                if (item.ID != 0){//IDが0で無い場合は、更新
                    _db.Update(item);
                    return item.ID;
                }
                return _db.Insert(item);//追加
            }
        }
    }
}

6. 画面実装とデータベースの利用

以上で、段取りは整ったので、画面とDBアクセスを実装します。
Addボタンで追加、リストのタップで削除になってます。

using System;
using Xamarin.Forms;
namespace SQLiteSample{
    public class App : Application{
        public App(){
            MainPage = new MyPage();
        }
        protected override void OnStart(){}
        protected override void OnSleep(){}
        protected override void OnResume(){}
    }
    class MyPage : ContentPage{

        readonly TodoRepository _db = new TodoRepository();

        public MyPage(){
            
            //データ表示用リストボックス
            var listView = new ListView{
                ItemsSource = _db.GetItems(),
                ItemTemplate = new DataTemplate(typeof(TextCell))
            };
            listView.ItemTemplate.SetBinding(TextCell.TextProperty, "Text");
            listView.ItemTemplate.SetBinding(TextCell.DetailProperty, new Binding("CreatedAt", stringFormat: 

"{0:yyy/MM/dd hh:mm}"));
            listView.ItemTapped += async (s, a) =>{//リストがタップされた時の処理
                var item = (TodoItem)a.Item;
                if (await DisplayAlert("削除してい宜しいですか", item.Text, "OK", "キャンセル")){
                    item.Delete = true;//削除フラグを有効にして
                    _db.SaveItem(item);//DB更新
                    listView.ItemsSource = _db.GetItems();//リスト更新
                }
            };
            //文字列入力
            var entry = new Entry{
                HorizontalOptions = LayoutOptions.FillAndExpand
            };
            //追加ボタン
            var buttonAdd = new Button{
                WidthRequest = 60,
                TextColor = Color.White,
                Text = "Add"
            };
            buttonAdd.Clicked += (s, a) =>{//追加ボタンが押された時の処理
                if (!String.IsNullOrEmpty(entry.Text)){//Entryに文字列が入力されている場合に処理する
                    var item = new TodoItem { Text = entry.Text, CreatedAt = DateTime.Now, Delete = false };
                    _db.SaveItem(item);
                    listView.ItemsSource = _db.GetItems();//リスト更新
                    entry.Text = "";
                }
            };

            Content = new StackLayout{
                Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0),
                Children = {
                    new StackLayout {
                        BackgroundColor = Color.Navy,//入力部の背景色はネイビー
                        Padding = 5,
                        Orientation = StackOrientation.Horizontal,
                        Children = {entry, buttonAdd}//Entryコントロールとボタンコントロールを横に並べる
                    },
                    listView//その下にリストボックスを置く
                }
            };
        }

    }
}

実行画面は次のとおりです。

f:id:furuya02:20150427154000p:plain:w200 f:id:furuya02:20150427154011p:plain:w200
f:id:furuya02:20150427154021p:plain:w200 f:id:furuya02:20150427154034p:plain:w200
f:id:furuya02:20150427154045p:plain:w200 f:id:furuya02:20150427154054p:plain:w200

7. WindowsPhoneの場合

以上で、Xamarin.FormsからSQLite.Netを利用する手順は終了なのですが・・・
WindowsPhoneは、デフォルトでSQLite自体がインストールされていないので、初めてのSQLiteを使用する場合は、このライブラリ自体をインストールする必要があります。そして、アプリ作成時は、このライブラリを参照に追加することになります。

以下、手順を追って説明します。

(1)ダウンロード

http://www.sqlite.org/download.html#wp8から、WindowsPhone8用のものをダウンロードします。
f:id:furuya02:20150427154449p:plain

(2)インストール

ダウンロードした、vsixファイルを実行して、インストールします。
f:id:furuya02:20150427154440p:plain

(3)VisualStudio再起動

参照の一覧に表示するためにVisualStudioの再起動が必要です。

(4)参照追加

WindowsPhoneプロジェクトの参照の追加で「SQLite for Windows Phone」を追加します。
f:id:furuya02:20150427154458p:plain
f:id:furuya02:20150427154510p:plain

8. 参考にさせて頂いたページ

Xamarin.AndroidでSQLiteを使う - かずきのBlog@hatena
Working with a Local Database - Xamarin


↓サンプルはここに置きました。github.com



【 Xamarin 記事一覧 】