読者です 読者をやめる 読者になる 読者になる

Xamarin 日本語情報

Xamarin(ザマリン) の代理店だったエクセルソフト田淵のブログです。主に Xamarin に関するエントリーをアップしていきます。(なるべく正しい有益な情報を掲載していきたいと考えていますが、このブログのエントリーは所属組織の公式見解ではありませんのでご注意ください)

Xamarin.Forms でアプリの設定情報を保存するには (Json.NET 使用)

Xamarin Xamarin.Forms

こんにちは。エクセルソフトの田淵です。

前回は Properties Dictionary を使用する方法 をご紹介しました。今回は Json.NET を使用する方法です。

サンプル

GitHub

画面写真

f:id:ytabuchi:20150706191922p:plain:w150 f:id:ytabuchi:20150706191927p:plain:w150

f:id:ytabuchi:20150706191911p:plain:w150 f:id:ytabuchi:20150706191919p:plain:w150

f:id:ytabuchi:20150706191932p:plain:w150 f:id:ytabuchi:20150706191934p:plain:w150

2015/7/6 時点の最新版、Xamarin.Forms 1.4.3.6374 で確認しています。

まず、Xamarin でファイルを保存するには、Xamarin.iOS/Androidでアプリの設定情報を保存するには? - Build Insider の 3 にある、System.IO.IsolatedStorage を使うと簡単みたいです。

ただし、Xamarin.Forms の PCL では使用できないため、Dependency Service を使用する必要があります。Dependency Service については Xamarin のドキュメント をご参照ください。

なお、Xamarin の Working with Files ドキュメント では、Windows Phone でしか IsolatedStorage と Windows.Storage API がサポートされていないので、System.IO の APIDependency Service を介して使うよ!とありますが、Build Insider にも書いてありますし、当たり前のように Android/iOS 共に IsolatedStorage 使えますね…

今回のサンプルは Xamarin のサンプルをちょろっと弄っただけですので、iOS/AndroidSystem.IOWindows Phone が Windows.Storage を使用しています。すみません。。

やり方

必要に応じて Model を用意します。今回は ViewModel のみにします。

Dependency Service 用の Interface を用意します。今回はメソッドを保存、復元、削除の 3つを用意しました。

public interface ISaveAndLoad
{
    void SaveData(string filename, string text);
    string LoadData(string filename);
    bool ClearData(string filename);
}

View

保存

vm インスタンスの値 をそのまま JsonConvert.SerializeObject します。

var json = JsonConvert.SerializeObject(vm);
DependencyService.Get<ISaveAndLoad>().SaveData("temp.json", json);
復元

値を引っ張ってきて DeserializeObject し、BindingContext に再設定します。

var data = DependencyService.Get<ISaveAndLoad>().LoadData("temp.json");
this.vm = JsonConvert.DeserializeObject<AllPagesViewModel>(data);
this.BindingContext = vm;
削除

Dependency Service で bool 返すようにしました。

(DependencyService.Get<ISaveAndLoad>().ClearData("temp.json")) ? true : false; 

こんな感じですかね。

OS 毎の実装

iOS, Android 共に同じです。
public void SaveData(string filename, string text)
{
    var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    var filePath = Path.Combine(documentsPath, filename);
    // File.WriteAllText ですべて上書きします。AppendAllText だと追加するようです。
    System.IO.File.WriteAllText(filePath, text);
}
public string LoadData(string filename)
{
    var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    var filePath = System.IO.Path.Combine(documentsPath, filename);
    // ファイルが無ければ null を返します。
    if (System.IO.File.Exists(filePath))
    {
        return System.IO.File.ReadAllText(filePath);
    }
    return null;
}
public bool ClearData(string filename)
{
    var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    var filePath = System.IO.Path.Combine(documentsPath, filename);
    System.IO.File.Delete(filePath);
    // ファイルが削除出来ていれば true, そうでなければ false を返します。
    return (System.IO.File.Exists(filePath)) ? false : true;
}

因みに以下のように IsolatedStorage を使用した場合は System.Environment.SpecialFolder.Personal とはファイルの PATH が違うみたいです。

using System.IO.IsolatedStorage;

var file = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream strm = file.CreateFile(filename))
{
    using (StreamWriter writer = new StreamWriter(strm))
    {
        writer.Write(text);
    }
}
Windows Phone
public string LoadData(string filename)
{
    var task = LoadDataAsync(filename);
    task.Wait(); // HACK: to keep Interface return types simple (sorry!)
    return task.Result;
}
async Task<string> LoadDataAsync(string filename)
{
    var local = Windows.Storage.ApplicationData.Current.LocalFolder;
    if (local != null)
    {
        var file = await local.GetItemAsync(filename);
        using (StreamReader streamReader = new StreamReader(file.Path))
        {
            var text = streamReader.ReadToEnd();
            return text;
        }
    }
    return "";
}
public async void SaveData(string filename, string text)
{
    var local = Windows.Storage.ApplicationData.Current.LocalFolder;
    var file = await local.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
    using (StreamWriter writer = new StreamWriter(await file.OpenStreamForWriteAsync()))
    {
        writer.Write(text);
    }
}

public bool ClearData(string filename)
{
    var task = ClearDataAsync(filename);
    task.Wait(); // HACK: to keep Interface return types simple (sorry!)
    return task.Result;
}

public async Task<bool> ClearDataAsync(string filename)
{
    var local = Windows.Storage.ApplicationData.Current.LocalFolder;
    await local.DeleteAsync();
    
    // ファイルが削除されているか?をGetFileAsyncがFileNotFoundExceptionを返すか?でチェックするのもダサいですよね。。
    try
    {
        var file = await local.GetFileAsync(filename);
    }
    catch (FileNotFoundException)
    {
        return true;
    }
    return false;
}

これで、無事 Json にデータを保存・復元できました。復元時に BindingContext に再設定するのがポイントだったようです。(@kurosawa0626さん、@ticktackmobile さん、ありがとうございました!) 何かの参考になれば幸いです。

Xamarin 気になった方は

是非 ダウンロード(直接) / ダウンロード(弊社経由) して触ってみてください。 学習用リソースJXUG リンクページ に参考資料を纏めてますので併せてどうぞ。

Xamarin の情報が欲しい方はこのブログも購読いただいたりすると嬉しいです。

以上です。