Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Noob Question : Access to default context and NSFetchedResultsController #76

Closed
wm-j-ray opened this issue May 27, 2016 · 16 comments
Closed
Labels

Comments

@wm-j-ray
Copy link

John,

Just getting started with CoreStore, so...

If I 'needed' to use another complementary framework that needs a NSFetchedResultsController and a context for reading//writing, what is the safest way to create/access them without breaking a CoreStore dataStack set up like so:

struct DataStoreService {

    static let coreDataStack: DataStack = {

        let dataStack = DataStack(modelName: "AppName")

        do {
            try dataStack.addSQLiteStoreAndWait(
                fileName: "AppName.sqlite",
                configuration:nil,
                resetStoreOnModelMismatch: true
            )

        } catch {
            print("Failed creating dataStack with error: \(error as NSError)")
        }
        return dataStack
    }()

}

And the stack is used from an interactor like structure using something like this:

  func fetchAllWidgets() -> [WidgetsMO]
  {
    return DataStoreService.coreDataStack.fetchAll(From(WidgetsMO))!
   }

I 'think' what I'm looking for is just a way to access the correct context to used for the NSFetchedResultsController (yours ideally, so I can use CoreStore fetching and queries) and the best way to deal with CoreStore transactions. If I can't access your NSFetchedController I would create one. Monitors and Observers, with my level of knowledge about CoreStore, seem a little beyond me.

And what are the pitfalls and downside to doing something like this?

The library is https://github.com/DenHeadless/DTTableViewManager by the way (which lightens up tableViews by using a manager (https://github.com/DenHeadless/DTModelStorage) to bind the model against tableViewCells), and I envision using it primarily for reads and using the CoreStore functionality for hopefully all of the writes.

Basically, I would really like to somehow wire CoreStore and DTTableViewManager together.

I want to make sure I'm aware of all of the potential issues and pick the best approach before I go any further.

Thanx in Advance

@JohnEstropia
Copy link
Owner

The only NSManagedObjectContext exposed by CoreStore is the context held by UnsafeDataTransactions.

let transaction = CoreStore.beginUnsafe() // store this somewhere so it doesn't get deallocated
let context = transaction.internalContext // this is what you need and you're free to do dirty things to it :)

See the documentation on UnsafeDataTransaction.internalContext: https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Saving%20and%20Processing/UnsafeDataTransaction.swift#L153

@JohnEstropia
Copy link
Owner

JohnEstropia commented May 27, 2016

Also, note that this means the objects you are working on are not from the main context. Don't mix objects fetched from the DataStack to the objects fetched from the transaction

// BAD!!
let someObject = CoreStore.fetchOne(From(...))

let transaction = CoreStore.beginUnsafe() // store this somewhere so it doesn't get deallocated
let context = transaction.internalContext
let anotherObject = // fetch from transaction/context
anotherObject.someRelationship = someObject // BAD!

@JohnEstropia
Copy link
Owner

If all you need is an NSFetchedResultsController instance, CoreStore can also create one for you. See
NSFetchedResultsController+Convenience.swift

@wm-j-ray
Copy link
Author

Thanks for the help. Let me make sure I understand...

So I can call NSFetchedResultsController.createForStack I would be using the correct context. I could use that NSFetchedResultsController to drive the tableView. Can you give me an example of what that call would look like using my dataStack? (lame, I know, but thanks in advance)

And, if I update I wouldn't be able to use the default CoreStore methods even though I'm in the proper context but rather I would have to do a save() manually or an UnsafeDataTransaction commit.

@JohnEstropia
Copy link
Owner

Let me push an update so the API call is simpler. Once it's up you should be able to call with

let controller = NSFetchedResultsController.createForStack(
    CoreStore.defaultStack,
    From(MyEntity),
    SectionBy("sectionKey"), // optional
    Where("value > %@", minValue), // optional
    OrderBy(.Ascending("value")) // optional
)

If I understand how DTTableViewManager works, it's only for displaying data, correct? If so then the NSFetchedResultsController created with CoreStore should notify DTTableViewManager without any issues, and you can still make updates using any of CoreStore's transactions (not just UnsafeDataTransactions).

@JohnEstropia
Copy link
Owner

Check out version 1.6.8. Renamed createForStack() to createFor() and now accepts either DataStack or UnsafeDataTransaction

@wm-j-ray
Copy link
Author

Excellent. Thanks so much.
On to the next.

@wm-j-ray
Copy link
Author

John,

I pulled down 1.6.8 and I must be an idiot because I can't get the stub you provided to work:

let controller = NSFetchedResultsController.createForStack(
CoreStore.defaultStack,
From(MyEntity),
SectionBy("sectionKey"), // optional
Where("value > %@", minValue), // optional
OrderBy(.Ascending("value")) // optional

)

How do the optional parameters work? Why can't I do this?

let controller = NSFetchedResultsController.createForStack(
    CoreStore.defaultStack,
    From(NoteMO))

I get a missing fetchClauses in call error.

I've also tried this:

let fetchreq = NSFetchRequest(entityName: "NoteMO")
let controller = NSFetchedResultsController.createForStack(CoreStore.defaultStack, fetchRequest: fetchreq, fetchClauses: nil)

I'm not sure what I'm doing incorrectly.

@JohnEstropia
Copy link
Owner

JohnEstropia commented May 27, 2016

Sorry for the confusion, I renamed createForStack() to createFor(). The current createForStack() is deprecated and requires argument labels. Here's the correct syntax

let controller = NSFetchedResultsController.createFor(
    CoreStore.defaultStack,
    From(MyEntity),
    SectionBy("sectionKey"), // optional
    Where("value > %@", minValue), // optional
    OrderBy(.Ascending("value")) // sorting is required
)

@JohnEstropia
Copy link
Owner

By the way,

let controller = NSFetchedResultsController.createFor(
    CoreStore.defaultStack,
    From(NoteMO))

will compile, but will raise an assertion because NSFetchedResultsControllers requires sorting information. Provide an OrderBy clause.

I'll update the documentation.

@wm-j-ray
Copy link
Author

Got it and many thanks.
I'll give it a shot.

On May 27, 2016, 7:28 PM -0400, John Estropianotifications@github.com, wrote:

By the way,

letcontroller=NSFetchedResultsController.createFor( CoreStore.defaultStack, From(NoteMO))

will compile, but will raise an assertion because NSFetchedResultsControllers requires sorting information. Provide anOrderByclause.

I'll update the documentation.


You are receiving this because you modified the open/close state.
Reply to this email directly,view it on GitHub(#76 (comment)), ormute the thread(https://github.com/notifications/unsubscribe/AAvDJJWyhYrXSoRQecQnCGsunqG_q_qTks5qF34DgaJpZM4IocDM).

@wm-j-ray
Copy link
Author

OK, now I'm perplexed (and probably don't know what I'm doing)

  1. Set up the stack like so:
func startService() {

    CoreStore.defaultStack = DataStack(
        modelName: "WriteTrack"
        //migrationChain: ["MyStore", "MyStoreV2", "MyStoreV3"]
    )

    do {
        try CoreStore.addSQLiteStoreAndWait(
            fileName: "WriteTrack.sqlite",
            configuration:nil,
            resetStoreOnModelMismatch: true
        )

    } catch {
        print("Failed creating CoreStore with error: \(error as NSError)")
    }

}
  1. Fetch all records normal CoreStore way, like so:
func fetchAllNotes() -> [NoteMO]
{
    return CoreStore.fetchAll(From(NoteMO))! 
}

Returns all of the records.

  1. Try with a NSFetchedController like so:
func test() {
let controller = NSFetchedResultsController.createFor(
    CoreStore.defaultStack,
    From(NoteMO), OrderBy(.Ascending("content")))

do {
    try controller.performFetch()
} catch {
    print("Failed performing fetch with error: \(error as NSError)")
}
controller.fetchedObjects
// breakpoint here

}

And I see the controller, but there is nothing in it...

screen shot 2016-05-27 at 9 52 28 pm

@JohnEstropia
Copy link
Owner

Did you set the NSFetchedResultsControllerDelegate? NSFetchedResultsControllers are lazy, and will not do actual fetching until you access it's array or unless you have a delegate that implements controllerDidChangeContent(_:)

@JohnEstropia
Copy link
Owner

Try to print the content after performFetch()

try controller.performFetch()
print(controller.fetchedObjects)

@wm-j-ray
Copy link
Author

That was it. Thanks again and I can't tell you how much I appreciate your patience.

@JohnEstropia
Copy link
Owner

No problem! Feel free to ask anything else anytime :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants