1
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?

[SwiftUI入門] 3.タスクキルしても情報を保存するようにしよう!

Last updated at Posted at 2025-04-08

はじめに

この記事は、[SwiftUI] 2.カウンターアプリの機能を増やそう!の続きになります。まだ読んでいないという人はそちらを先に読むことをおすすめしましす。

それでは今回もがんばりましょう!

環境

  • Mac mini 2020
  • チップ: Apple M1
  • メモリ: 16GB
  • OS: macOS Sequoia
  • ツール: Xcode v16.3(バージョンアップ)

つくるもの

Tapボタンで数字を上げる

スクリーンショット 2025-04-08 13.00.51.png

タスクキルをする

スクリーンショット 2025-04-08 13.01.26.png

もう一度立ち上げると状態が保存されている

スクリーンショット 2025-04-08 13.00.51.png

そして、アンインストールすると状態が初期化される

このように、タスクキルしても状態が保存されるようなアプリをつくっていきましょう!

TODO

  • 色を変えるロジックを切り出す
  • @AppStorageを指定してデータを保存する

開発

それでは、TODOに沿って開発をしていきましょう!

色を変えるロジックを切り出す

それでは現状のソースコードを見てみましょう。

import SwiftUI

struct ContentView: View {
    @State var count: Int = 0
    @State var textColor: Color = .black
    var body: some View {
        VStack {
            Text(String(count))
                .font(.largeTitle)
                .fontWeight(.regular)
                .foregroundColor(textColor)
            HStack {
                Button("Reset") {
                    count = 0
                    
                    textColor = .black
                }

                Button("Tap") {
                    count += 1
                    
                    if (count % 3 == 0) {
                        textColor = .red
                    } else {
                        textColor = .black
                    }
                }
            }
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

これを見て、ちょっと修正したいなと感じるところがあると思います。
そう、textColorを変更するところがちょっとごちゃっとしてるんですよね。なぜなら、countを計算するところとtextColorを計算するところが同じで、かつButton("Reset")にもtextColorのロジックが記述されているため、全体的に一貫性がないように感じるからです。

直し方としては、countに応じて適切な色を返す計算プロパティをつくってそれを使うことにしましょう。

var colorForMultipleOf3: Color {
    if (count % 3 == 0) {
        return .red
    } else {
        return .black
    }
}

単純に考えればこのような実装になると思うのですが、このままではcountが0のときにもテキストが赤になってしまって元の挙動と変わってしまうので、 countが0ではない という条件も付け足しましょう。

var colorForMultipleOf3: Color {
    if (count % 3 == 0 && count != 0) {
        return .red
    } else {
        return .black
    }
}

これで動きはするんですが、この程度の処理なら三項演算子を使えばもっと簡単に記述できそうですね。

var colorForMultipleOf3: Color {
    return (count % 3 == 0 && count != 0) ? .red : .black
}

そして、最後にSwiftではreturnを省略できるという文法があるため

var colorForMultipleOf3: Color {
    (count % 3 == 0 && count != 0) ? .red : .black
}

こんな感じで書くとスッキリしますね。

では、これをプログラムに組み込みましょうか。

この計算プロパティは、.foregroundColorに直接指定してやればよさそうですね。

Text(String(count))
    .font(.largeTitle)
    .fontWeight(.regular)
    .foregroundColor(colorForMultipleOf3)

こんな感じで大丈夫です。

それでは無駄な部分を消して実装しましょう。

import SwiftUI

struct ContentView: View {
    @State var count: Int = 0
    @State var textColor: Color = .black
    var body: some View {
        VStack {
            Text(String(count))
                .font(.largeTitle)
                .fontWeight(.regular)
                .foregroundColor(colorForMultipleOf3)
            HStack {
                Button("Reset") {
                    count = 0
                }

                Button("Tap") {
                    count += 1
                }
            }
        }
        .padding()
    }
    
    var colorForMultipleOf3: Color {
        (count % 3 == 0 && count != 0) ? .red : .black
    }
}

#Preview {
    ContentView()
}

これでソースコードがスッキリしましたね。

@AppStorageを指定してデータを保存する

それではここからが本番です!

今の状態では、countを上げてもタスクキルしてしまうとcountが0になってしまいます。
ですが、今回はタスクキルしてもcountを保存できるようにします。

それを実現するためにUserDefaultsという仕組みを使います。
UserDefaultsはAppleの公式ドキュメントで

An interface to the user’s defaults database, where you store key-value pairs persistently across launches of your app.

と説明されています。
これを翻訳すると

ユーザーのデフォルト・データベースへのインターフェイスで、アプリの起動に渡ってキーと値のペアを永続的に保存します。

となります。
簡単に説明すると、データベースにキーと値のペアを保存できるもの です。
そして、この仕組みはアプリを削除すると保存したデータも削除されるため今回に適しているといえるでしょう。

これは永続的にデータを保存できるものの、アプリを削除してしまうとデータが消えるため重要なデータを保存しないようにしましょう。

そして、SwiftUIには@AppStorageという便利な仕組みがあり、これを使えばUserDefaultsを簡単に扱えます。

使い方は

@AppStorage("キー") var 変数名: 

となります。
ここでの"キー"は値を識別するためのキーになります。できるだけ何の値なのかわかりやすい名前を付けておきましょう。

ここで注意なのですが、@AppStorageを使えるのは

  • Bool
  • String
  • Int
  • Double
  • Float
  • Data
  • URL

基本的にこれらなので、これ以外を保存したいときはUserDefaults(まだ保存できる型が多いため)を使うか、どうにか工夫して使ってください。

では実装していきましょうか。

今回は、countを保存していけばいいので

@AppStorage("CountKey") var count: Int = 0

こんな感じでいいと思います。
キーは適当につけちゃったんですが、無難にcountとかでもよかった気がします笑

では実際のコードに実装しましょうか。

import SwiftUI

struct ContentView: View {
    @AppStorage("CountKey") var count: Int = 0
    @State var textColor: Color = .black
    var body: some View {
        VStack {
            Text(String(count))
                .font(.largeTitle)
                .fontWeight(.regular)
                .foregroundColor(colorForMultipleOf3)
            HStack {
                Button("Reset") {
                    count = 0
                }

                Button("Tap") {
                    count += 1
                }
            }
        }
        .padding()
    }
    
    var colorForMultipleOf3: Color {
        (count % 3 == 0 && count != 0) ? .red : .black
    }
}

#Preview {
    ContentView()
}

こんな感じですね。

ここで、鋭い人は気がついたでしょう。
@Stateを消してもUIに反映されるのか」

そうです。本来は@StateをつけなければUIには反映されないはずです。
しかし、SwiftUIには@AppStorageの値が変更されるとViewが再描画されるという仕組みがあります。

つまり、@Stateも一緒にしてくれるっていうことですね。
(いやーべんりべんり)

というわけで、これで完成です:v:

UserDefaultsについて

ちょっとUserDefaultsの使い方も書いておこうと思います。

UserDefaultsを使ったデータの保存と取り出し方を紹介します

  • 保存
UserDefaults.standard.set(, forKey: "キー")
  • 取り出す
UserDefaults.standard.object(forKey: "キー")
UserDefaults.standard.string(forKey: "キー")
UserDefaults.standard.array(forKey: "キー")
UserDefaults.standard.dictionary(forKey: "キー")
UserDefaults.standard.data(forKey: "キー")
UserDefaults.standard.stringArray(forKey: "キー")
UserDefaults.standard.integer(forKey: "キー")
UserDefaults.standard.float(forKey: "キー")
UserDefaults.standard.double(forKey: "キー")
UserDefaults.standard.bool(forKey: "キー")
UserDefaults.standard.url(forKey: "キー")

取り出すには型にあった関数を使う

  • 削除
UserDefaults.standard.removeObject(forKey: キー)
  • 全削除
let appDomain = Bundle.main.bundleIdentifier
UserDefaults.standard.removePersistentDomain(forName: appDomain!)

ここで注意なのですが、値を取り出すときにオプショナル型で返ってくるものと非オプショナル型で返ってくるものがあります。

メソッド名 返り値の型 オプショナル? 説明
string(forKey:) String? ✅ Yes 文字列が存在しないと nil
array(forKey:) [Any]? ✅ Yes 配列が存在しないと nil
dictionary(forKey:) [String: Any]? ✅ Yes 辞書が存在しないと nil
data(forKey:) Data? ✅ Yes データが存在しないと nil
object(forKey:) Any? ✅ Yes 値が存在しないと nil
url(forKey:) URL? ✅ Yes URLが存在しないと nil
stringArray(forKey:) [String]? ✅ Yes 文字列配列が存在しないと nil
bool(forKey:) Bool ❌ No 値が存在しない場合は false を返す
integer(forKey:) Int ❌ No 値が存在しない場合は 0 を返す
float(forKey:) Float ❌ No 値が存在しない場合は 0.0 を返す
double(forKey:) Double ❌ No 値が存在しない場合は 0.0 を返す

こんな感じで、型によって違うので気をつけてください。

そして、今回のソースコードをUserDefualtsを使ったやり方で書いてみようと思います。

import SwiftUI

struct ContentView: View {
    @State private var count: Int = UserDefaults.standard.integer(forKey: "CountKey")
    @State var textColor: Color = .black
    var body: some View {
        VStack {
            Text(String(count))
                .font(.largeTitle)
                .fontWeight(.regular)
                .foregroundColor(colorForMultipleOf3)
            HStack {
                Button("Reset") {
                    count = 0
                    UserDefaults.standard.set(0, forKey: "CountKey")
                }

                Button("Tap") {
                    count += 1
                    UserDefaults.standard.set(count, forKey: "CountKey")
                }
            }
        }
        .padding()
    }
    
    var colorForMultipleOf3: Color {
        (count % 3 == 0 && count != 0) ? .red : .black
    }
}

#Preview {
    ContentView()
}

少し適当になってしまった気もするのですが、こんな感じで実装できます。
実行したらわかるのですが同じ挙動をすると思います。

おわりに

今回は、@AppStorageUserDefaultsを使ってタスクキルをしても情報を保存するプログラムを書きました。
UserDefaultsも簡単に扱えますが、@AppStorageはもっと簡単に扱えてとても便利ですね!

では次回もがんばっていきましょうー!お疲れ様でしたー!

次回: [SwiftUI入門] 4.NavigationStackでページの遷移をしよう!
前回: [SwiftUI] 2.カウンターアプリの機能を増やそう!

1
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
1
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?