I have an app with a simple table stored in a common sqlite database. The app has a mainview and several other views say, view1, view2, ....,viewN. From a mainview, the user go to view1 by this code section:
screen.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
[self presentModalViewController:screen animated:YES];
in view1, the user will access the database, doing something, then update the database, quite view1 back to the mainview:
[self dismissModalViewControllerAnimated:YES];
The user will do the same thing for the other views, i.e., accessing the database, doing something, updating the database, then back to the mainview.
My question is how should I organize the database in my case, using a singleton to create a common object to open the database at the mainview, then all views will access the database, updating it or each view will open the database, accessing it, then update individually or is there any other efficient way. Thank you
As you describe the structure of your app being one threaded only - using a singleton is perfectly OK. You need to open the DB only once when the app starts and make sure to close it when the app ends or even when the app goes to background (of course then you need to open the DB when return from background too)
By the way, I also tried to open and close the DB for each view - which also works fine. In this approach I also sometimes use a "dirty" flag, that is set to indicate that the DB needs updateing before closing - but that turned out to make no difference in performance.
Instead of using a singleton you may also use a class variable or declare it within your app delegate, which is often done for the cotext of core data ( where the "context" in core data is similar to the DB in your case)
What is important in whatever approach you use is that your DB will be in a consistent state all the time since your app can get "interrupted" by phone call for example.
By the way, I tend to use core data more often on iPhone if circumstances allow, since core data takes care of many of the DB issues - only saving at consistent states needs to be done explicitly. But it really depends if your data is more a big DB or just "some" persitent objects/attributes.
I suggest avoiding c level code for calling a SQLite DB - try a simple wrapper over SQLite - see https://github.com/JohnGoodstadt/EasySQLite
This uses a singleton when you 'connect' to it:
self.db = [DBController sharedDatabaseController:#"MyDB.sqlite"];
Any file can then access the shared singleton - e.g.
int personCount = [_db ExecuteScalar:#"SELECT count(*) FROM person" asInt:YES];
DataTable* table = [_db ExecuteQuery:#"SELECT firstname,lastname FROM person"];
for (NSArray* row in table.rows)
{
NSString* firstname = row[0];
NSString* firstname = row[1];
...
}
Related
I'm sorry the title may mislead you, since I'm not so good at English. Let me describe my problem as below (You may skip to the TL;DR version at the bottom of this question).
In Coredata, I design a Product entity. In app, I download products from a server. It return JSON string, I defragment it then save to CoreData.
After sometimes has passed, I search a product from that server again, having some interaction with server. Now, I call the online product XProduct. This product may not exist in CoreData, and I also don't want to save it to CoreData since it may not belong to this system (it come from other warehouse, not my current warehouse).
Assume this XProduct has the same properties as Product, but not belong to CoreData, the developer from before has designed another Object, the XProduct, and copy everything (the code) from Product. Wow. The another difference between these two is, XProduct has some method to interact with server, like: - (void)updateStock:(NSInteger)qty;
Now, I want to upgrade the Product properties, I'll have to update the XProduct also. And I have to use these two separately, like:
id product = anArrayContainsProducts[indexPath.row];
if ([product isKindOfClass:[XProduct class]] {
// Some stuff with the xproduct
}
else {
// Probably the same display to the cell.
}
TL;DR
Basically, I want to create a scenario like this:
Get data from server.
Check existed in CoreData.
2 == true => add to array (also may update some data from server).
2 == false => create object (contains same structure as NSManagedObject from JSON dictionary => add to array.
The object created in step 4 will never exist in CoreData.
Questions
How can I create an NSManagedObject without having it add to NSMangedObjectContext and make sure the app would run fine?
If 1 is not encouragement, please suggest me a better approach to this. I really don't like to duplicate so many codes like that.
Update
I was thinking about inheritance (XProduct : Product) but it still make XProduct the subclass of NSManagedObject, so I don't think that is a good approach.
There are a couple of possibilities that might work.
One is just to create the managed objects but not insert them into a context. When you create a managed object, the context argument is allowed to be nil. For example, calling insertNewObjectForEntityForName(_:inManagedObjectContext:) with no context. That gives you an instance of the managed object that's not going to be saved. They have the same lifetime as any other object.
Another is to use a second Core Data stack for these objects, with an in-memory persistent store. If you use NSInMemoryStoreType when adding the persistent store (instead of NSSQLiteStoreType), you get a complete, working Core Data stack. Except that when you save changes, they only get saved in memory. It's not really persistent, since it disappears when the app exits, but aside from that it's exactly the same as any other Core Data stack.
I'd probably use the second approach, especially if these objects have any relationships, but either should work.
i am new in core data and i created 2 tables,Night and Session. i manage to create new object of Night and new object for Session. when i try this code:
Session * session = [NSEntityDescription insertNewObjectForEntityForName:#"Session" inManagedObjectContext:[[DataManager sharedManager] managedObjectContext]];
Night * night = [NSEntityDescription insertNewObjectForEntityForName:#"Night" inManagedObjectContext:[[DataManager sharedManager] managedObjectContext]];
night.sessions = [NSSet setWithObject:session];
the session is getting into the night and the cool thing is, when i Fetch this night and can get the session for the night using:
currentNight.Seesion
But i can't see this link in the DB tables :(
UPDATE:
I mean when i write night.sessions = [NSSet setWithObject:session]; i need to see in the table DB (yes in the DB.sqlite file).
i thought that i should see some thing there ...
Core Data is not a relational Database.It makes structure of their own.It defines the Database tables structure according to your Managed Objects.For debugging you can see what queries core data is firing on sqlite.This will show you how core data is getting data from these two tables.
You have to go Product -> Edit Scheme -> Then from the left panel select Run yourApp.app and go to the main panel's Arguments Tab.
There you can add an Argument Passed On Launch.
You should add -com.apple.CoreData.SQLDebug 1
Press OK and your are all set.
Than next time it will show all the queries it running to fetch data from your tables.
It's not clear to me what your question is. But:
A context is a scratchpad. Its contents will not be moved to the persistent store until you -save:. If you drop into the filing system and inspect your persistent store outside of your app without having saved, your changes will not be recorded there.
For all of the stores the on-disk format is undefined and implementation dependent. So inspecting them outside of Core Data is not intended to show any specific result.
Anecdotally, if you're using a SQLite store then you should look for a column called Z_SESSIONS or something similar. It'll be a multivalued column. Within it will be the row IDs of all linked sessions. Core Data stores relationships with appropriately named columns and direct row IDs, which are something SQLite supplies implicitly. It does not use an explicit foreign/primary key relationship.
To emphasise the point: that's an implementation-specific of Core Data. It's not defined to be any more reliable than exactly what ARM assembly LLVM will spit out for a particular code structure. It's as helpful to have a sense of it as to know about how the CPU tends to cache, to branch predict, etc, but you shouldn't expect to be able to take the SQLite file and use it elsewhere, or in any way interact with it other than via Core Data.
I'm building a tab bar application for iPhone and i'm using Core Data with two UIManagedDocuments. In the first tab, i write the data to database and in the second i read them into UITableView with UIFetchedResultsController.
At the start of application, if i write data first, and after then i read results, it works fine. Results appear in the second tab immediately. However, if i read some data first and after then if i write something to database, results appear in second tab with considerable delay (almost 1 minutes). If is there any synchronization problem between two UIManagedObjectContexts or two UIManagedDocuments, how does it works in the first condition? And, is there any solution for this delay?
The way that you can ensure that your UIManagedDocument is up to date is to make sure you're saving your changes properly. Given the information you've shown above, I'm not really sure about how you're managing your documents or your managedObjectContexts. There are just too many factors that could be affecting this to be able to give you a 100% concrete answer.
So without knowing what your code looks like and without knowing how you're managing your context, the only thing I can do is give you what I use in my own projects. This may or may not help you, but it helped me - more times than one - when it comes to handing core data by UIManagedDocument.
When it comes to Context:
I use a singleton to manage UIManagedDocument. I do this because I don't want to have to deal with what you're talking about above - having more than one managedObjectContext. When you start dealing with multiple contexts, you have the issue where the data will not be consistent unless you manage all of your contexts properly. If you save on one but don't update the other - then your data can become out of sync. You also have to make sure that each context is working on the property thread - the Apple Docs is a great resource for understanding the whys ad hows this even matters.
The point is, though, that this is one of the biggest problems with working with UIManagedDocument that isn't as bad as when you're working with pure core data and using a SQL persistent store. The main reason that I've found is because of how UIManagedDocument actually saves to its UIDocument store. It is very unpredictable about when it wants to save. This makes knowing when your UIManagedDocument will actually persist and have your data a freaking shot in the dark. You end having to do all kinds of stupid stuff just to make sure that it is always readily available.
Considering I have a belief (that many, maybe rightfully so, believe is an ignorant belief) that working with core data is hard, and UIManagedDocument makes it easier than it would be if you didn't work with it at all. That being said, I don't like it when working with something as simple as UIManagedDocument begins to get complicated - so I use the one thing that has always kept it simple, and that is a singleton, shared-instance of a single UIManagedDocument so that I only have 1 managedObjectContext, ever, to have to work with.
When it comes to saving:
Whenever I make any significant change to a model ( Create, Update, Delete, Edit ), I always make sure to call [document updateChangeCount:UIDocumentChangeDone]; I do this because I do not use the "undo manager" (NSUndoManager) when working with UIManagedDocument. Simply because I haven't needed to yet, plus because I hate all the "workaround" garbage you have to do with it.
Working only on the Main Thread:
Whenever I do anything with my UIManagedDocument or Core Data, I always make sure its on the main thread. I think I've already said it once, but I'll say it again: working in threads is helpful when you need it, but also when you actually understand threading in general. I like working in threads, but it comes at a cost of complexity that makes me not want to work in them when it comes to core data. With that being the case, I tend to stay strictly on the main thread as this keeps things simple and easy (for me).
Saving the Document
When I absolutely need to make sure that the UIManagedDocument is "saved" ( written to disk ), I have 2 methods that I wrote and use that are always readily available for me to call: saveDocument and forceSaveDocument.
The first one ( saveDocument ) merely checks the context for changes. If it has any, it then checks to see if we have any newly inserted objects. When insertedObjects are found, it obtains the perm ids for these items. You can think of this one as a good way to ensure that your core data model is up to date, and that your managed context is in a safe state, so that when your document is actually saved, that you get everything saved in the state that it needs to be in (your ids are realized, your contexts are clean, and what you are about to save represents a state of your model once all work has been complete on it).
Its big brother, forceSaveDocument, actually calls saveDocument first. Again, to make sure that your actual model/context is saved and proper. If it returns successful ( YES ), then it will actually do the real saving and write the document to disk by means of saveToUrl.
Some Code (hopefully it helps):
Here are those 2 methods in case it helps:
-(BOOL)saveDocument {
NSManagedObjectContext *context = self.document.managedObjectContext;
if(!context.hasChanges) return YES;
NSSet *inserts = [context insertedObjects];
if([inserts count] > 0) {
NSError *error = nil;
if(![context obtainPermanentIDsForObjects:[inserts allObjects] error:&error] && error) {
[self reportError:error];
return NO;
}
}
return YES;
}
-(void)forceSaveDocument {
if( [self saveDocument] ) {
[self.document saveToURL:self.document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:self.onSaveBlock ? self.onSaveBlock : nil];
}
}
General Rules/Guidelines
Overall, these are my guidelines that I follow ( and have worked for me for about 3 years now ) when working with UIManagedDocument and Core Data. I'm sure there are better out there from guys/gals much smarter than me, but these have what I use. The benefit I get out of them is that it makes me have to worry less about managing my data and gives me more freedom to work with everything else:
Use a singleton to manage my UIManagedDocument until the need of multiple threads is absolutely necessary - then migrate over to start using multiple contexts ( i've never needed to do this yet - but then again I try to keep things simple )
Always call updateChangeCount:UIDocumentChangeDone when I make any change to a model. It is very light weight and has little impact. If anything, it will help ensure your document stays up to date and never gets too out of sync with your data.
Don't use undo manager unless you actually need it ( I have yet to need it )
Use save/ForceSave sparingly, and only when absolutely necessary (deletes are a good reason to use it. Or if you create a new item on one view controller and need it on the next one, but can't wait for core data and the document to sync up - its kind of like kicking it in the ass and saying "I object to you saving whenever you want - save now lol.. )
Final Thoughts
All of the above is my own belief and understandings. These come from a lot of research, reading, and being a pain the ass when it comes to wanting to do things right, all while keeping it simple. Anyone can write a complex solution - but I think the fundamental question is always: do you really need the complexity, or do you just need it to work so you can focus on more complex issues?
I'm sure the above is way more than you probably wanted, and may even add more questions than you have. If you need some links and resources, let me know and I'll try to throw a few together.
Either way, hope that helps.
There are several view controllers in my app where I need to sync the local contents with server using a method running in a background thread. Sometimes I need to insert data to my database on server if user has created any. The approach I am using here is to set a flag(something like isSynced = NO) on objects that I need to sync with server (there objects are in Core Data). When the syncing is complete my method will get rid of the flag(e.g. isSynced = YES) so it won't be sent again next time.
Now the problems is that the syncing method takes very long to complete(1 or 2seconds.). If now user pops out this particular view controller and swiftly comes back the previous call is still in progress and next one will be kicked off. The consequence is that there might be duplication in database.
My approach now is the make the syncing method to be called by a Singleton object:
#property (nonatomic) BOOL isSyncing;
//every time before syncing. check if object is available for syncing
if (!isSyncing) {
isSyncing = YES;
// sync server
// when complete
isSyncing = NO;
// post notification to view controller to reload table
} else {
// cancel because previous call is not finished
}
My concern is that if the call is cancelled my view controller will not be able to receive the notification is waiting for. I can fix this by posting another notification in the event of cancelation. I am wondering if this is the right to do this because I think that this problem should be pretty common in iOS development and there should be a standard way to deal with it
Your singleton approach may not be necessary. I don't see the harm in sending a database insert for each new object. You will still need to ensure each object is synched. That is, update the "isSynched" flag. Keep each object that needs to be synced in a "need to synch" list.
Then, update the "isSynced" flag by performing a background query on the database to check if the object exits. Then, use the result of the query to set the isSynched flag.
If the query result indicates the object is not in the database you then resend the object and leave it's "isSynced" flag set to NO.
If the query result indicates the object is in the database, set the "isSynced" flag to YES and remove it from your "need to synch" list.
An approach for preventing duplicate database entries is to make a unique key. For example, tag each with a hash based on the time and date. Then configure the table to ensure each key is unique.
I hope this question isn't too general/ambiguous...
I'm writing an iphone quiz game app and am having trouble figuring out the best way to handle data. Currently I am thinking of having a single Model class that holds an array of "User" classes which each have an array of user-specific "Question" classes. I'd like to be able to access the overarching Model from any of my view controllers, but that means I'll probably have to pass the model object to any new view controller, use a singleton, or do something else. What is the best way to access my Model object from other classes? Another factor I'm not sure about is being able to save the data - would I have to use Core Data/SQLite to save my single Model object, or is there a simpler way?
I'd start by designing a schema using CoreData. IMO, its best to start out using CoreData because then you'll never have to convert your data layer to CoreData, in the event that your app scales beyond a simple object or two.
The other route would be to create a web service that returns your data... so you just call the service and it returns a collection of user objects. You can either send down the entire object graph with the questions, or create another service to return a collection of questions for a specific user. If you have a web server handy, this method scales the best because you don't have to rely on app updates to get new questions into your system. I would still use CoreData to cache the results... so that way you're not downloading the same information all the time.
So when it comes to accessing CoreData objects, I use a repository class that's a singleton. This makes it easy for any view controller to grab an instance of the repository and get some data. Here's what something like that might look like;
[[Repository defaultRepository] findFirst:[User class]
where:#"name == 'John'"]
There's a lot of redundant code to fetch data so wrapping that up in an object will help get all that nasty code, like predicates and sorting, out of your view controllers. You can see where I leverage a va_list in the where clause so I can inject that string right into my predicate. Here are some other methods you could implement:
- (NSArray *) findAll:(Class)entity
sortByKey:(NSString *)key
ascending:(BOOL)ascending;
- (NSArray *) findAll:(Class)entity
sortByKey:(NSString *)key
ascending:(BOOL)ascending
where:(NSString *)format, ...;
- (id) findFirst:(Class)entity
where:(NSString *)format, ...;
I'm not sure if this is the preferred way, but I've had a lot of success with this method. Hope this helps!
Check this link, this will help you a lot
Link: http://mobile.tutsplus.com/tutorials/iphone/iphone-sdk_store-data/
This cover 4 major ways to store data in iPhone with sample code.
1) NSUserDeafult
2) Property Lists
3) SQLLite
4) Core Data