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

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

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

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

解決済

Swift:UserDefaultsを読み込んだ際に値がnilになる

toyu
toyu

総合スコア11

Swift

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

1回答

0評価

0クリップ

152閲覧

投稿2022/05/09 03:29

前提

Swiftを独学で勉強し始めて1ヶ月ほどの初心者です。
家計簿のアプリを作成しています。
データを保存するためにUserDefaultsを使用して、MyData クラスを保存しています。
セクション名をMyDataクラスのインスタンス変数から読み取って使用しています。

発生している問題・エラーメッセージ

アプリ再起動時にUserDefaultsに保存されているMyDataクラスのインスタンス変数を読み込もうとすると、値が nil になっており、セクションを表示することができません。

UserDefaults呼び出し直後に値を読み込んでいることが問題なのかなと考えています。どのように改善するのが良いのでしょうか?

再起動前の画面

イメージ説明

再起動時の画面

イメージ説明

試したこと

tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? にて、戻り値の確認を行いました。

再起動時の出力結果 *** nil *** nil *** nil *** nil *** nil *** nil

再起動時の画面の状態から新たに同じセクションのデータを追加した場合、正しくセクションが追加されます。(データの追加は他クラスからsaveDataメソッドを呼び出しています)

再起動時の画面に5/8のデータを追加

イメージ説明

データ追加時の出力結果 *** nil *** Optional("2022-05-08") *** Optional("2022-05-08") *** Optional("2022-05-08") *** nil *** nil *** nil *** nil

該当のソースコード

Swift

import UIKit //UITableViewDataSource、UITableViewDelegate、DataSaveプロトコルを使用 class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, DataSave { // MyDataを格納した配列 var mydataList = [[MyData]]() // セクション名を格納した配列 var sectionList: [String] = [] @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() //let appDomain = Bundle.main.bundleIdentifier //UserDefaults.standard.removePersistentDomain(forName: appDomain!) let userDefaults = UserDefaults.standard // 保存しているsectionListを読み込み if let sectionMyList = userDefaults.array(forKey: "sectionList") as? [String] { sectionList = sectionMyList } // 保存しているMyDataの読み込み if let MyList = userDefaults.object(forKey: "mydataList") as? Data { do { if let appendList = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, MyData.self], from: MyList) as? [[MyData]] { mydataList.append(contentsOf: appendList) } } catch { // エラー処理なし } } } // 入力されたデータをMyDataに保存するメソッド func saveData(_ price: Int, _ tag: String, _ date: String, _ content: String, _ value: Int) { let mydata = MyData() mydata.price = price mydata.tag = tag mydata.date = date mydata.content = content mydata.segmented_value = value var index: Int = 0 // 一致するデータが存在するか確認 let result = sectionList.firstIndex(of: mydata.date!) /* ***日付を参照し、データの挿入場所を決定する*** */ if result == nil { // 挿入場所を表す変数 var insertNum: Int = 0           省略 index = insertNum } /* ***日付を参照し、インデクスを取得*** */ else { index = result! } // 追加 if result == nil { self.mydataList.insert([MyData](), at: index) } self.mydataList[index].insert(mydata, at: 0) // テーブルに行が追加されたことをテーブルに通知 if result == nil { self.tableView.insertSections(IndexSet(integer: index), with: .automatic) } else{ self.tableView.insertRows(at: [IndexPath(row: 0, section: index)], with: .automatic) } // MyDataの保存処理 let userDefaults = UserDefaults.standard userDefaults.set(sectionList, forKey: "sectionList") // Data型にシリアライズ do { let data = try NSKeyedArchiver.archivedData(withRootObject: self.mydataList, requiringSecureCoding: true) userDefaults.set(data, forKey: "mydataList") userDefaults.synchronize() } catch { // エラー処理なし } } // セクション数を返却するメソッド func numberOfSections(in tableView: UITableView) -> Int { return sectionList.count } // セクションに対してセル数を返却するメソッド func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{ return mydataList[section].count } // セクションに対してセクション名を返却するメソッド func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { print("***") print(mydataList[section][0].date) if let title = mydataList[section][0].date { return title } return nil } // テーブルの行ごとのセルを返却する func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {      省略 return cell } } // 独自クラスをシリアライズする際には、NSObjectを継承し // NSSecureCodingプロトコルに準拠する必要がある class MyData: NSObject, NSSecureCoding { static var supportsSecureCoding: Bool { return true } // インスタンス変数の宣言 var price: Int = 0 var tag: String? var date: String? var content: String? var segmented_value: Int = 0 // コンストラクタ override init() { } // デコード処理 required init?(coder aDecoder: NSCoder) { price = aDecoder.decodeInteger(forKey: "price") tag = aDecoder.decodeObject(forKey: "tag") as? String date = aDecoder.decodeObject(forKey: "date") as? String content = aDecoder.decodeObject(forKey: "content") as? String segmented_value = aDecoder.decodeInteger(forKey: "segmented_value") } // エンコード処理 func encode(with aCoder: NSCoder) { aCoder.encode(price, forKey: "price") aCoder.encode(tag, forKey: "tag") aCoder.encode(date, forKey: "data") aCoder.encode(content, forKey: "content") aCoder.encode(segmented_value, forKey: "segmented_value") } }

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

まだ回答がついていません

会員登録して回答してみよう

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

ただいまの回答率
87.20%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Swift

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