0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

独自OSとSwiftを連結させて『花子の庭』のバックアップを実装した例

0
Last updated at Posted at 2026-03-05

SwiftUIで開発しているゲームアプリ「花子の庭」で、
ローカルデータのバックアップ機構を実装しました。

「花子の庭」は、庭に生き物が訪れたり、アイテムを配置したりしながら
自分だけの庭を育てていく小さな庭ゲームです。

アプリでは次のようなデータを保存しています。

・庭に訪れる生き物
・庭に配置されたアイテム
・コインなどのゲーム状態

これらのデータは基本的に端末内(ローカル)に保存されています。

しかし、端末の故障やアプリ削除などが起きた場合、
ゲームデータがすべて消えてしまう可能性があります。

ゲームを楽しんでくれているユーザーにとって、これは少しネックになる問題でした。

そこで今回、個人開発している 独自OS「OS Yamato」 のバックエンドを利用して、
ゲームデータをバックアップできる仕組みを実装しました。

この記事では、SwiftUIアプリのローカルデータを
どのようにバックアップできるようにしたのか、
その構成と実装方法をすこし紹介します。

使用した技術

今回のバックアップ機能では、次の技術を使用しています。

・SwiftUI
・AWS Amplify(iOS SDK)
・GraphQL API
・DynamoDB

SwiftUIアプリ側では AmplifyをAPI呼び出し用のSDKとして利用しています。
GraphQLのスキーマやバックエンド構成は、OS Yamato側で管理しています。

構成は次のようになっています。

SwiftUI App

Amplify API

GraphQL

DynamoDB

バックアップ対象のデータ

花子の庭では、ゲーム状態をまとめてバックアップしています。

主なデータは次の通りです。

・コイン
・インベントリ
・庭に配置されたアイテム
・レアアイテム
・図鑑データ

これらのデータを JSON形式にまとめて保存しています。

SwiftUIからバックアップを送信する

バックアップ処理では、現在のゲーム状態を取得し、
GraphQL APIを通じてバックエンドへ送信します。

import Foundation
import Amplify

enum BackupManager {

    static func saveBackup(appState: AppState) async {

        do {

            let user = try await Amplify.Auth.getCurrentUser()
            let sub = user.userId

            let itemsJson = try encode(appState.inventory)
            let gardenItemsJson = try encode(appState.gardenItems)
            let rareItemsJson = try encode(appState.rareItems)
            let encyclopediaJson = try encode(Array(appState.savedCreatureNames))

            let backup = HanacoGardenBackup(
                id: sub,
                coins: appState.hanacoin,
                goldCoins: appState.goldhanacoin,
                itemsJson: itemsJson,
                gardenItemsJson: gardenItemsJson,
                encyclopediaJson: encyclopediaJson,
                rareItemsJson: rareItemsJson
            )

            try await saveOrOverwrite(backup)

        } catch {
            print("Backup error:", error)
        }
    }
}

JSONエンコード

ゲームデータはそのまま送信するのではなく、
JSON文字列へ変換して送信しています。

static func encode<T: Encodable>(_ value: T) throws -> String {
    let data = try JSONEncoder().encode(value)
    return String(data: data, encoding: .utf8) ?? "{}"
}

この方法により、複雑なデータ構造でも
まとめてバックアップできるようになります。

UPDATE → CREATE 戦略

バックアップは次の順序で保存しています。
1. 既存データの UPDATE を試す
2. 失敗した場合 CREATE

この方法により、

・初回バックアップ
・既存バックアップの更新

の両方に対応できます。

static func saveOrOverwrite(_ backup: HanacoGardenBackup) async throws {

    do {
        try await update(backup)
        return

    } catch {
        try await create(backup)
    }
}

GraphQL mutation

バックアップはGraphQLのmutationを利用して保存します。

let request = GraphQLRequest<JSONValue>(
    document: """
    mutation CreateBackup($input: CreateHanacoGardenBackupInput!) {
      createHanacoGardenBackup(input: $input) { id }
    }
    """,
    variables: ["input": backup.toDictionary()],
    responseType: JSONValue.self
)

AmplifyのAPIを利用して送信します。

let res = try await Amplify.API.mutate(request: request)

まとめ

SwiftUIアプリのローカルデータは便利ですが、
端末トラブルなどで失われる可能性があります。

今回紹介した方法では、

・ゲームデータをJSONにまとめる
・AmplifyからGraphQL APIを呼び出す

というシンプルな構成で
クラウドバックアップを実装することができました。

SwiftUIアプリでも比較的少ないコードで
バックアップ機能を追加できるのは大きなメリットだと思います。

今後は、バックアップデータの復元などについても
整理していきたいと思います。

OS Yamato 倉岡

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?