Результаты @FetchRequest не обновляются в SwiftUI, когда источником изменений является расширение общего ресурса - PullRequest
6 голосов
/ 08 мая 2020

По @FetchRequest есть много вопросов с ответами, которые предоставляют обходные пути для принуждения SwiftUI к перерисовке представления. Я не могу найти ни одного ответа на этот вопрос.

Мое приложение использует расширение общего ресурса для вставки нового элемента в мою модель CoreData (sqlite). Платформа используется совместно основным приложением и целевым расширением общего ресурса. Основной SwiftUI ContentView в моем приложении использует свойство @FetchRequest для отслеживания содержимого базы данных и обновления представления:

struct ContentView: View {
    @FetchRequest(fetchRequest: MyObj.allFetchRequest()) var allObjs: FetchedResults<MyObj>
    ...
}

моя xcdatamodel расширяется с помощью:

    public static func allFetchRequest() -> NSFetchRequest<MyObj> {
        let request: NSFetchRequest<MyObj> = MyObj.fetchRequest()
        // update request to sort by name
        request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        return request
    }

Расширение общего доступа добавляет новый элемент, используя:

let newObj = MyObj(context: context)
newObj.name = "new name"
try! context.save()

После добавления нового элемента с помощью расширения общего доступа, когда я возвращаюсь в свое приложение, переключая приложения на устройстве, SceneDelegate обнаруживает sceneDidBecomeActive. В этой функции я могу вручную запустить запрос на выборку и увидеть, что новый элемент был добавлен. Я даже могу использовать ответы на другие вопросы SO, чтобы заставить пользовательский интерфейс перестроить в ContentView (путем переключения Bool, используемого в построителе при получении уведомления), но в любом случае, когда пользовательский интерфейс перестраивается, allObjs не содержит обновленные результаты из базы данных.

В моем sceneDidBecomeActive я пробовал множество комбинаций кода, чтобы попытаться обновить контекст последним содержимым базы данных таким образом, чтобы @FetchRequest его увидел. Данные есть, и мой управляемый контекст может видеть эти новые данные, потому что запрос ручной выборки возвращает новый элемент, но @FetchRequest всегда устарел, предотвращая перестроение пользовательского интерфейса. В обоих местах я распечатал контекст и уверен, что это тот же контекст. Я пробовал:

  • context.reset()
  • context.refreshAllObjects()
  • context.save()

Я в растерянности относительно того, почему @FetchRequest не обновляет свои значения, когда ContentView redr aws.

Как я могу заставить @FetchRequest обновить свои объекты и вызвать перестройку SwiftUI после того, как мое расширение общего ресурса добавляет новый объект в базу данных?

(Xcode 11.4.1, Swift 5, цель: iOS 13.1)

редактировать:

В моем ContentView, я печатаю allObjs переменные, их состояние, адрес в памяти контекста, и c. Я сравниваю это с новой выборкой из контекста, и все значения для существующих объектов являются то же самое, за исключением того, что новая выборка показывает только что добавленный объект, тогда как allObjs не содержит новый объект. Совершенно ясно, что объект @FetchRequest не обновляется.

изменить 2:

Я попытался создать собственный класс ObservableObject со свойством @Published и явным методом update(), а затем использовать его как @ObservedObject дюйм ContentView. Это работает.

class MyObjList: ObservableObject {
    static let shared = MyObjList()
    @Published var allObjs: [MyObj]!

    init() {
        update()
    }

    func update() {
        allObjs = try! DataStore.persistentContainer.viewContext.fetch(MyObj.allFetchRequest())
    }
}

Затем в ContentView:

@EnvironmentObject var allObjs: MyObjList

Это помогает подтвердить, что данные есть, просто они не обновляются должным образом с помощью @FetchRequest. Это функциональный обходной путь.

Ответы [ 2 ]

0 голосов
/ 13 мая 2020

Я использую метод из упомянутого комментария ответ . Я могу разрешить ManagedID из URI. Но не могу получить объект по existingObject(with:). Возвращается только ноль. И метод object(with:) превратит sh приложение.

020-05-13 18:34:54.324914+0800 SwiftUI-FetchRequest[7802:388610] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff23e39f0e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x00007fff50ad79b2 objc_exception_throw + 48
    2   CoreData                            0x00007fff23a0b0a9 -[_NSCoreManagedObjectID _isPersistentStoreAlive] + 0
    3   CoreData                            0x00007fff239f704a -[NSManagedObjectContext objectWithID:] + 555
    4   SwiftUI-FetchRequest                0x000000010e922933 $s20SwiftUI_FetchRequest16ContentViewModelCACycfcyypSgcfU_ + 1827
    5   SwiftUI-FetchRequest                0x000000010e922d21 $sypSgIegn_yXlSgIeyBy_TR + 161
    6   MMWormhole                          0x000000010ebbfe34 __61-[MMWormhole notifyListenerForMessageWithIdentifier:message:]_block_invoke + 36
    7   libdispatch.dylib                   0x000000010ebe1f11 _dispatch_call_block_and_release + 12
    8   libdispatch.dylib                   0x000000010ebe2e8e _dispatch_client_callout + 8
    9   libdispatch.dylib                   0x000000010ebf0d97 _dispatch_main_queue_callback_4CF + 1149
    10  CoreFoundation                      0x00007fff23d9da89 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    11  CoreFoundation                      0x00007fff23d985d9 __CFRunLoopRun + 2041
    12  CoreFoundation                      0x00007fff23d97ac4 CFRunLoopRunSpecific + 404
    13  GraphicsServices                    0x00007fff38b2fc1a GSEventRunModal + 139
    14  UIKitCore                           0x00007fff48bc7f80 UIApplicationMain + 1605
    15  SwiftUI-FetchRequest                0x000000010e920c3b main + 75
    16  libdyld.dylib                       0x00007fff519521fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Контекст уже обновлен sh методом reset(). И вручную запрос последней записи может быть разрешен правильно. Я думаю, что это ошибка SwiftUI.framework. И отправьте вопрос через Feedback.app.

0 голосов
/ 11 мая 2020

Попробуйте переместить, если это применимо для вашего приложения, создание ContentView в приложении go на передний план (таким образом, запрос на выборку будет воссоздан с нуля)

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    // leave here only window creation
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        self.window = window
    }
}

// ... other code here

func sceneWillEnterForeground(_ scene: UIScene) {

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let contentView = ContentView().environment(\.managedObjectContext, context)
    let controller = UIHostingController(rootView: contentView)

    window?.rootViewController = controller
    window?.makeKeyAndVisible()
}
...