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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Core Data

Core DataはAppleのOS X and iOSのためのオブジェクトモデリングと持続性を持ったフレームワークです。Xcodeはエンティティー、属性そして関係性を特定するためのオブジェクトモデルの編集機能を提供します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

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

Q&A

解決済

1回答

1047閲覧

[SwiftUI]カスタムクラスへデータを保存したいです。

kngtkhr

総合スコア8

Core Data

Core DataはAppleのOS X and iOSのためのオブジェクトモデリングと持続性を持ったフレームワークです。Xcodeはエンティティー、属性そして関係性を特定するためのオブジェクトモデルの編集機能を提供します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

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

0グッド

0クリップ

投稿2022/09/30 02:39

編集2022/09/30 02:50

前提

SwiftUIで以下のような明細欄を作っています。

商品列2列3
鉛筆AB
消しゴムCD
りんごEF
ノートGH

商品,列2,列3以外はTextFieldです。

こちらの記事を参考に、
明細欄の商品列のデータ(鉛筆, ...ノート)を保存したいのですが、保存できませんでした。

CoreData

ENTITIES : DetailTable
Attributes : detailList(Binary Data)

カスタムクラス

Detail.swift

1import Foundation 2 3// Detail(明細)クラス定義 4class Detail: NSObject, NSCoding { 5 var ID = UUID() 6 var productName = "" 7 8 init(productName: String?) { 9 self.productName = productName! 10 } 11 12 func encode(with aCoder: NSCoder) { 13 aCoder.encode(self.productName, forKey: "productName") 14 } 15 16 required init?(coder aDecoder: NSCoder) { 17 self.productName = aDecoder.decodeObject(forKey: "productName") as! String 18 } 19}

データの保存

CreateView.swift

1struct CreateView: View { 2 @Environment(\.managedObjectContext) private var viewContext 3 @State var detailField = ["", ""] 4 ... 5 var body: some View { 6 ... 7 } 8// 保存ボタン押下時の処理 9 private func createEstimate() { 10 let detail = DetailTable(context: viewContext) 11 12// 明細情報の保存 13 let detailFields = detailField // 配列[鉛筆, 消しゴム, りんご, ノート] 14 @State var detailList = [] 15 for i in 0..<detailFields.count { 16 let detail = Detail(productName: detailFields[i]) 17 detailList.append(detail) 18 } 19 do { 20 let detailListData: Data = try NSKeyedArchiver.archivedData(withRootObject: detailList, requiringSecureCoding: false) as Data 21 22 detail.detailList = detailListData 23 24 } catch let error { 25 print(error.localizedDescription) 26 } 27 28 // 生成したインスタンスをCoreDataに保存する 29 do { 30 try viewContext.save() 31 print("保存成功") 32 } catch { 33 let nsError = error as NSError 34 fatalError("Unresolved error \(nsError), \(nsError.userInfo)") 35 } 36 37 presentation.wrappedValue.dismiss() 38 } 39}

実現したいこと

・カスタムクラスへデータを保存したいです。

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

エラーメッセージはありません。

該当のソースコード

CreateView.swift

1// 保存ボタン押下時の処理 2 private func createEstimate() { 3 let detail = DetailTable(context: viewContext) 4 5// 明細情報の保存 6 let detailFields = detailField // 配列[鉛筆, 消しゴム, りんご, ノート] 7 @State var detailList = [] 8 for i in 0..<detailFields.count { 9 let detail = Detail(productName: detailFields[i]) 10 detailList.append(detail) 11 }

試したこと

print()を使い、値が出力されているか試しました。

  1. 配列detailListには値が追加されていませんでした。
  2. 配列detailFieldsには値が追加されていました。
  3. detailにも値は入っていました。しかし、以下のような同じ値でした。

detail結果
<ProjectName.Detail: 0x600000ec46f0>
<ProjectName.Detail: 0x600000e63210>
<ProjectName.Detail: 0x600000e63210>
<ProjectName.Detail: 0x600000e63210>

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

macOS Monterey バージョン 12.3
Swift version 5.6.1
Xcode バージョン 13.4.1

たくさん調べたのですが解決できませんでした。
お力添えいただけますととても嬉しく思います。
よろしくお願いいたします。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2022/09/30 13:49

Core Dataは レコード(行?)ごとに保存するのが一般的なのかな?と思うのですが、 (質問欄の例で言うと「鉛筆」「消しゴム」「りんご」「ノート」の4レコード) 1行にすべてのデータを保存しようとしているように見えます。 これには何か理由があるのでしょうか? *Core Dataを使用する意味があまりないように見えまして、普通のファイルでも良いのかな?と思いましたので確認してみました
kngtkhr

2022/09/30 21:13

コメントくださり、ありがとうございます。 私がリレーションシップを使ったCore Dataへの保存と呼び出しをできなかったのが理由になります。 特に ①概要テーブル:明細欄テーブルは1:多のリレーションシップがある時、概要テーブルが主キーになると、Viewから呼び出しずらくならないか ②保存した後、編集画面でCore Dataを呼び出すとき、2つのENTITIYの全てのカラムを呼び出すことができるか 1ヶ月間もがいているうちに、1レコード(行)に全てのデータを保存する方法なら明細欄の保存と呼び出しができると思い、このようなやり方になりました。 Core DataはiCloudと連携できる点が魅力と思い、採用しました。 もし私が質問したような1レコード(行)に全てのデータを保存する方法を使わない場合、 データベースを普通のファイルやRealm,Firebase等に変更する方がいいのか、 リレーションシップを使う方法が適しているのか、 アドバイス頂けると幸いです(T T) よろしくお願い致します。
退会済みユーザー

退会済みユーザー

2022/09/30 23:46

コメントありがとうございます。 そういう理由だったのですね。 1ヶ月進まないのはちょっと苦痛ですね・・ iCloudとの連携を考えるのでしたら、 Realm、FirebaseなどよりCore Dataの方が良いのかもしれませんね。 リレーションシップを使う方法が良い気がしますが、 具体的な問題点を正しく認識できていないかもしれませんので、 まだなんとも言えないです・・ --- 「リレーションシップを使ったCore Dataへの保存と呼び出しをできなかった」 というのは、Fetchで特定のレコードを取得できないということでしょうか? Filtering Results https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FetchingObjects.html#//apple_ref/doc/uid/TP40001075-CH6-SW3 Fetch request https://developer.apple.com/library/archive/documentation/DataManagement/Devpedia-CoreData/fetchRequest.html#//apple_ref/doc/uid/TP40010398-CH26-SW1
kngtkhr

2022/10/01 00:44

ご返信ありがとうございます。 苦痛です・・・ リレーションシップを使う方法で進め、諦めず、形作っていこうと思います。 Fetchで概要テーブルの特定のレコードは取得できますが、リレーションシップを使って明細欄テーブルを呼び出すことができませんでした。 保存の方でも、概要テーブルの保存はできますが、明細欄テーブルに保存することができませんでした(TT) 新規作成するCreateView.swiftで保存ボタンを押し、Core Dataに保存する処理を行う部分において、リレーションシップの使い方が分からないままになっています。 // 保存ボタン押下時の処理 private func createEstimate() { let outline = OutlineTbl(context: viewContext) let detail = DetailTbl(context: viewContext) // ←viewContextの2個目を呼び出して概要テーブルと明細テーブルそれぞれに保存できるのか? outline.Number = Number outline.prjName = prjName outline.Date = createDate outline.updateDate = Date() detail.productName = productName // 生成したインスタンスをCoreDataに保存する try? viewContext.save() presentation.wrappedValue.dismiss() } リンクありがとうございます。 初めて見るリンクで参考になります。今から読みます。
guest

回答1

0

ベストアンサー

コメントありがとうございます。
画像を載せたかったので回答欄に書きました。

「1:多のリレーションシップがある時」がポイントなのでしょうかね。

適切かどうかわからないところもありますが、次のようなコードで確認しましたところ、
1:多でも保存と読み込みができたようでした。

swift

1 private func save() { 2 let outline = Outline(context: viewContext) 3 outline.number = 2 4 outline.prjName = "prjName(2)" 5 outline.date = Date() 6 outline.updateDate = Date() 7 let detail1 = Detail(context: viewContext) 8 detail1.productName = "鉛筆" 9 detail1.attribute2 = "A" 10 detail1.attribute3 = "B" 11 outline.addToDetails(detail1) 12 let detail2 = Detail(context: viewContext) 13 detail2.productName = "消しゴム" 14 detail2.attribute2 = "C" 15 detail2.attribute3 = "D" 16 outline.addToDetails(detail2) 17 let detail3 = Detail(context: viewContext) 18 detail3.productName = "りんご" 19 detail3.attribute2 = "E" 20 detail3.attribute3 = "F" 21 outline.addToDetails(detail3) 22 let detail4 = Detail(context: viewContext) 23 detail4.productName = "ノート" 24 detail4.attribute2 = "G" 25 detail4.attribute3 = "H" 26 outline.addToDetails(detail4) 27 try? viewContext.save() 28 print("save") 29 } 30 private func fetch() { 31 let request = Outline.fetchRequest() 32 request.predicate = NSPredicate(format: "prjName == %@", "prjName(2)") 33 let result = try? viewContext.fetch(request) 34 if let outlineArray = result, 35 let outline = outlineArray.first { 36 print("\(outline.number), \(outline.prjName!), \(outline.date!), \(outline.updateDate!)") 37 let details = outline.details as? Set<Detail> ?? [] 38 for detail in details { 39 print("\(detail.productName!), \(detail.attribute2!), \(detail.attribute3!)") 40 } 41 } 42 }

EntityのRelationshipの設定の部分につきまして、
画像を添付しておきます。
*仕様に合うように細かい項目は置き換えて見てください

概要エンティティのリレーションシップです。
イメージ説明

明細エンティティのリレーションシップです。
イメージ説明

投稿2022/10/01 02:17

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

kngtkhr

2022/10/01 02:32

とても分かりやすく、コードまで記載していただきありがとうございます。嬉しいです。 今から確認し、改めてご返信します。
kngtkhr

2022/10/01 06:22

保存することができました。 private func save() { let outline = Outline(context: viewContext) let outline = Outline(context: viewContext) outline.number = number outline.prjName = prjName outline.date = Date() outline.updateDate = Date() for i in 0..<detailField.count { let detail = Detail(context: viewContext) detail.productName = detailField[i] // [“鉛筆”, “消しゴム”, ] detail.dtlStandard = dtlStandard[i] // [“2B”, “MONO”, ] outline.addToDetails(detail) print(detail) } 出力結果 <Detail: 0x60000000f070> (entity: Detail; id: 0x6000022fd0c0 <x-coredata:///Detail/tF4A1FF31-1935-494A-9E0F-2686F9A800043>; data: { comment = nil; dtlStandard = 2B; productName = 鉛筆 outline = "0x6000022fca60 <x-coredata:///Outline/tF4A1FF31-1935-494A-9E0F-2686F9A800042>"; }) ... 続いて、呼び出しもやっていきます。
kngtkhr

2022/10/01 09:00

呼び出しの確認もできました。 let request = Outline.fetchRequest() request.predicate = NSPredicate(format: "number == %@", number) let result = try? viewContext.fetch(request) if let outlineArray = result, let outline = outlineArray.first { print("\(outline.number), \(outline.prjName!)") let details = outline.details as? Set<Detail> ?? [] for detail in details { print("\(detail.productName!), \(detail.dtlNote!)") } } 出力結果 Optional("001"), 買い物 ノート, Optional("A5") 消しゴム, Optional("MONO") 鉛筆, Optional("HB") とても分かりやすく、自分の仕様に合わせることができました。 ありがとうございます。
kngtkhr

2022/10/01 09:09

この度は休日にも関わらず、たくさんお時間くださりありがとうございます。 長い時間もやもやしていた部分がやっと晴れました。 諦めかけていたリレーションシップの方法で解決することができ、とても感謝しています。 これから編集画面へ呼び出した値を入れていきます。 もっとコードが書けるように努力します。 本当にたくさんのアドバイスとご教示ありがとうございます。 感謝の気持ちでいっぱいです。 ありがとうございます: )
退会済みユーザー

退会済みユーザー

2022/10/01 09:48

こんなに丁寧に返信をしてもらえて本当に嬉しいです。 回答してみて良かったです。 1ヶ月も悩んでしまう前にまた質問してみてくださいね。 *Swiftは業務で使ったことがありませんので見当違いなことを書いてしまうかもしれませんが・・
kngtkhr

2022/10/01 10:14

ありがとうございます。 時間がかかったことが解決し、xg63ex2bさんの丁寧で分かりやすい回答に温かさを感じました。 質問するの恐かったのですが、次からはもう少し早く質問しようと思います。 コメントいただき自分の頭も整理され、とても助かります。 先輩エンジニアに感謝します: )
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問