質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2214閲覧

配列の中身を保存して、初期化しないようにしたい

shuuuuun

総合スコア1

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/04/27 00:11

編集2021/04/27 01:57

前提・実現したいこと

現在支出管理アプリを作成しているのですが、その際、項目+利用金額という形で表示させています。
これを配列に格納して管理しているのですが、アプリを閉じても初期化されないようにUserDefaultsを使おうと考えています。
しかし、調べて試してもうまくいかず、行き詰まっています。
どなたかご教授いただけると助かります。

該当のソースコード

import SwiftUI struct ContentView: View { //タイトルバーの年月 @AppStorage("TitleYear") var titleYear = "yyyy" @AppStorage("TitleMonth") var titleMonth = "mm" //シートの状態 //年月変更シート @State var isShowDate: Bool = false //項目追加シート @State var isShowItem: Bool = false //各種インスタンス生成 @EnvironmentObject var userItemList: UserItemList var userDefaults = UserDefaults.standard var body: some View { NavigationView{ List{ ForEach(userItemList.itemList){ item in KomokuList(items: item.itemName, spendPrice: item.price) } //行削除操作時に呼び出す処理の指定 .onDelete(perform: rowRemove) } //タイトル .navigationBarTitle("(self.titleYear)" + "年" + "(self.titleMonth)" + "月の利用予定額", displayMode: .inline) //項目追加ボタンと年月変更ボタンを配置 .navigationBarItems(leading: Button(action: { isShowItem = true }){ Text("追加") }, trailing: Button(action: { isShowDate = true }) { Text("年月変更") }) .sheet(isPresented: $isShowDate){ DateChange(year: $titleYear, month: $titleMonth, isPresented: $isShowDate) } .sheet(isPresented: $isShowItem){ ItemAdd(isPresentedItem: $isShowItem) } } } //行削除処理 func rowRemove(offsets: IndexSet) { userItemList.itemList.remove(atOffsets: offsets) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView(titleYear: "0000", titleMonth: "00") .environmentObject(UserItemList()) } }
//項目や金額を設定する import SwiftUI struct ItemAdd: View { @Binding var isPresentedItem: Bool @State var inputItem: String = "" @State var inputPrice: String = "" @EnvironmentObject var userItemList: UserItemList var body: some View { NavigationView{ VStack{ HStack { Text("利用品目名:") TextField("利用品目名を記入してください", text: $inputItem) } HStack { Text("利用予定額:") TextField("利用予定額を記入してください", text: $inputPrice).keyboardType(.numberPad) } Spacer() Button(action: { isPresentedItem = false }){ Text("キャンセル") } Button(action: { isPresentedItem = false self.createItem() }){ Text("追加") }.disabled(!inputValueCheck()) Spacer() } .navigationBarTitle("購入品目追加", displayMode: .inline) } } //利用品目名と利用予定額を生成する関数 func createItem(){ let newItem = KomokuData(itemName: self.inputItem, price: Int(self.inputPrice)!) self.userItemList.itemList.insert(newItem, at: 0) self.inputItem = "" self.inputPrice = "" } //入力値チェック //各項目が前後空白を除いて1文字以上ある場合trueを返す func inputValueCheck() -> Bool { inputItem.trimmingCharacters(in: .whitespacesAndNewlines).count >= 1 && inputPrice.trimmingCharacters(in: .whitespacesAndNewlines).count >= 1 } } struct ItemAdd_Previews: PreviewProvider { static var previews: some View { ItemAdd(isPresentedItem: Binding.constant(false)) .environmentObject(UserItemList()) } }
//各種データ管理 struct KomokuData: Identifiable { var id = UUID() var itemName:String var price:Int init(itemName: String, price: Int) { self.itemName = itemName self.price = price } }
//配列を管理 import swiftUI class UserItemList: ObservableObject{ @Published var itemList = [ KomokuData(itemName: "課金", price: 10000), KomokuData(itemName: "お菓子代", price: 3000), KomokuData(itemName: "漫画代", price: 1000) ] @Published var isEditing: Bool = false }

試したこと

下記のサイトを調べてみました
https://capibara1969.com/2531/#toc16

補足情報(FW/ツールのバージョンなど)

Xcode:12.5
UI:SwiftUI

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomato879241

2021/04/27 01:50

投稿ボタンを押す前に、「配列保存の保存をして」という題名から自分の書いた内容を確認されたらどうでしょうか?
shuuuuun

2021/04/27 01:55

すみません。 ご指摘ありがとうございます。
tomato879241

2021/04/27 02:44

userDefaultsを使って、どこで何を保存しているかの記載がないのではないですか?それとも、それも質問のうちですか?
shuuuuun

2021/04/27 03:50

そちらも質問のうちです。 僕が思っていたことは、ContentViewでuserDefaultsで保存するのかなと思っていたのですが、うまくできないため、どこで使えばいいのかという質問になります。
tomato879241

2021/04/27 04:00

それは自分で検索すれば、答えがわかるような気がしますが。。。
shuuuuun

2021/04/27 04:26

まだswift始めて間もなく、分からないことだらけでした。 もう少し自分でやってみます。
tomato879241

2021/04/27 07:04

KomokuDataのStructが間違っています。
shuuuuun

2021/04/27 12:17

そのようでした。 色々とご指摘していただきありがとうございます。
guest

回答1

0

ベストアンサー

UserDefaults には構造体やその配列の値を直接保存することはできないので、JSON に変換して保存するのが一般的です。そのためにはまず KomokuData を Codable にする必要があります。

swift

1struct KomokuData: Identifiable, Codable { 2 // 中身はそのまま

あとは UserItemList で読み書きすれば良いかと。
なお、デバッグ用に print してますが、動作が確認できたら消してください。
また、saveItemList ではデバッグビルドの場合に synchronize してますが、これは Xcode からプログラムを停止させると UserDefaults が保存されないことがあるのを防ぐためです。

swift

1class UserItemList: ObservableObject{ 2 @Published var itemList = loadItemList() ?? [ 3 KomokuData(itemName: "課金", price: 10000), 4 KomokuData(itemName: "お菓子代", price: 3000), 5 KomokuData(itemName: "漫画代", price: 1000) 6 ] { 7 didSet { 8 saveItemList() 9 } 10 } 11 @Published var isEditing: Bool = false 12 13 static func loadItemList() -> [KomokuData]? { 14 guard let data = UserDefaults.standard.data(forKey: "itemList") else { return nil } 15 print("load: (String(decoding: data, as: UTF8.self))") 16 return try? JSONDecoder().decode([KomokuData].self, from: data) 17 } 18 19 func saveItemList() { 20 if let data = try? JSONEncoder().encode(itemList) { 21 print("save: (String(decoding: data, as: UTF8.self))") 22 UserDefaults.standard.set(data, forKey: "itemList") 23 #if DEBUG 24 UserDefaults.standard.synchronize() 25 #endif 26 } 27 } 28}

投稿2021/04/27 11:13

編集2021/04/27 11:16
hoshi-takanori

総合スコア7901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shuuuuun

2021/04/27 12:16

ご教授いただきありがとうございます。 うまくできました。 リストを表示させるContentViewでやるものだと思っていたことがいけなかったみたいです。 大変勉強になりました。
hoshi-takanori

2021/04/27 12:28

itemList を追加・編集・削除した時に saveItemList を呼び出しても良いのですが、書き忘れる可能性が高いので、didSet で処理すれば itemList が変更された時には確実に保存されるようになります。
shuuuuun

2021/04/29 02:53

返信遅くなり申し訳ありません。 もう一点お聞きしたいことがあるのですが、 利用した金額を入力して現在から引いて表示させるという処理をした時に、その値も保存したいのですがどのようにしたらよろしいでしょうか? みづらくて申し訳ないのですが、計算部分は下記のコードになります。 import SwiftUI struct KomokuList: View { //利用項目名 let items:String //予定支出額 @State var spendPrice: Int //利用額 @State var useSpendPrice:Int = 0 //シートの状態 @State var isShow: Bool = false var body: some View { HStack { Text(items + ":") if spendPrice - useSpendPrice <= 0 { Text("(spendPrice - useSpendPrice)" + "円") .foregroundColor(.red) } else{ Text("(spendPrice - useSpendPrice)" + "円") } Button(action: { spendPrice = spendPrice - useSpendPrice isShow = true }){ Text("利用") } .sheet(isPresented: $isShow){ PriceChange(isPresentedPrice: $isShow, price: $useSpendPrice) } } } } struct KomokuList_Previews: PreviewProvider { static var previews: some View { KomokuList(items: "課金", spendPrice: 10000) } } コメントはマークダウン形式で書けないですよね。。。 もしアドバイスあれば頂きたいです。
hoshi-takanori

2021/04/29 03:44

えっと、KomokuList は KomokuData の各要素を表示するやつですよね。(名前に List があると複数表示することを意味しますが、実際には一つの KomokuList には一つの項目しか表示しないので、List という名前にするのは良くないです。) で、「利用した金額を入力して現在から引いて表示させる」ってつまり現在の残高ってことでしょうか。UserItemList にプロパティを追加して保存すれば良いのでは…。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問