ListMonitor doesn't return initial dataset if created right after addPersistentStoreAndWait() #38

Closed
opened 2025-12-29 15:22:47 +01:00 by adam · 12 comments
Owner

Originally created by @jamesbebbington on GitHub (Feb 8, 2016).

Having implemented a sectioned ListMonitor as the datasource for my controller, I'm finding that it only returns records when the underlying store content changes.

I boot my app, insert data into the store and the table view is updated accordingly. But when I restart the app, the table view is empty. However, a CoreStore.fetchAll reveals that there is data in the store.

Am I doing anything obviously wrong?

import UIKit
import CoreStore

class ProjectsController: UITableViewController, ListSectionObserver, TableViewDisabling {

    // MARK: Properties

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    enum Segue: String {
        case ShowProject
    }

    let projects: ListMonitor<Project> = {
        return CoreStore.monitorSectionedList(
            From(Project),
            SectionBy("state") { (sectionName) -> String? in
                sectionName?.capitalizedString
            },
            Where(true),
            OrderBy(.Ascending("state"), .Ascending("name"))
        )
    }()

    // MARK: NSObject

    deinit {
        projects.removeObserver(self)
    }


    // MARK: UIViewController

    override func viewDidLoad() {
        super.viewDidLoad()

        projects.addObserver(self)

        self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)

        appDelegate.syncManager.onComplete.listen(self) {

            // N.B. Refresh control can cause UI test weirdness
            // See: https://forums.developer.apple.com/thread/6503
            if let refreshControl = self.refreshControl where refreshControl.refreshing {
                refreshControl.endRefreshing()
            }
        }

        // Prints records to the console as expected…
        print("projects", CoreStore.fetchAll(From(Project)))

    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        // Deselect row on Back gesture. See: http://stackoverflow.com/a/23895557/171144
        if let indexPath = tableView.indexPathForSelectedRow {
            tableView.deselectRowAtIndexPath(indexPath, animated: animated)
        }

    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        guard let identifier = Segue(rawValue: segue.identifier!) else { return }

        switch identifier {
        case .ShowProject:
            guard let indexPath = tableView.indexPathForSelectedRow else { return }
            let destination = segue.destinationViewController as! ProjectController
            let project = projects[indexPath]
            destination.project = project
        }

    }


    // MARK: UITableViewController

    func refresh(sender: AnyObject) {
        if let rootController = self.parentViewController as? RootController {
            rootController.sync()
        }
    }


    // MARK: UITableViewDataSource

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return projects.numberOfSections()
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return projects.numberOfObjectsInSection(section)
    }

    // Section headers
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let sectionInfo = self.projects.sectionInfoAtIndex(section)
        return sectionInfo.name
    }

    // Project cells
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("ProjectCell", forIndexPath: indexPath) as UITableViewCell
        let project = projects[indexPath]
        cell.textLabel!.text = project.name
        return cell
    }

    override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
        // Don't display section index letters down right-hand side of table
        return nil
    }


    // MARK: ListObserver

    func listMonitorWillChange(monitor: ListMonitor<Project>) {
        self.tableView.beginUpdates()
    }

    func listMonitorDidChange(monitor: ListMonitor<Project>) {
        self.tableView.endUpdates()
    }

    func listMonitorWillRefetch(monitor: ListMonitor<Project>) {
        self.setTableEnabled(false)
    }

    func listMonitorDidRefetch(monitor: ListMonitor<Project>) {
        self.tableView.reloadData()
        self.setTableEnabled(true)
    }


    // MARK: ListObjectObserver

    func listMonitor(monitor: ListMonitor<Project>, didInsertObject object: Project, toIndexPath indexPath: NSIndexPath) {
        self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }

    func listMonitor(monitor: ListMonitor<Project>, didDeleteObject object: Project, fromIndexPath indexPath: NSIndexPath) {
        self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }

    func listMonitor(monitor: ListMonitor<Project>, didUpdateObject object: Project, atIndexPath indexPath: NSIndexPath) {
        if let cell = self.tableView.cellForRowAtIndexPath(indexPath) {
            let project = projects[indexPath]
            cell.textLabel!.text = project.name
        }
    }

    func listMonitor(monitor: ListMonitor<Project>, didMoveObject object: Project, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
        self.tableView.deleteRowsAtIndexPaths([fromIndexPath], withRowAnimation: .Automatic)
        self.tableView.insertRowsAtIndexPaths([toIndexPath], withRowAnimation: .Automatic)
    }


    // MARK: ListSectionObserver

    func listMonitor(monitor: ListMonitor<Project>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
        self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic)
    }

    func listMonitor(monitor: ListMonitor<Project>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
        self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic)
    }

}

Incidentally, the ListObserver demos in the CoreStoreDemo app initially appear empty too, which was unexpected (are they meant to start empty?) and led me to wonder if CoreStore might be at fault:

screen shot 2016-02-08 at 17 33 00

Thanks.

Originally created by @jamesbebbington on GitHub (Feb 8, 2016). Having implemented a sectioned `ListMonitor` as the datasource for my controller, I'm finding that it only returns records when the underlying store content changes. I boot my app, insert data into the store and the table view is updated accordingly. But when I restart the app, the table view is empty. However, a `CoreStore.fetchAll` reveals that there is data in the store. Am I doing anything obviously wrong? ``` swift import UIKit import CoreStore class ProjectsController: UITableViewController, ListSectionObserver, TableViewDisabling { // MARK: Properties let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate enum Segue: String { case ShowProject } let projects: ListMonitor<Project> = { return CoreStore.monitorSectionedList( From(Project), SectionBy("state") { (sectionName) -> String? in sectionName?.capitalizedString }, Where(true), OrderBy(.Ascending("state"), .Ascending("name")) ) }() // MARK: NSObject deinit { projects.removeObserver(self) } // MARK: UIViewController override func viewDidLoad() { super.viewDidLoad() projects.addObserver(self) self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged) appDelegate.syncManager.onComplete.listen(self) { // N.B. Refresh control can cause UI test weirdness // See: https://forums.developer.apple.com/thread/6503 if let refreshControl = self.refreshControl where refreshControl.refreshing { refreshControl.endRefreshing() } } // Prints records to the console as expected… print("projects", CoreStore.fetchAll(From(Project))) } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) // Deselect row on Back gesture. See: http://stackoverflow.com/a/23895557/171144 if let indexPath = tableView.indexPathForSelectedRow { tableView.deselectRowAtIndexPath(indexPath, animated: animated) } } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { guard let identifier = Segue(rawValue: segue.identifier!) else { return } switch identifier { case .ShowProject: guard let indexPath = tableView.indexPathForSelectedRow else { return } let destination = segue.destinationViewController as! ProjectController let project = projects[indexPath] destination.project = project } } // MARK: UITableViewController func refresh(sender: AnyObject) { if let rootController = self.parentViewController as? RootController { rootController.sync() } } // MARK: UITableViewDataSource override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return projects.numberOfSections() } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return projects.numberOfObjectsInSection(section) } // Section headers override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let sectionInfo = self.projects.sectionInfoAtIndex(section) return sectionInfo.name } // Project cells override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("ProjectCell", forIndexPath: indexPath) as UITableViewCell let project = projects[indexPath] cell.textLabel!.text = project.name return cell } override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? { // Don't display section index letters down right-hand side of table return nil } // MARK: ListObserver func listMonitorWillChange(monitor: ListMonitor<Project>) { self.tableView.beginUpdates() } func listMonitorDidChange(monitor: ListMonitor<Project>) { self.tableView.endUpdates() } func listMonitorWillRefetch(monitor: ListMonitor<Project>) { self.setTableEnabled(false) } func listMonitorDidRefetch(monitor: ListMonitor<Project>) { self.tableView.reloadData() self.setTableEnabled(true) } // MARK: ListObjectObserver func listMonitor(monitor: ListMonitor<Project>, didInsertObject object: Project, toIndexPath indexPath: NSIndexPath) { self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) } func listMonitor(monitor: ListMonitor<Project>, didDeleteObject object: Project, fromIndexPath indexPath: NSIndexPath) { self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) } func listMonitor(monitor: ListMonitor<Project>, didUpdateObject object: Project, atIndexPath indexPath: NSIndexPath) { if let cell = self.tableView.cellForRowAtIndexPath(indexPath) { let project = projects[indexPath] cell.textLabel!.text = project.name } } func listMonitor(monitor: ListMonitor<Project>, didMoveObject object: Project, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { self.tableView.deleteRowsAtIndexPaths([fromIndexPath], withRowAnimation: .Automatic) self.tableView.insertRowsAtIndexPaths([toIndexPath], withRowAnimation: .Automatic) } // MARK: ListSectionObserver func listMonitor(monitor: ListMonitor<Project>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic) } func listMonitor(monitor: ListMonitor<Project>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Automatic) } } ``` Incidentally, the ListObserver demos in the CoreStoreDemo app initially appear empty too, which was unexpected (are they meant to start empty?) and led me to wonder if CoreStore might be at fault: <img width="432" alt="screen shot 2016-02-08 at 17 33 00" src="https://cloud.githubusercontent.com/assets/5934/12893994/3e942fac-ce8c-11e5-8167-a9ad789ac58f.png"> Thanks.
adam added the pending label 2025-12-29 15:22:47 +01:00
adam closed this issue 2025-12-29 15:22:47 +01:00
Author
Owner

@JohnEstropia commented on GitHub (Feb 9, 2016):

I can't find anything wrong in your code, architectually sound actually, but you can try check the following:

  • Is your tableView an IBOutlet? Tableviews automatically reload on layout, but you may want to try calling tableView.reloadData() at the end of viewDidLoad or viewWillAppear
  • put breakpoints on numberOfSections and numberOfRowsInSections and check the timing if called after viewDidLoad, and what the value being returned are
  • should be unrelated, but you can ommit the Where(true) clause

The demo app starts with a clean database, but after you add some items and try to restart the app, the previous entries should be visible.

@JohnEstropia commented on GitHub (Feb 9, 2016): I can't find anything wrong in your code, architectually sound actually, but you can try check the following: - Is your tableView an IBOutlet? Tableviews automatically reload on layout, but you may want to try calling `tableView.reloadData()` at the end of viewDidLoad or viewWillAppear - put breakpoints on numberOfSections and numberOfRowsInSections and check the timing if called after viewDidLoad, and what the value being returned are - should be unrelated, but you can ommit the `Where(true)` clause The demo app starts with a clean database, but after you add some items and try to restart the app, the previous entries should be visible.
Author
Owner

@jamesbebbington commented on GitHub (Feb 9, 2016):

I can't find anything wrong in your code, architectually sound actually,

Thanks, it's my first Cocoa app so that's good to hear.

but you can try check the following:

  • Is your tableView an IBOutlet? Tableviews automatically reload on layout, but you may want to try calling tableView.reloadData() at the end of viewDidLoad or viewWillAppear

It's not an IBOutlet but adding it as one and calling reloadData() doesn't help.

  • put breakpoints on numberOfSections and numberOfRowsInSections and check the timing if called after viewDidLoad, and what the value being returned are

Ah yes, I did try this, and should have mentioned it in my previous post. Adding breakpoints to the UITableViewDataSource methods reveals that numberOfSectionsInTableView is called six times, and each time projects.numberOfSections() returns 0, where I would expect it to return 2 for my dataset (as it does after an initial data fetch):

screen shot 2016-02-09 at 10 24 18

This led me to suspect the sectioned list monitor might be playing up, but converting it to a plain list monitor makes no difference:

let projects: ListMonitor<Project> = {
    return CoreStore.monitorList(
        From(Project),
        OrderBy(.Ascending("state"), .Ascending("name"))
    )
}()
  • should be unrelated, but you can ommit the Where(true) clause

Ah thanks, I wasn't sure about that.

The demo app starts with a clean database, but after you add some items and try to restart the app, the previous entries should be visible.

OK, thanks for the clarification, it wasn't obvious to me from the source what the expected behaviour was.


Ah ha! I definitely I tried adding this in various places before and it had no effect, but a projects.refetch() at the end of viewDidLoad() seems to do the trick.

Can we conclude that there is something not quite right with ListMonitor, as I presume that this shouldn't be necessary?

@jamesbebbington commented on GitHub (Feb 9, 2016): > I can't find anything wrong in your code, architectually sound actually, Thanks, it's my first Cocoa app so that's good to hear. > but you can try check the following: > - Is your tableView an IBOutlet? Tableviews automatically reload on layout, but you may want to try calling tableView.reloadData() at the end of viewDidLoad or viewWillAppear It's not an `IBOutlet` but adding it as one and calling `reloadData()` doesn't help. > - put breakpoints on numberOfSections and numberOfRowsInSections and check the timing if called after viewDidLoad, and what the value being returned are Ah yes, I did try this, and should have mentioned it in my previous post. Adding breakpoints to the `UITableViewDataSource` methods reveals that `numberOfSectionsInTableView` is called six times, and each time `projects.numberOfSections()` returns `0`, where I would expect it to return `2` for my dataset (as it does after an initial data fetch): <img width="432" alt="screen shot 2016-02-09 at 10 24 18" src="https://cloud.githubusercontent.com/assets/5934/12913544/510fc32c-cf17-11e5-8415-b4cc6a98e6cd.png"> This led me to suspect the sectioned list monitor might be playing up, but converting it to a plain list monitor makes no difference: ``` swift let projects: ListMonitor<Project> = { return CoreStore.monitorList( From(Project), OrderBy(.Ascending("state"), .Ascending("name")) ) }() ``` > - should be unrelated, but you can ommit the Where(true) clause Ah thanks, I wasn't sure about that. > The demo app starts with a clean database, but after you add some items and try to restart the app, the previous entries should be visible. OK, thanks for the clarification, it wasn't obvious to me from the source what the expected behaviour was. --- Ah ha! I definitely I tried adding this in various places before and it had no effect, but a `projects.refetch()` at the end of `viewDidLoad()` seems to do the trick. Can we conclude that there is something not quite right with `ListMonitor`, as I presume that this shouldn't be necessary?
Author
Owner

@JohnEstropia commented on GitHub (Feb 9, 2016):

Thanks for sharing your debugging info! Getting it fixed by calling refetch() (it definitely shouldn't be needed) makes me think that your ListMonitor is getting initialized before the NSPersistentStore gets added completely to the stack.

  • Can you share how your addSQLiteStore...() is setup and when it gets called?
  • Is your view controller the storyboard's initial view controller? (or a child of the initial controller?)

My guess is either:

  1. You are using the aynchonous form of addSQLiteStore(...) and the view controller is created before the completion is called. Or,
  2. You are using the synchonous addSQLiteStoreAndWait(...) one but the view controller is created right after it on the same run loop. (if so, this could be be a bug in CoreStore or an undocumented behavior of NSPersistentStoreCoordinator)
@JohnEstropia commented on GitHub (Feb 9, 2016): Thanks for sharing your debugging info! Getting it fixed by calling `refetch()` (it definitely shouldn't be needed) makes me think that your ListMonitor is getting initialized before the NSPersistentStore gets added completely to the stack. - Can you share how your `addSQLiteStore...()` is setup and when it gets called? - Is your view controller the storyboard's initial view controller? (or a child of the initial controller?) My guess is either: 1. You are using the aynchonous form of `addSQLiteStore(...)` and the view controller is created before the completion is called. Or, 2. You are using the synchonous `addSQLiteStoreAndWait(...)` one but the view controller is created right after it on the same run loop. (if so, this could be be a bug in CoreStore or an undocumented behavior of NSPersistentStoreCoordinator)
Author
Owner

@jamesbebbington commented on GitHub (Feb 9, 2016):

Thanks for sharing your debugging info!

You're welcome, hopefully it'll help others who might have similar issues too.

Getting it fixed by calling refetch() (it definitely shouldn't be needed) makes me think that your ListMonitor is getting initialized before the NSPersistentStore gets added completely to the stack.

Ah, yeah that sounds like a possibility.

Can you share how your addSQLiteStore...() is setup and when it gets called?

I'm calling CoreStore.addSQLiteStoreAndWait() in applicationDidFinishLaunching(application: UIApplication).

Is your view controller the storyboard's initial view controller? (or a child of the initial controller?)

Yes, indirectly. The ProjectsController is a child of my RootController which is the initial controller.

My guess is either:

  1. You are using the aynchonous form of addSQLiteStore(...) and the view controller is created before the completion is called. Or,
  2. You are using the synchonous addSQLiteStoreAndWait(...) one but the view controller is created right after it on the same run loop. (if so, this could be be a bug in CoreStore or an undocumented behavior of NSPersistentStoreCoordinator)

Yeah, it looks like the latter is the problem. If I remove the refetch() workaround and change the ListMonitor property from a constant to a lazy var the problem goes away:

lazy var projects: ListMonitor<Project> = {
    return CoreStore.monitorSectionedList(
        From(Project),
        SectionBy("state") { (sectionName) -> String? in
            sectionName?.capitalizedString
        },
        OrderBy(.Ascending("state"), .Ascending("name"))
    )
}()
@jamesbebbington commented on GitHub (Feb 9, 2016): > Thanks for sharing your debugging info! You're welcome, hopefully it'll help others who might have similar issues too. > Getting it fixed by calling refetch() (it definitely shouldn't be needed) makes me think that your ListMonitor is getting initialized before the NSPersistentStore gets added completely to the stack. Ah, yeah that sounds like a possibility. > Can you share how your addSQLiteStore...() is setup and when it gets called? I'm calling `CoreStore.addSQLiteStoreAndWait()` in `applicationDidFinishLaunching(application: UIApplication)`. > Is your view controller the storyboard's initial view controller? (or a child of the initial controller?) Yes, indirectly. The `ProjectsController` is a child of my `RootController` which is the initial controller. > My guess is either: > 1. You are using the aynchonous form of addSQLiteStore(...) and the view controller is created before the completion is called. Or, > 2. You are using the synchonous addSQLiteStoreAndWait(...) one but the view controller is created right after it on the same run loop. (if so, this could be be a bug in CoreStore or an undocumented behavior of NSPersistentStoreCoordinator) Yeah, it looks like the latter is the problem. If I remove the `refetch()` workaround and change the `ListMonitor` property from a constant to a lazy var the problem goes away: ``` swift lazy var projects: ListMonitor<Project> = { return CoreStore.monitorSectionedList( From(Project), SectionBy("state") { (sectionName) -> String? in sectionName?.capitalizedString }, OrderBy(.Ascending("state"), .Ascending("name")) ) }() ```
Author
Owner

@JohnEstropia commented on GitHub (Feb 9, 2016):

Okay, I'll try to see if there's a way to handle this from within CoreStore. I'm not sure how to proceed so far though, as I already use NSPersistentStoreCoordinator.performBlockAndWait { ... }.

Anyway I'm glad using lazy fixed it for you. You can also skip the closure altogether and just declare it this way:

lazy var projects: ListMonitor<Project> = CoreStore.monitorSectionedList(
    From(Project),
    SectionBy("state") { (sectionName) -> String? in
        sectionName?.capitalizedString
    },
    OrderBy(.Ascending("state"), .Ascending("name"))
)
@JohnEstropia commented on GitHub (Feb 9, 2016): Okay, I'll try to see if there's a way to handle this from within CoreStore. I'm not sure how to proceed so far though, as I already use `NSPersistentStoreCoordinator.performBlockAndWait { ... }`. Anyway I'm glad using `lazy` fixed it for you. You can also skip the closure altogether and just declare it this way: ``` swift lazy var projects: ListMonitor<Project> = CoreStore.monitorSectionedList( From(Project), SectionBy("state") { (sectionName) -> String? in sectionName?.capitalizedString }, OrderBy(.Ascending("state"), .Ascending("name")) ) ```
Author
Owner

@jamesbebbington commented on GitHub (Feb 9, 2016):

Okay, I'll try to see if there's a way to handle this from within CoreStore. I'm not sure how to proceed so far though, as I already use NSPersistentStoreCoordinator.performBlockAndWait { ... }.

Sounds like a tricky one. Maybe there's some inspiration to be found in the source of Signals and its listenPast functionality.

You can also skip the closure altogether…

Ah yes, cheers and thanks for all your help on this one!

@jamesbebbington commented on GitHub (Feb 9, 2016): > Okay, I'll try to see if there's a way to handle this from within CoreStore. I'm not sure how to proceed so far though, as I already use `NSPersistentStoreCoordinator.performBlockAndWait { ... }`. Sounds like a tricky one. Maybe there's some inspiration to be found in the source of [`Signals`](https://github.com/artman/Signals) and its `listenPast` functionality. > You can also skip the closure altogether… Ah yes, cheers and thanks for all your help on this one!
Author
Owner

@JohnEstropia commented on GitHub (Feb 10, 2016):

@fractious

Sounds like a tricky one. Maybe there's some inspiration to be found in the source of Signals and its listenPast functionality.

Yup, if I find a notification to listen for it might be easy to implement on addSQLiteStore(..., completion:) (async version). My concern is if it is possible at all for the synchronous addSQLiteStoreAndWait(...) method.

I'll try to see if there's a workaround. Thanks for the detailed investigation!

@JohnEstropia commented on GitHub (Feb 10, 2016): @fractious > Sounds like a tricky one. Maybe there's some inspiration to be found in the source of Signals and its listenPast functionality. Yup, if I find a notification to listen for it might be easy to implement on `addSQLiteStore(..., completion:)` (async version). My concern is if it is possible at all for the synchronous `addSQLiteStoreAndWait(...)` method. I'll try to see if there's a workaround. Thanks for the detailed investigation!
Author
Owner

@JohnEstropia commented on GitHub (Feb 11, 2016):

@fractious Can I ask one more thing? What iOS version are you having this problem?
I can't seem to reproduce this with the demo app on iOS 9 (even if I set the Colors Demo as the initial view controller)

@JohnEstropia commented on GitHub (Feb 11, 2016): @fractious Can I ask one more thing? What iOS version are you having this problem? I can't seem to reproduce this with the demo app on iOS 9 (even if I set the Colors Demo as the initial view controller)
Author
Owner

@JohnEstropia commented on GitHub (Feb 11, 2016):

The only way I have reproduced this is to call addSQLiteStoreAndWait() right after monitorSectionedList()... Can you check again with breakpoints which gets called first in your previous setup? (without the lazy fix)

@JohnEstropia commented on GitHub (Feb 11, 2016): The only way I have reproduced this is to call `addSQLiteStoreAndWait()` right after `monitorSectionedList()`... Can you check again with breakpoints which gets called first in your previous setup? (without the `lazy` fix)
Author
Owner

@jamesbebbington commented on GitHub (Feb 11, 2016):

What iOS version are you having this problem?

I've been using iOS 9.2 in the iPhone 5s simulator.

The only way I have reproduced this is to call addSQLiteStoreAndWait() right after monitorSectionedList()... Can you check again with breakpoints which gets called first in your previous setup? (without the lazy fix)

Yeah that what appears to be happening, my ProjectsController is instantiated before applicationDidFinishLaunching() is called.

An alternative fix to lazily instantiating the ListMonitor is to move the addSQLiteStoreAndWait() to a property declaration, so that it fires first:

class AppDelegate: UIResponder {

    let persistentStore = try! CoreStore.addSQLiteStoreAndWait()

    
}

Obviously the above example will cause the app to crash if the method throws, but it could always be changed to a computed property that catches.

Is the above something you'd recommend over the lazy alternative?

@jamesbebbington commented on GitHub (Feb 11, 2016): > What iOS version are you having this problem? I've been using iOS 9.2 in the iPhone 5s simulator. > The only way I have reproduced this is to call `addSQLiteStoreAndWait()` right after `monitorSectionedList()`... Can you check again with breakpoints which gets called first in your previous setup? (without the `lazy` fix) Yeah that what appears to be happening, my `ProjectsController` is instantiated before `applicationDidFinishLaunching()` is called. An alternative fix to lazily instantiating the `ListMonitor` is to move the `addSQLiteStoreAndWait()` to a property declaration, so that it fires first: ``` swift class AppDelegate: UIResponder { let persistentStore = try! CoreStore.addSQLiteStoreAndWait() … } ``` Obviously the above example will cause the app to crash if the method `throws`, but it could always be changed to a computed property that `catch`es. Is the above something you'd recommend over the `lazy` alternative?
Author
Owner

@JohnEstropia commented on GitHub (Feb 11, 2016):

@fractious I see, so it's not a problem with Core Data after all. I'll add an assert() on ListMonitor to prevent getting created without any persistent stores.

As for your app, you can adopt the style that the demo app uses:

let projects: ListMonitor<Project> = {

        try! CoreStore.addSQLiteStoreAndWait(
            fileName: "YourFileName.sqlite",
            configuration: "YourConfiguration",
            resetStoreOnModelMismatch: true
        )

        return CoreStore.monitorSectionedList(
            From(Project),
            SectionBy("state") { (sectionName) -> String? in
                sectionName?.capitalizedString
            },
            OrderBy(.Ascending("state"), .Ascending("name"))
        )
}()

I'll close this issue as it seems to be fixable on the client side. I already added the assert on my local repository so it should be in the develop branch soon. Thanks again for the prompt responses! If there's anything else feel free to message anytime :)

@JohnEstropia commented on GitHub (Feb 11, 2016): @fractious I see, so it's not a problem with Core Data after all. I'll add an `assert()` on ListMonitor to prevent getting created without any persistent stores. As for your app, you can adopt the style that the demo app uses: ``` swift let projects: ListMonitor<Project> = { try! CoreStore.addSQLiteStoreAndWait( fileName: "YourFileName.sqlite", configuration: "YourConfiguration", resetStoreOnModelMismatch: true ) return CoreStore.monitorSectionedList( From(Project), SectionBy("state") { (sectionName) -> String? in sectionName?.capitalizedString }, OrderBy(.Ascending("state"), .Ascending("name")) ) }() ``` I'll close this issue as it seems to be fixable on the client side. I already added the `assert` on my local repository so it should be in the develop branch soon. Thanks again for the prompt responses! If there's anything else feel free to message anytime :)
Author
Owner

@jamesbebbington commented on GitHub (Feb 11, 2016):

Cheers @JohnEstropia and thanks for helping get to the bottom of this one, much appreciated.

I think I'll keep my CoreStore.addSQLiteStoreAndWait() in the AppDelegate as I've got a number of ListSectionObserver controllers so would like to keep them DRY. I'll run with non-lazy fix for now and see how that goes.

@jamesbebbington commented on GitHub (Feb 11, 2016): Cheers @JohnEstropia and thanks for helping get to the bottom of this one, much appreciated. I think I'll keep my `CoreStore.addSQLiteStoreAndWait()` in the `AppDelegate` as I've got a number of `ListSectionObserver` controllers so would like to keep them DRY. I'll run with non-`lazy` fix for now and see how that goes.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/CoreStore#38