কীভাবে @ ফেচআরকেস্ট আপডেট করবেন, যখন সুইফটইউআই-তে কোনও সম্পর্কিত সত্তা পরিবর্তন হয়?


13

একটি সুইফ্টআইআইতে Viewআমার কাছে কোনও সত্তার ডেটা এবং ওয়াইড রিলেশনশিপ সংযুক্ত সত্তার ডেটা দেখানোর Listউপর ভিত্তি করে আছে । এবং তার সঠিকভাবে আপডেট করা হয়, যখন আমি একটি নতুন যোগ একটি নতুন সংশ্লিষ্ট মাধ্যমিক সত্তা সঙ্গে সত্তা।@FetchRequestPrimarySecondaryViewListPrimary

সমস্যাটি হ'ল, আমি যখন সংযুক্ত Secondaryআইটেমটি বিশদ দৃষ্টিতে আপডেট করি তখন ডাটাবেস আপডেট হয়, তবে পরিবর্তনগুলি Primaryতালিকাতে প্রতিফলিত হয় না । স্পষ্টতই, @FetchRequestঅন্য ভিউ-র পরিবর্তনের ফলে এগুলি ট্রিগার হয় না।

এর পরে আমি যখন প্রাথমিক ভিউতে কোনও নতুন আইটেম যুক্ত করি তখন পূর্ববর্তী পরিবর্তিত আইটেমটি শেষ পর্যন্ত আপডেট হয়।

কার্যকারণ হিসাবে, আমি অতিরিক্তভাবে Primaryসত্ত্বার একটি বৈশিষ্ট্য আপডেট করে বিশদটি পরিবর্তনগুলিতে সঠিকভাবে প্রচার করি Primary

আমার প্রশ্নটি: আমি কীভাবে @FetchRequestsসুইফটইউআই কোর ডেটার সাথে সম্পর্কিত সমস্ত বিষয়ে একটি আপডেট জোর করতে পারি ? বিশেষত, যখন আমার সম্পর্কিত সংস্থাগুলিতে সরাসরি কোনও প্রবেশাধিকার নেই @Fetchrequests?

তথ্য কাঠামো

import SwiftUI

extension Primary: Identifiable {}

// Primary View

struct PrimaryListView: View {
    @Environment(\.managedObjectContext) var context

    @FetchRequest(
        entity: Primary.entity(),
        sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
    )
    var fetchedResults: FetchedResults<Primary>

    var body: some View {
        List {
            ForEach(fetchedResults) { primary in
                NavigationLink(destination: SecondaryView(primary: primary)) {
                VStack(alignment: .leading) {
                    Text("\(primary.primaryName ?? "nil")")
                    Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
                }
                }
            }
        }
        .navigationBarTitle("Primary List")
        .navigationBarItems(trailing:
            Button(action: {self.addNewPrimary()} ) {
                Image(systemName: "plus")
            }
        )
    }

    private func addNewPrimary() {
        let newPrimary = Primary(context: context)
        newPrimary.primaryName = "Primary created at \(Date())"
        let newSecondary = Secondary(context: context)
        newSecondary.secondaryName = "Secondary built at \(Date())"
        newPrimary.secondary = newSecondary
        try? context.save()
    }
}

struct PrimaryListView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        return NavigationView {
            PrimaryListView().environment(\.managedObjectContext, context)
        }
    }
}

// Detail View

struct SecondaryView: View {
    @Environment(\.presentationMode) var presentationMode

    var primary: Primary

    @State private var newSecondaryName = ""

    var body: some View {
        VStack {
            TextField("Secondary name:", text: $newSecondaryName)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
                .onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
            Button(action: {self.saveChanges()}) {
                Text("Save")
            }
            .padding()
        }
    }

    private func saveChanges() {
        primary.secondary?.secondaryName = newSecondaryName

        // TODO: ❌ workaround to trigger update on primary @FetchRequest
        primary.managedObjectContext.refresh(primary, mergeChanges: true)
        // primary.primaryName = primary.primaryName

        try? primary.managedObjectContext?.save()
        presentationMode.wrappedValue.dismiss()
    }
}

সাহায্যকারী নয়, দুঃখিত। তবে আমি এই একই সমস্যা নিয়ে চলেছি। আমার বিস্তারিত দর্শনটিতে নির্বাচিত প্রাথমিক অবজেক্টের একটি উল্লেখ রয়েছে। এটি গৌণ বস্তুর একটি তালিকা দেখায়। সমস্ত সিআরইউডি ফাংশন কোর ডেটাতে সঠিকভাবে কাজ করে তবে ইউআইতে প্রতিফলিত হয় না। এই সম্পর্কে আরও তথ্য পেতে চাই।
পিজে রাশটন

আপনি ব্যবহার চেষ্টা করেছেন ObservableObject?
kdion4891

আমি @ObmittedObject var প্রাথমিক: বিস্তারিত দর্শনে প্রাথমিক ব্যবহার করার চেষ্টা করেছি। তবে পরিবর্তনগুলি প্রাথমিক ভিউতে ফিরে প্রচার করে না।
বিজার্ন বি।

উত্তর:


15

আপনার এমন প্রকাশক দরকার যা প্রসঙ্গে পরিবর্তনের বিষয়ে ইভেন্ট তৈরি করবে এবং প্রাথমিক দর্শনে কিছু রাজ্য পরিবর্তনশীল সেই প্রকাশকের কাছ থেকে ইভেন্টটি পুনর্নির্মাণের জন্য পুনর্নির্মাণের জন্য বাধ্য করবে।
গুরুত্বপূর্ণ: রাষ্ট্রের ভেরিয়েবলটি অবশ্যই বিল্ডার কোডে ব্যবহার করতে হবে, অন্যথায় রেন্ডারিং ইঞ্জিনটি জানতে পারে না যে কিছু পরিবর্তন হয়েছে।

আপনার কোডটির প্রভাবিত অংশের এখানে সাধারণ পরিবর্তন রয়েছে যা এটি আপনার প্রয়োজনীয় আচরণ করে।

@State private var refreshing = false
private var didSave =  NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)

var body: some View {
    List {
        ForEach(fetchedResults) { primary in
            NavigationLink(destination: SecondaryView(primary: primary)) {
                VStack(alignment: .leading) {
                    // below use of .refreshing is just as demo,
                    // it can be use for anything
                    Text("\(primary.primaryName ?? "nil")" + (self.refreshing ? "" : ""))
                    Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
                }
            }
            // here is the listener for published context event
            .onReceive(self.didSave) { _ in
                self.refreshing.toggle()
            }
        }
    }
    .navigationBarTitle("Primary List")
    .navigationBarItems(trailing:
        Button(action: {self.addNewPrimary()} ) {
            Image(systemName: "plus")
        }
    )
}

এখানে আশা করা হচ্ছে অ্যাপল ভবিষ্যতে মূল ডেটা <-> সুইফটইউআই ইন্টিগ্রেশনকে উন্নত করবে। প্রদত্ত সেরা উত্তরে অনুগ্রহ প্রদান করা। ধন্যবাদ এস্পেরি
ড্যারেল রুট

আপনার উত্তর করার জন্য আপনাকে ধন্যবাদ! তবে @ ফ্যাচআরকুয়েস্টের ডাটাবেসের পরিবর্তনের বিষয়ে প্রতিক্রিয়া দেখা উচিত। আপনার সমাধানের সাথে, ভিউটি ডেটাবেসের প্রতিটি সেভের সাথে আপডেট করা হবে, এতে জড়িত আইটেম নির্বিশেষে। আমার প্রশ্ন ছিল কিভাবে ডেটাবেস সম্পর্কের সাথে জড়িত পরিবর্তনের বিষয়ে প্রতিক্রিয়া জানাতে @ ফ্যাচআরকুয়েস্ট পাবেন? আপনার সমাধানটির জন্য @ ফ্যাচআরকুয়েস্টের সমান্তরালে দ্বিতীয় গ্রাহক (নোটিফিকেশন সেন্টার) প্রয়োজন। এছাড়াও একটি অতিরিক্ত জাল ট্রিগার ব্যবহার করতে হবে `+ (স্বতঃআফ্রেশিং?" ":" ")` ` হয়তো @Fetchrequest নিজেই উপযুক্ত সমাধান নয়?
বি

হ্যাঁ, আপনি ঠিক বলেছেন, তবে উদাহরণস্বরূপ তৈরি করার অনুরোধটি ইদানীং করা পরিবর্তনগুলি দ্বারা প্রভাবিত হয় না, এজন্য এটি আপডেট / পুনরায় সংশোধন হয় না। বিভিন্ন আনতে অনুরোধের মানদণ্ড বিবেচনা করার কোনও কারণ থাকতে পারে, তবে এটি ভিন্ন প্রশ্ন।
Asperi

2
@ এস্পেরি আমি আপনার উত্তরটি গ্রহণ করি। যেমনটি আপনি বলেছেন, সমস্যাটি কোনওভাবে রেন্ডারিং ইঞ্জিনের সাথে কোনও পরিবর্তন সনাক্ত করতে পারে। পরিবর্তিত অবজেক্টের রেফারেন্স ব্যবহার করা যথেষ্ট নয়। একটি পরিবর্তিত পরিবর্তনশীল অবশ্যই একটি ভিউতে ব্যবহার করা উচিত। শরীরের যে কোনও অংশে। এমনকি তালিকার একটি পটভূমিতে ব্যবহৃত কাজ করবে। আমি RefreshView(toggle: Bool)এর শরীরে একটি একক খালি ভিউ সহ একটি ব্যবহার করি । ব্যবহার List {...}.background(RefreshView(toggle: self.refreshing))কাজ করবে।
বিজার্ন বি

তালিকার রিফ্রেশ / রিফ্যাচ জোর করার আরও ভাল উপায় আমি খুঁজে পেয়েছি, এটি সুইফটআইআইতে সরবরাহ করা হয়েছে : সমস্ত কোর ডেটা সত্তা এন্ট্রি মুছে ফেলার পরে তালিকা স্বয়ংক্রিয়ভাবে আপডেট হয় না । শুধু ক্ষেত্রে।
আস্পেরি

1

আমি এইরূপে বিশদ দৃষ্টিতে প্রাথমিক বিষয়টিকে স্পর্শ করার চেষ্টা করেছি:

// TODO: ❌ workaround to trigger update on primary @FetchRequest

if let primary = secondary.primary {
   secondary.managedObjectContext?.refresh(primary, mergeChanges: true)
}

তারপরে প্রাথমিক তালিকা আপডেট হবে। তবে বিস্তারিত দর্শনটি প্যারেন্ট অবজেক্ট সম্পর্কে জানতে হবে। এটি কাজ করবে, তবে এটি সম্ভবত সুইফটইউআই বা সম্মিলনের উপায় নয় ...

সম্পাদনা:

উপরের কাজের ভিত্তিতে আমি গ্লোবাল সেভ (ম্যানেজডঅবজেক্ট :) ফাংশন দিয়ে আমার প্রকল্পটি পরিবর্তন করেছি। এটি সমস্ত সম্পর্কিত সত্তাকে স্পর্শ করবে, এইভাবে সমস্ত প্রাসঙ্গিক @ ফেচআরকুয়েস্ট আপডেট করে।

import SwiftUI
import CoreData

extension Primary: Identifiable {}

// MARK: - Primary View

struct PrimaryListView: View {
    @Environment(\.managedObjectContext) var context

    @FetchRequest(
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Primary.primaryName, ascending: true)]
    )
    var fetchedResults: FetchedResults<Primary>

    var body: some View {
        print("body PrimaryListView"); return
        List {
            ForEach(fetchedResults) { primary in
                NavigationLink(destination: SecondaryView(secondary: primary.secondary!)) {
                    VStack(alignment: .leading) {
                        Text("\(primary.primaryName ?? "nil")")
                        Text("\(primary.secondary?.secondaryName ?? "nil")")
                            .font(.footnote).foregroundColor(.secondary)
                    }
                }
            }
        }
        .navigationBarTitle("Primary List")
        .navigationBarItems(trailing:
            Button(action: {self.addNewPrimary()} ) {
                Image(systemName: "plus")
            }
        )
    }

    private func addNewPrimary() {
        let newPrimary = Primary(context: context)
        newPrimary.primaryName = "Primary created at \(Date())"
        let newSecondary = Secondary(context: context)
        newSecondary.secondaryName = "Secondary built at \(Date())"
        newPrimary.secondary = newSecondary
        try? context.save()
    }
}

struct PrimaryListView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        return NavigationView {
            PrimaryListView().environment(\.managedObjectContext, context)
        }
    }
}

// MARK: - Detail View

struct SecondaryView: View {
    @Environment(\.presentationMode) var presentationMode

    var secondary: Secondary

    @State private var newSecondaryName = ""

    var body: some View {
        print("SecondaryView: \(secondary.secondaryName ?? "")"); return
        VStack {
            TextField("Secondary name:", text: $newSecondaryName)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
                .onAppear {self.newSecondaryName = self.secondary.secondaryName ?? "no name"}
            Button(action: {self.saveChanges()}) {
                Text("Save")
            }
            .padding()
        }
    }

    private func saveChanges() {
        secondary.secondaryName = newSecondaryName

        // save Secondary and touch Primary
        (UIApplication.shared.delegate as! AppDelegate).save(managedObject: secondary)

        presentationMode.wrappedValue.dismiss()
    }
}

extension AppDelegate {
    /// save and touch related objects
    func save(managedObject: NSManagedObject) {

        let context = persistentContainer.viewContext

        // if this object has an impact on related objects, touch these related objects
        if let secondary = managedObject as? Secondary,
            let primary = secondary.primary {
            context.refresh(primary, mergeChanges: true)
            print("Primary touched: \(primary.primaryName ?? "no name")")
        }

        saveContext()
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.