shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.Formsでアプリケーションのデータ保存先はどこがいい?

結論としては共有プロジェクト側でSpecialFolder.LocalApplicationDataを使えばいい気がしてきた.

アプリケーションがデータを保存する先はどのディレクトリにしたらいいのだろうか?

各プラットフォームごとに異なるディレクトリを指定しなければならないのだろうか?

プラットフォームごとにコードを変えなければいけないのはいやだな,と思う.

それに対するスレッドが↓

stackoverflow.com

SpecialFolderに対応するディレクトリはプラットフォームごとに異なる

SpecialFolderの種類はこちらにすべてリストアップされている.

docs.microsoft.com

この中でモバイルアプリのデータ保存先としてはPersonal,MyDocuments,LocalApplicationDataが代表的である.

Android

SpecialFolder.PersonalSpecialFolder.MyDocumentsは,両方とも/data/data/@PACKAGE_NAME@/filesを指す.

SpecialFolder.LocalApplicationData/data/data/@PACKAGE_NAME@/files/.local/shareを指す.

これらのフォルダには他のアプリケーションからアクセスできないし,rootを取得しない限りユーザーもアプリ以外からアクセスできない.

ということはどれ使っても構わない,ということか.

iOS

SpecialFolder.PersonalSpecialFolder.LocalApplicationData,SpecialFolder.MyDocumentsはすべて/Documentsを指す.

iOSは以下のディレクトリ構造を持っている.

/Documents
/Library
/Library/Application Support
/Library/Caches
/tmp

/DocumentsiTunesから見える.iTunesの共有機能がOnの場合は,この中のデータがiTunes/iCloudにバックアップされる.

/LibraryiTunesから見えない./Library/Caches以外はiTunes/iCloudにバックアップされる.

ユーザーに直接見せる必要がないファイルやデータ,例えば(SQLiteなどの)Databaseファイルは,Libraryフォルダに置くことをおすすめする.もし必要なら暗号化して.

LibraryフォルダのパスはiOSでは以下のようにする.

Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "..", "Library");

これはAndroid側とは異なる指定になるため,プラットフォームごとに指定しわける必要がある.

プラットフォームごとにアプリ保存先のパスを変える方法

方法1

各プラットフォームのプロジェクト内でそれぞれパスを取得し,それをAppクラスのコンストラクタに渡す.

Android : MainActivity.cs

            var dbPath = Path.Combine(System.Environment.GetFolderPath
                (System.Environment.SpecialFolder.Personal), "productsDB.db");
            var productsRepository = new ProductsRepository(dbPath);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App(productsRepository));

iOS : AppDelegate.cs

            SQLitePCL.Batteries_V2.Init();//iOSはこれが必要らしい

            var dbPath = Path.Combine(Environment.GetFolderPath
                (Environment.SpecialFolder.MyDocuments), "..", "Library", "products.db");

            var productsRepository = new ProductsRepository(dbPath);

            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App(productsRepository));

iOS,AndroidそれぞれのプロジェクトにSqlite.Standardプロジェクトへの参照を追加する.

もちろんAppクラスに引数を受け取るコンストラクタを作る必要がある.

Shared : App.xaml.cs

        public App(IProductsRepository productsRepository)
        {
            InitializeComponent();

            MainPage = new MainPage();
        }

方法2

Device.RuntimePlatformで分ける?

App.xaml.csで.

            string dbPath = string.Empty;
            switch (Device.RuntimePlatform)
            {
                case Device.iOS:
                    dbPath = Path.Combine(Environment.GetFolderPath
                                (Environment.SpecialFolder.MyDocuments), "..", "Library", "products.db");
                    break;
                case Device.Android:
                    dbPath = Path.Combine(System.Environment.GetFolderPath
                (System.Environment.SpecialFolder.Personal), "productsDB.db");
\                    break;
                case Device.UWP:
                    //
                    break;
            }
            productsRepository = new ProductsRepository(dbPath);

これでいけるかどうかは確認していない.