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

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

新規登録して質問してみよう
ただいま回答率
85.50%
保存

保存(save)とは、特定のファイルを、ハードディスク等の外部記憶装置に記録する行為を指します。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

1回答

3936閲覧

[Swift]構造体をUserDefaultsで保存する方法

niship

総合スコア37

保存

保存(save)とは、特定のファイルを、ハードディスク等の外部記憶装置に記録する行為を指します。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

1クリップ

投稿2017/05/24 05:30

編集2017/05/26 01:49

質問をご覧いただき、有難うございます。
現在、スケジュールアプリを作成しております。

実現したいこと:

ユーザーが指定した「タイトル(String)」「開始時刻(Date)」「終了時刻(Date)」を構造体として配列に追加し、UserDafaultsで保存したいと思っております。
保存したものを画面遷移後に取得し、TableViewCellに表示させたいと思っております。

調べたところ、構造体はUserdefaultsに保存ができないとのことでしたので、
下記URLを見ながら、「NSCodingとNSKeyedArchiverを使わないパターン」を試してみましたが、シミュレータを起動すると、デバックエリアに

Could not cast value of type '__NSCFData' (0x1094ea6d0) to 'NSArray' (0x1094eae28). (lldb)

と表示され、クラッシュしてしまいます。

URL:http://qiita.com/paming/items/a93b8572f65275eb1dd4

extensionやstructなど、使い慣れておりませんので、
使用時のルールや記載場所などで、間違いがあるかもしれません。
また、extensionで機能を拡張した後のUserdefaultsの使用方法などもご教示いただければ幸いです。

/* 表示データ */ struct ScheduleData{ // スケジュールのコメント var title: String = "" // スケジュールの開始時間 var startDate: Date! // スケジュールの終了時間 var endDate: Date! init(title : String , startDate : Date , endDate : Date){ self.title = title self.startDate = startDate self.endDate = endDate } } var schedules = [ScheduleData]()
func saveData() { dataOut() var dateFromString = myDateFormatter.date(from: dateTextField.text!) var dateFromString2 = myDateFormatter.date(from: dateTextField2.text!) if dateFromString != nil { if dateFromString2 != nil { if titleTextField.text! != nil { schedules.append(ScheduleData(title : titleTextField.text!, startDate : dateFromString!, endDate : dateFromString2!)) detailArray.append(detailTextView.text!) placeArray.append(placeTextField.text!) urlArray.append(urlTextField.text!) alarmDateArray.append(alarmTimeTextField.text!) UserDefaults.standard.set(schedules, forKey: "schedules") UserDefaults.standard.set(detailArray, forKey: "detailArray") UserDefaults.standard.set(placeArray, forKey: "placeArray") UserDefaults.standard.set(urlArray, forKey: "urlArray") UserDefaults.standard.set(alarmDateArray, forKey: "alarmDateArray") } } } else { warningAlertController() }
func dataOut() { if UserDefaults.standard.object(forKey: "schedules") != nil { schedules = UserDefaults.standard.object(forKey: "schedules") as! [ScheduleData] } if UserDefaults.standard.object(forKey: "detailArray") != nil { detailArray = UserDefaults.standard.object(forKey: "detailArray") as! [String] } if UserDefaults.standard.object(forKey: "placeArray") != nil { placeArray = UserDefaults.standard.object(forKey: "placeArray") as! [String] } if UserDefaults.standard.object(forKey: "urlArray") != nil { urlArray = UserDefaults.standard.object(forKey: "urlArray") as! [String] } if UserDefaults.standard.object(forKey: "alarmDateArray") != nil { alarmDateArray = UserDefaults.standard.object(forKey: "alarmDateArray") as! [String] } }
extension UserDefaults { var logDataArray:[ScheduleData]{ set(datas){ // Swiftのオブジェクトを、NSObjectなオブジェクトに変換する let newDatas:[NSDictionary] = datas.map{ ["title":$0.title, "startdate":$0.startDate, "endDate":$0.endDate] as NSDictionary } // NSObjectなオブジェクトのみになったから、setObjectできる self.set(newDatas,forKey:"schedules") } get{ // NSDictionaryの配列として、データを取得 let datas = self.object(forKey: "schedules") as? [NSDictionary] ?? [] // 保存されたデータから復元出来無い場合もあり得るので、 // mapではなくreduceを使う let array = datas.reduce([]){ (ary, d:NSDictionary) -> [ScheduleData] in // dateやmessageがnilでないなら、MyLogDataを作って足し込む if let title = d["title"] as? String, let endDate = d["endDate"] as? Date, let startDate = d["startDate"] as? Date { return ary + [ScheduleData(title: title, startDate: startDate, endDate: endDate)] }else{ return ary } } return array } } }

訂正箇所など、ご教示いただければ幸いです。

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

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

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

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

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

fuzzball

2017/05/25 02:51

下記URLとは?
guest

回答1

0

ベストアンサー

Arrayで取得するときはobject(forKey:)ではなくarray(forKey:)を使って下さい。

追記

extensionで保存/取得出来るように拡張してあるのに、それを使っていません。
下記のようにして保存/取得を行って下さい。

データの保存

swift

1UserDefaults.standard.logDataArray = schedules
取得の方法

swift

1schedules = UserDefaults.standard.logDataArray

ScheduleData.swift

ScheduleDataの定義とExtensionを別ファイルに切り出します。
ViewControllerとDetailsViewControllerからは、これらの記述を削除して下さい。

swift

1import Foundation 2 3struct ScheduleData { 4 (省略) 5} 6 7extension UserDefaults { 8 (省略) 9}

投稿2017/05/24 06:06

編集2017/05/25 05:25
fuzzball

総合スコア16731

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

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

niship

2017/05/24 06:41

ご回答有り難うございます。 その他にextension UserDefaults{}はUserDefaultsでデータを保存する画面でしか宣言していないのですが、UserDefaultsでデータを取り出す画面(別の画面)でもUserDefaultsの機能は拡張されているのでしょうか?
fuzzball

2017/05/24 06:48

されています。 というか、実際に試してみれば分かるでしょう?
niship

2017/05/24 06:56

有り難うございます。 後ほど、ご回答頂いた箇所を訂正し、検証してみます。
niship

2017/05/25 02:33

取得時のコードをarray(forKey:)に変更し、再度シミュレータにて確認したところ、 クラッシュし、デバックエリアには[User Defaults] Attempt to set a non-property-list objectや、Attempt to insert non-property list object と表示されていました。構造体がしっかり変換されていないということでしょうか。
fuzzball

2017/05/25 02:38

どこで落ちている(エラーが出ている)のでしょうか?
niship

2017/05/25 02:42

完了ボタンを押下するとクラッシュします。 コード上では、完了ボタンを押下するとdataOut()とSaveData()を呼び出すとともに、元の画面へ遷移するようにしております。
fuzzball

2017/05/25 02:44

「どこで」というのは「コードのどこで」という意味です。
niship

2017/05/25 02:57

エラーは出ておらず、ビルドも通るのですが、シミュレータで動作を確認すると、saveData()とdataOut()が呼び出される場面でクラッシュします。 デバックエリアにも[User Defaults] Attempt to set a non-property-list objectや、Attempt to insert non-property list objectと表示されていたので、恐らく変換が出来ていないのかなと思ったのですが、原因が掴めません。
fuzzball

2017/05/25 03:03

回答に追記しました。
niship

2017/05/25 04:39

回答の追記、有難うございます。コードを訂正しましたが、 Cannot assign value of type '[DetailsViewController.ScheduleData]' to type '[ScheduleData]' という内容のコンパイルエラーが出ました。
fuzzball

2017/05/25 04:44

どこで出たのでしょうか?DetailsViewControllerという単語は質問内に一度も出てきていませんし。
niship

2017/05/25 04:53

申し訳有りません。失念しておりました。 DetailsViewController.swiftにてschedulesを保存し、ViewController.swiftにてschedulesのデータを取り出したいと思っています。 DetailsViewController.swiftのsaveData()内でUserDefaults.standard.logDataArray = schedulesを記述すると先ほどのコンパイルエラーは発生しました。
fuzzball

2017/05/25 05:02 編集

DetailsViewController内のschedulesの定義はどうなっていますか?
niship

2017/05/25 05:09

/* 表示データ */ struct ScheduleData{ // スケジュールのコメント var title: String = "" // スケジュールの開始時間 var startDate: Date! // スケジュールの終了時間 var endDate: Date! init(title : String , startDate : Date , endDate : Date){ self.title = title self.startDate = startDate self.endDate = endDate } } var schedules = [ScheduleData]() と、なっております。 ViewController.swiftとDetailsViewController.swift どちらも同じ内容でschedulesを定義をしています。
fuzzball

2017/05/25 05:27

回答に追記しました。 実際に検証出来ないので想像になりますが、class内にScheduleDataの定義を書いているようなので、Extensionから見えてないような気がします。(同じ定義を二箇所に書くのも良くないです) ScheduleDataに関する別ファイルを作成して下さい。
niship

2017/05/26 01:29

回答の追記、有難うございました。 ご教示頂いた内容で検証すると、ビルドは通ったのですが、シミュレータで確認すると、TableViewCellにschedulesのデータが表示されなくなりました。ここから先はまた別の質問として投稿したほうがよろしいでしょうか。
fuzzball

2017/05/26 01:35 編集

schedulesの保存/取得が出来ているのにTableViewCellに表示されないのであれば別の質問にした方が良いと思いますが、その前に、一旦質問内のコードを更新して下さい。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問