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

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

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

Q&A

解決済

1回答

1266閲覧

【SwiftUI】CoreDataに 条件によってデータを追加または更新したい

kuwatsy

総合スコア5

0グッド

0クリップ

投稿2022/09/10 04:47

前提

SwiftUI初心者です。iPhone向けのアプリを開発すべく勉強中です。
CoreDataを実装する手順は理解したつもりです。

Entity名: Events
Attributes: dateID (Date), event (String) が登録されていて、すでにデータ

dateID  event
2022/1/1 初日の出
2022/5/1 山登り
2022/9/1 ぶどう狩り

がsaveされています。(dateIDは、常に時刻は00:00:00として格納されます)

実現したいこと

  1. DatePickerで日付を入力し、selectedDateに格納
  2. TextFieldでイベント内容を入力し、addEventに格納後、
  3. "入力" ボタンを押すと、
  4. selectedDateの時刻を00:00:00に変えて
  5. CoreData に
    a: リストにない新しい日付の場合は、CoreDataにデータを追加
    b: 同じ日付がリストにある場合は、eventの中身を新しい内容に変更

としたいと考えています。

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

5)-a までは動作を確認した(下記ソースコード)のですが、
5)-b をどうすれば実現できるかがわかりません。

要は、2022/10/1 「なし狩り」と入力されたら、CoreDataに4つ目のデータとして追加され、
2022/9/1 「なし狩り」と入力されたら、「ぶどう狩り」→「なし狩り」に3つ目のデータを変更したいというものです。

private func updateEvents() { } の中身に「追加の場合」だけでなく「変更の場合」を組み込みたいのですが、どなたか解決策をいただけると大変ありがたいです。よろしくお願いいたします。
(CoreData導入に関わるソースコードは煩雑になるので記入しておりません)

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

該当のソースコード

SwiftUI

1import SwiftUI 2 3struct InputViewCoreData: View { 4 @Environment(\.managedObjectContext) private var viewContext 5 @State private var selectedDate = Date() 6 @State private var addEvent = "" 7 8 @FetchRequest( 9 entity: Events.entity(), 10 sortDescriptors: [NSSortDescriptor(key: "dateId", ascending: false)], 11 animation: .default 12 ) var fetchedEventsList: FetchedResults<Events> 13 14 var body: some View { 15 VStack { 16 Text("日付").font(.title).padding() 17 HStack { 18 DatePicker("", selection: $selectedDate, displayedComponents: .date) 19 .labelsHidden() 20 TextField("イベントを入力", text: $addEvent) 21 .textFieldStyle(RoundedBorderTextFieldStyle()) 22 Button(action: { updateEvents() } ) { 23 Text("入力").buttonStyle(BorderlessButtonStyle()) 24 } 25 } .padding() 26 27 List { 28 ForEach(fetchedEventsList) { events in 29 HStack { 30 Text(events.stringDateId) // stringDateId: String { dateFomatter(date: dateId ?? Date()) } 31 Text(events.event ?? "") 32 } 33 } 34 .onDelete(perform: deleteEvents) 35 } 36 } 37 } 38 39 private func updateEvents() { 40 let events = Events(context: viewContext) 41 selectedDate = dateToJstDate(date: selectedDate) 42 43 events.dateId = selectedDate 44 events.event = addEvent 45 46 try? viewContext.save() 47 } 48 49 private func deleteEvents(offsets: IndexSet) { 50 offsets.forEach { index in 51 viewContext.delete(fetchedEventsList[index]) 52 } 53 try? viewContext.save() 54 } 55 56 private func dateToJstDate(date: Date) -> Date { 57 let calendar = Calendar(identifier: .gregorian) 58 59 return calendar.date(from: DateComponents( 60 year: Calendar.current.component(.year, from: date), 61 month: Calendar.current.component(.month, from: date), 62 day: Calendar.current.component(.day, from: date)))! 63 } 64 65 struct InputViewCoreData_Previews: PreviewProvider { 66 static var previews: some View { 67 InputViewCoreData().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) 68 } 69 } 70}

試したこと

ネット情報でCoreData検索に関するものを見てみましたが、いまいちしっくりくるものがありませんでした。

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

Xcode 13.4.1

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2022/09/10 08:39

しっくりこない回答をしてしまうかもしれませんので、、 どのようなコードがしっくりこないか、 質問欄に追記していただいてもよろしいでしょうか?
kuwatsy

2022/09/10 09:12

見ていただきましてありがとうございます。 しっくりいかないというか、勉強&理解不足でして、おそらくですけど fetchedEventList で、dateが2022/9/1の行のeventの値はどうすれば参照できるのか、がわかれば何とかなるのでは?と思っています。 fetchedEventList[date].eventではないと思いますが…
guest

回答1

0

ベストアンサー

コメントありがとうございます。

fetchedEventsListを使うとしたら次のような感じでしょうか。

swift

1 private func updateEvents() { 2 // ***** 同じ日付の要素が取得できた場合は変更、取得できない場合は追加します。 3 selectedDate = dateToJstDate(date: selectedDate) 4 if let events = filter(date: selectedDate) { 5 events.event = addEvent 6 } else { 7 let events = Events(context: viewContext) 8 9 events.dateId = selectedDate 10 events.event = addEvent 11 } 12 try? viewContext.save() 13 } 14 15 // ***** 同じ日付の要素を取得します。 16 // https://developer.apple.com/documentation/swiftui/fetchedresults/filter(_:) 17 func filter(date: Date) -> Events? { 18 let array = fetchedEventsList.filter({ $0.dateId == date }) 19 let element = array.first 20 return element 21 }

CoreDataは勉強中なので誤ったことを書いているかもしれませんが・・
fetchedEventsListを使った形ですと、
全件を処理することになり、
データ件数が多くなるとパフォーマンスの問題が出てくるかもしれません。
NSPredicateを使ってCoreDataから抽出する際にデータを絞り込んだ方が本当は良いのかもしれません。

https://developer.apple.com/documentation/foundation/nspredicate

投稿2022/09/10 09:43

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

kuwatsy

2022/09/11 12:55

回答いただきましてありがとうございました。コードをご提案いただいたものに修正し、無事思った通りに動作することを確認いたしました。データは多くても100件程度ですので、この方法でそのまま進めたいと思います。 if letやEvents?など、理解できてないところが多々ありますが、ひとつづつ克服していきたいと思います。本当にありがとうございました!
退会済みユーザー

退会済みユーザー

2022/09/11 14:16

コメントありがとうございます。 > データは多くても100件程度ですので、この方法でそのまま進めたいと思います。 100件であれば特に気にしなくても大丈夫そうですね。 --- Events?の「?」はOptionalですね。 nilが含まれる可能性のある型です。 今回はデータがない場合にnilを返していますのでOptionalになっています。 (配列に要素がない場合にfirstがnilを返しています) Optionals https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID330 --- if letはOptional Bindingですね。 Optionalの型を通常の型の変数に代入して(アンラップ)、ifのtrueの方の処理をしてくれます。 この一時的な変数はifのtrueの方の{}の中で使えます。 Optionalの変数がnilの場合はfalseの方の処理をしてくれます。 Optional Binding https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID333
kuwatsy

2022/09/13 03:00

追加の解説ありがとうございました。Optional型はなじみにくくてややこしそうですが、徐々にアレルギーが減ってきているところです。いただいた情報で勉強させていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問