見出し画像

アプリ起動時の表示情報にSwiftDataを使う

割引あり

SwiftUI と iOS 17 から利用可能な SwiftData の組み合わせは強力です。
アプリダウンロード直後の初期データにSwiftDataを使う方法を二つ試しました。
一つは永続化データをアプリに組み込みそのパスを指定する方法、もう一つはアプリ起動時に必要な初期データを追加する方法です。

⚠️注意:
実行には Xcode 15 が必要です。
Xcode 15 は iOS 17 と同時に正式リリースされました。

執筆時点の環境:
Xcode 15
macOS 13.6
(※ Xcode 15 は Beta 5 以降それぞれいくつか変更がありました。正式版はRC版と同じです。)

この記事は『SwiftData を iOS アプリでためす』マガジンで読むことができます。

『SwiftData を iOS アプリでためす』マガジンで読める記事:
SwiftDataをシンプルにためす
CSVファイルデータを読みSwiftDataで使う
アプリ起動時の表示情報にSwiftDataを使う
(この記事)
マクロ と オブザベーション
SwiftDataの検索・絞り込みと並べ替え
--- そのほかも準備中 ---



  • 画像クリックで拡大表示できます

  • 画像を拡大表示中は画像の左右をクリックで画像だけを順に表示できます

  • ソースコード部分は左右にスクロールできます

  • リンクしているドキュメントは英文が多いですが、翻訳機能を活用してください



Xcode 15

SwiftData は iOS 17以降(iPadOS 17以降、macOS 14以降)が必要です。
開発には Xcode 15 が必要です。
Xcode 15 は iOS 17 と同時に、日本では2023年9月19日にリリースされました。
正式版 Xcode 15 は RC 版と同じでした。

Xcode 15 の Documentation ウインドウで SwiftUI ドキュメントの「Beta」表示は取れましたがサンプルは二つしかありません。

Xcodeのドキュメントウインドウ

SwiftDataをシンプルにためす」に書きましたがこのほかに二つサンプルが追加されています。
しかし「Maintaining a local copy of server data」からダウンロードできる「SwiftDataLocalDataCacheSample」は正式版 Xcode 15 ではエラーが出てビルドできません。
Xcode 15 Beta8 ではエラーなく実行できます。(Xcode 15 Beta8 は DeveloperサイトのDownloads > More からダウンロードできます。【デベロッパー登録必要】)
このエラー発生に関しては、サンプルの修正または Xcode 15 のアップデートで対応されると思われます。


起動時に表示する情報もSwiftDataにする

SwiftDataアプリをインストール直後に起動するとデータは空の状態です。
アプリの目的により初期データが必要な場合があります。

たとえば路線図(駅名データ)や時刻表、製品カタログなどでオフライン状態でも利用可能にしなければならない場合などです。
このような場合も今後は SwiftUI と SwiftData を組み合わせたアプリを最初に検討することになるでしょう。

電子書籍でも作者のデータと作品タイトルのデータが必要です。

電子書籍アプリでは作者や作品タイトルデータが多数必要

現在弊社のアプリ「neo文庫」で作者は1100件以上、作品タイトルは17000件以上あります。
このように件数が多い場合も SwiftData で実用的に扱えるのかが知りたくて、いろいろ調査しています。

今回はアプリの初期データとして SwiftData を使う SwiftUIアプリを試してみます。

アプリをインストールした直後に表示したい情報を SwiftData で扱う場合、永続化したデータをあらかじめ用意しアプリ内に組み込む方法と、実行時に用意する方法が考えられます。

永続化したデータをあらかじめ用意する方法はデータ件数が多い場合にも確実に対応できます。
実行時にデータを用意する方法は、データを追加したりカスタマイズする場合に適しています。
まずは小規模なデータで二つの方法をそれぞれ確認するサンプルアプリを作りましょう。


永続化データの扱い

SwiftData アプリの永続化データのデフォルト保存先は(これまで確認した範囲では)…Library/Application Support/ でした。
…Library/Application Support/ はどのアプリもそれぞれ持つ、アプリが自由に利用可能なディレクトリで、アプリをバックアップした場合にこのディレクトリ内のファイルもバックアップの対象にもなっています。
…Library/Application Support/ ディレクトリはアプリインストール直後は何もファイルが存在しない空っぽの状態です。
デフォルト設定の SwiftUI アプリはインストール直後に…Library/Application Support/ ディレクトリ内に件数ゼロの永続化ファイルを作成し、それを利用開始します。

一方、アプリに埋め込まれたリソースデータはアプリのバンドル内にあり、アプリをインストールした時にアプリの一部としてそのままデバイスにインストールされます。
初期データとして SwiftData で利用するにはバンドル内の永続化情報のファイルを明示しアクセスが必要です。

永続化データをバンドル内からデフォルト保存先にコピーする方法なども考えられますが、ここでは直接アクセスします。


保存した永続化データを使う

一つ目の方法は別アプリで永続化したデータをアプリに組み込み使います。
永続化したデータの扱いは「CSVファイルデータを読みSwiftDataで使う」を参照してください。
前回CSVファイルを読み込みSwiftDataで永続化したデータファイルを今回のアプリで使いましょう。


永続化データをアプリに組み込む

永続化データをアプリに組み込む方法とアクセスする方法は前回のCSVファイルと基本的には同じです。

永続化データファイルをアプリのプロジェクトに組み込むことで、ビルド時にアプリにコピーされます。

まずSwiftDataのテンプレートでプロジェクトを保存しましょう。
SwiftDataのテンプレートについては「SwiftDataをシンプルにためす」を参照してください。

プロジェクト名は「SwiftDataInitial01」としました。
前回の「SDCSV02」をコピーして修正してもかまいませんが、テンプレートをもとに修正すると、エラー表示に対応するスキルなども獲得できるのでおすすめです。


モデル

モデルはもちろん組み込んだ永続化データに完全対応していなければなりません
このサンプルでは「CSVファイルデータを読みSwiftDataで使う」の第2のサンプルと同じです。

// モデル
import Foundation
import SwiftData

@Model
final class DeviceName {
    @Attribute(.unique) var device: String
    var screenSize: String
    var weight: Int
    var height: Double
    var cpu: String

    init(device: String, screenSize: String,
         weight: Int, height: Double, cpu: String) {
        self.device = device
        self.screenSize = screenSize
        self.weight = weight
        self.height = height
        self.cpu = cpu
    }
}


.store データをプロジェクトに追加

前回のサンプルでCSVファイルから作成したデータの三つのファイル 
default.store
default.store-shm
default.store-wal
を今回のプロジェクトに組み込みます。

どのプロジェクトで作った .store ファイルも同じ『default.store』では見分けるのが困難なので、ファイル名をデータを作成したプロジェクト名の sdcsv02 に変更しました。
組み込んだ後に Xcode の Project navigator でファイルの名称を変更できます。
もちろん変更してから組み込むこともできます。

三つの永続化ファイルをプロジェクトに追加


SwiftDataをデフォルト設定以外で使う

永続化データを指定するにはどうすべきでしょうか?
SwiftDataテンプレートを修正することで可能です。

ところで、Xcode 15 Beta 6 までは SwiftData のテンプレートの@mainコードは非常にシンプルでした。

// Xcode 15 Beta 6 のコード
import SwiftUI
import SwiftData

@main
struct SDSample01App: App {

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Item.self)
    }
}

モデルコンテナも modelType だけを指定してデフォルトを自動で生成させています。

SwiftData は少ないコードで利用可能なことを強調できますが、今回のように .store ファイルを指定しようとすると行き詰まってしまいます。
Beta 7 から SwiftData のテンプレートは ModelConfiguration など詳細を設定可能な(より実用的な)コードを最初から示すようになったと考えられます。

Xcode 15 Beta 7 から SwiftData のテンプレートの@mainコードはModelContainer インスタンスを引数で渡すように変更されました。

// リリースされた Xcode 15 のコード
import SwiftUI
import SwiftData

@main
struct tmpRCApp: App {
    var sharedModelContainer: ModelContainer = {
        let schema = Schema([
            Item.self,
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

        do {
            return try ModelContainer(for: schema, configurations: [modelConfiguration])
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(sharedModelContainer)
    }
}

ModelContainer のイニシャライザで ModelConfiguration インスタンンスも使っています。
コードの行数は大幅に増えましたが機能はまったく同じです。
このまま使うには冗長ですが、カスタマイズが前提のテンプレートとしては応用しやすいコードになっています。


永続化データurlを指定してSwiftDataで使う

永続化データのURL指定は ModelConfiguration のイニシャライザ init(_:schema:url:allowsSave:cloudKitDatabase:) で可能です。

テンプレートで使っているイニシャライザは 

init(
    _ name: String? = nil,
    schema: Schema? = nil,
    isStoredInMemoryOnly: Bool = false,
    allowsSave: Bool = true,
    groupContainer: ModelConfiguration.GroupContainer = .automatic,
    cloudKitDatabase: ModelConfiguration.CloudKitDatabase = .automatic
)

です。
テンプレートでは schema: を使っています。
(isStoredInMemoryOnly: はデフォルトと同じですが省略していません)

let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

URLを設定する場合は別のイニシャライザは

init(
    _ name: String? = nil,
    schema: Schema? = nil,
    url: URL,
    allowsSave: Bool = true,
    cloudKitDatabase: ModelConfiguration.CloudKitDatabase = .automatic
)

です。
利用目的が違うので引数で設定できるオプションも違っています。


.store ファイルのURLを指定する

ここから先は

9,758字 / 5画像 / 2ファイル
この記事のみ ¥ 300〜

今後も記事を増やすつもりです。 サポートしていただけると大変はげみになります。