Я не вижу ничего сразу "неправильного", поэтому давайте немного отладим.
- Установите точку останова в
applicationDidFinish...
сразу после охраны. - Установите точку остановапри создании
privateContext
.
Который срабатывает первым?
Где находится функция registerUser
?В контроллере представления?Я надеюсь, что нет:)
точка останова сразу после того, как мое охранное заявление срабатывает первым.И да, моя функция registerUser действительно находится внутри ViewController.
Помещение сетевого кода в контроллеры представления - это запах кода.Контроллеры вида имеют одну работу, управляют своими видами.Сбор данных принадлежит контроллеру постоянства;например, расширение вашего NSPersistentContainer
и размещение там кода для сбора данных.
Однако здесь проблема не в этом, а только запах кода.
Следующий тест.
Передается ли ваш постоянный контейнер и / или viewContext в контроллер представления и сохраняется ли он?
Уничтожается ли контроллер представления до того, как сработает блок?
Чтобы проверить этоЯ бы поставил утверждение перед Alamofire.request
и произвел бы сбой, если контекст nil
:
NSAssert(self.managedObjectContext != nil, @"Main context is nil in the view controller");
Я бы также поставил эту же строку кода непосредственно перед:
privateContext.parent = self.managedObjectContext
Беги снова.Что происходит?
Я выполнил тест, как вы описали, и я получаю ошибку: Поток 1: Утверждение не выполнено: Основной контекст равен nil в контроллере представления
Какойутверждение рухнуло?(вероятно, следует немного изменить текст ...)
Если это первый текст, то ваш контроллер представления не получает viewContext
.
Если это второй, то viewContext
возвращается к nil
до выполнения блока.
Измените свои предположения соответствующим образом.
обнаружил кое-что, что имеет отношение к этому: если я помещу кнопку для вызова функции registerUser () по усмотрению пользователя вместо вызова ее непосредственно изМетод viewDidLoad (), сбоев нет, код работает нормально, а MOC имеет значение
. Это подводит меня к теории, что ваш registerUser()
вызывался до вашего viewDidLoad()
.Вы можете проверить это, установив точку останова в обоих и посмотреть, какая из них срабатывает первой.Если ваш registerUser()
срабатывает первым, посмотрите на стек и посмотрите, как он вызывается.
Если он выстрелит после вашего viewDidLoad()
, тогда установите точку останова для свойства context и посмотрите, чтовозвращает значение nil
.
Итак, если я удалю эту строку, как мне установить свойство MOC на моем RootViewController с помощью внедрения зависимостей?
Строкапрямо перед тем, как здесь ключ.
let storyboard = self.window?.rootViewController?.storyboard
Здесь вы получаете ссылку на storyboard
от rootViewController
, который уже создан и связан с window
вашего приложения.
Поэтому вы могли бы изменить логику на:
(self.window?.rootViewController as? ViewController).managedObjectContext = container.viewContext
Хотя я бы очистил это и применил немного логики nil
:)
Проблема IСледует понимать, что MOC в RootViewController
используется ДО того, как MOC возвращается из замыкания и устанавливается в AppDelegate
.Что мне здесь делать?
Это общая проблема синхронного (UI) и асинхронного (персистентности).В идеале ваш пользовательский интерфейс должен ждать, пока постоянство загружается.Если вы загрузите постоянные хранилища, а затем завершите пользовательский интерфейс после загрузки хранилищ, это решит эту проблему.
Без миграции мы обычно говорим здесь мс, а не секунды.
НО ... Вы хотите, чтобы один и тот же код обрабатывал загрузку пользовательского интерфейса, будь то миллисекунды или секунды.Как вы решаете, что решать вам (дизайнерское решение).Одним из примеров является продолжение представления загрузки до тех пор, пока уровень персистентности не будет готов, а затем переход на другой уровень.
Если бы вы сделали это, вы могли бы слегка изменить представление загрузки, если происходит миграция, чтобы проинформировать пользователя о том, почемутак долго.