I need a suggestios on how to correctly implement CoreData with objects that can be uploaded/downloaded from server and sent through game center.
The app is similar to a trading card game, you can get the idea from these 2 separate entities:
Card: The actual unique cards with all the information about each one. The "Card"s entities are static and do not change, they do not need to be sent on server either because all I need to do is send the "CardId" to pull a "Card" entity.
UserCard: All the cards that a user owns, a user may have the same card multiple times but this would be a different "UserCard" with a pointer ("CardId") to a basic "Card". They are always changing. They need to be easily sent through GameCenter and uploaded/downloaded from server. Also, some temporary "UserCard"s would need to be downloaded from server when the user visits a friend's profile to see which cards they have.
Does anyone have any suggestion on how to do this correctly? Currently I have two ideas:
A. Use CoreData for everything, this would mean that the "UserCard"s are NSManagedObject subclasses which get intelligently encoded/decoded (using NSCoding) to upload/download and send through GameCenter. The basic "Card" is set as a relationship in Core Data. A "temporary" attribute is also set for the "UserCard"s to be able to know which ones do not belong to the current user so they can be deleted later.
B. Use CoreData only for the Basic "Card"s and use a NSObject subclass for the "UserCard"s. This allows me to directly use the "UserCard"s without the need to insert them into CoreData. Makes it easier to download/upload and send through GameCenter. It also removes the need for the "temporary" property because the objects will just be deallocated when the view has stopped using them. The problem with this method is that I would need another way to store the current user's "UserCard"s in the device because they do need to be available offline.
Thank you!
Personally, I would use CoreData all around. Reasons:
You're already having to dive into CoreData to deal with Cards anyways
Just create an NSManagedObject category called NSManagedObject+JSON. In it have a simple method called -(NSDictionary*)jsonRepresentation and have the UserCard format itself into a dictionary and return itself. Super easy.
It would be safer to use CoreData and save often, rather than run the risk of writing to a file and it either: 1. getting corrupted (highly unlikely but user would be mad if it did); or 2. not getting fully written (user could kill the app before it has time to write everything to a file)
The extra little bit of work to implement the UserCard entity would be small.
You get the built memory management and searching functions of CoreData.
I just finished a large project working with CoreData so I may be a little biased. From what you've stated in your question though, I would take CoreData for everything. Here are some helpful opensource libraries when dealing with CoreData and a web server:
MagicalRecord
AFIncrementalStore
And a great tutorial for integrating CoreData with a web server.
Related
I am starting a new project (learning purposes) and I am trying to figure out what is the best software design pattern to use in the following scenario.
I have several data that need to be downloaded from multiple webservices and store somewhere in my app, to display it later. However each piece of data (e.g. list of teachers, students) will only be used in one or more specific view controllers (e.g. teachersViewController and studentsViewController).
I read that the Singleton pattern or use the AppDelegate to store a variable (an object like ApplicationData) is a bad practise, even more in this example which I want to restrict the data access.
So, which design pattern should I choose? I have read something about dependency injection, but I don't have any clue about it or if it even helps me in this question. If it helps, some examples with explanation would be nice.
You need some sort of database to store downloaded data. Good choices are Realm and Core Data. The right way to process data is:
Check if data is already in DB and show it if available.
Download or update data from server and parse it to objects.
Save objects to DB.
Show data taken from DB to user.
Download data as needed. When you open VC with students then download only students data and so on.
EDITED: If you need all the data on app open then load it and put in a DB before first screen opens. Then just use DB to show data to user.
I want to ask for a safe way to clear subEntities in coredata.
I have my a many-to-many relationship like this: Product *<->* Product. Therefore, I've got to create a subEntity to hold some special values between (sortPosition, groupName.....).
So it's like this: Product *<->1 ProductSubEntity 1<->*Product.
When I download products from server's API, the easiest way to update correctly correspond to the server's result is:
Remove all child relationship ([self removeProductSubEntities:self.subEntities]).
Add sub from server's result.
Result: There'd be a lot of subEntity in coredata (which won't hold relationship to any product), and this might take storage/memory/cpu when CRUD (I think?). But I can't actual delete the subEntity (in case it's being hold reference to as an viewController's Object somewhere, and it might cause crash: access to a deleted object).
QUESTION:
How can I clear those sub entities (might occur sometimes) if:
No relationship to any product.
No actual reference from anywhere (any viewControllers or objects)???
P/S: I'm thinking of implement a batch delete when terminate app. Could that be consider a safe solution?
I don't consider this to be a datastore issue, rather a UI update issue. You should delete the objects from the datastore when you don't need them any more and you should update the UI accordingly.
1 thing you didn't mention is re-use. It's possible that your download may be an update to an existing item, which you could find and update, then life is easy all round. Arguably everything below still applies in this case though as your UI might not update to reflect changes and you may need to refresh the managed object.
For the UI update it's generally wise to observe the datastore for changes, usually with an NSFetchedResultsController. If you're doing this then your UI would automatically update itself with the changes.
If you're explicitly passing entity instances around then you should have some way to trigger an update explicitly, and exactly how that works depends on your UI. Generally speaking you'd be doing something like posting a UINotification to tell the system that the datastore changed and they need to re-validate their data objects. For the UI you shouldn't be showing now-dead objects to the user, and in your question where you talk about not deleting to avoid crashes, it's probably worse to allow the user to update invalid objects and just quietly not telling them that their updates won't be saved. When the notification is received you may want to pop a (some) controller(s) off the stack, or re-query the datastore for the new data to be displayed.
If for some reason you don't want to do the above, then yes, you can query for all of the entities with a nil relationship and then batch delete them. This should be done on a background thread just like data loading and I'd recommend doing it on app load instead of close (because you won't have so many view controllers loaded and the ones that are should all have only valid references now...).
I've worked on several iOS apps, some of them utilize Core Data, and some of them don't. While I consider myself having a basic to somewhat good understanding of Core Data, there's always something that makes me doubt the usefulness of it. I've done a lot of reading on the subject, and the general consensus seems to be the advantages of using it outweighs the disadvantages. I recently submitted an app without using Core Data, and always planned on going back to update the project to utilize it when I have the time for some optimization work. Now's the time, but I wonder if it makes sense for the app I'm working on, and maybe I am not using it correctly all along. Please advise and point out what I am missing.
The project I am working on is a social networking app, which also has a front-end site. We have standard features like a newsfeed, event listing, the ability to follow/unfollow someone, and a map with POIs at user's location. Currently, we're using pagination whenever needed when requesting data from server.
My understanding of why Core Data is great:
Easier to manage data with complicated relationship
Easier to access data without having to pass them around
Easier to manipulate, fetch, and sort your data
Better memory utilization
Improve perceived performance by preloading data stored locally until latest data's received
The problem I am having is, since I am using pagination when requesting for data instead of requesting for all at once. The data stored locally is only a subset of the current state in the database. Let's use newsfeed as an example. If I do the following, it will cause some problems:
User manually refresh the newsfeed -> Controller notifies model that it needs the latest 20 items -> Model requests for the latest 20 items in the newsfeed and save them as NSManagedObject -> Model notifies controller that data is ready -> Fetch the latest 20 items to show in UITableView
If user A refreshes the newsfeed, background the app, and then user B deletes his post in the newsfeed (let's say it was 10th item) before user A foregrounds the app again to refresh the newsfeed. In user A's newsfeed, B's post will still be up there because according to the createdAt attribute, it's indeed one of the latest 20 items.
To fix this problem, I can think of a few solutions:
Add a flag to the item to indicate it's removed
Always discard local data when new data arrives
Disable pagination
Instead of using the workflow described above, always present the requested data only instead of fetching the latest
Solution 1 means custom code is required to deal with different clients since browser doesn't need deleted items but iOS client does. However, even though it can work, it can potentially mess up the pagination mechanism and can cause weird behaviours in the client. For example, if a large amount of items gets removed, the latest 20 items will contain only a few items that will actually show up in the newsfeed on the client when user refreshes it. As user follows more people, more stories will be inserted in his newsfeed as well. This solution won't work very well in this situation.
Solution 2 totally defeats the purpose of using Core Data in the first place unless I am missing something.
Solution 3 means the client always needs to request for all data. This is nearly impossible to do because as you get more data, the time to retrieve and process them will make the app slow and unresponsive. It also doesn't make sense from technical and UX point of view.
Solution 4 also kinda defeats the purpose of using Core Data because it's the same workflow when we only store data in memory. You can still fetch and find objects but they might be invalid on the server already at the time of access.
Am I missing something? How is Core Data supposed to be used in this scenario? How do you ensure data consistency when the client doesn't have all the data? Thanks you in advance.
app scenario: on the UI, a button is tapped to get contact list from the server. the request goes to subproject which does the download and parsing and returns the result thru its delegate to the UI. so far everything works properly. lets say there is no internet connection and we cant have the contact list. to solve the problem, I want to cache the data in core data. if there is no internet, the cached data will be returned. now the question that bugs me, is it possible to create one data model and use it in subproject to save the data and in UI where data get pulled and edit from the same data model?
so basically i want to access core data from different subprojects and UI.
i couldnt find hints or tutorials regarding this issue. any ideas?
thanks in advance!
edit:
a project "b" that is added to the parent project "a". the project "b" is actually a static library.
if i let the library to do the saving and returning data to UI, wont it be inefficient to get all data from core data then send it to the UI?
i actually hope that there is a way to use same data model in both UI and the library.
i want prevent the UI to have huge load of data. its better to hace core data to handle that incl. memory mangement. i'm still reading some sources and trying to implement it on a test project.
I would argue that only the main project should deal with persistency, as than you can always decide to handle it differently — save it permanently or not, use core data or a home grown sql wrapper…. So it would be up the the delegate to decide what to do with more data.
But along with the delegate protocol you could decide to maintain different model protocols that define, what your models can hold. this would be independent to the implementation. The delegate now could return objects — no matter if core data models or not — to the delegator if this objects conforms to the protocols. The delegator in the sub module now could check for values on the server and/or in the cache.
First, Thank you for any help provided.
I have an iOS leveraging CoreData to retain various presentations, this data comes from a sqlite file and there is no server connection.
I will have to be able to provide App updates (via appstore), this update may add more data to the database.
The tricky part is that it can not simply overwrite the current database, there are a few user tables that I will not like touched.
Please provide any information I should consider when accomplishing this or any links are greatly appreciated.
Thank you.
Given your app has no server connection, you will have to rely on shipping data within the updated application itself. I would recommend using a plist file or define your own xml or json structure. You can then read this data to create/update core data nsmanagedobjects.
It looks like someone in the past was using plist->coredata on SO
Would you have relationships between user created data and shipped data?
If not, you might go the route of connecting two stored to the persistent store coordinator. The shipped store would be read-only. The store with user created data would be read-write. You can use this approach, too, if you have relationships between shipped and user-created objects, but it's a lot more complicated, since CoreData doesn't manage cross-store relationships for you, and you'll need to write your own logic (doable, but not straight forward).
If you need to have relationships between shipped and user-created objects, you can still ship a CoreData store. When the app launches for the first time (no user-created objects), you copy the store to the Documents folder and user this store to create your CoreData stack. User created objects will be added to this store. Once you have new 'shipped' objects (i.e. a new store in the app-bundle), you'll have to manually migrate that stores data into the store that the user has changed. You'll have to be able to find
(1) objects that need to be deleted
(2) objects that need to be updated (changed)
(3) objects that need to be added
If you mark your shipped objects with a special flag such that you can tell if it's a user created object or a shipped one, that would be doable. You also have to have some sort of ID to be able to tell which objects in the new store correspond to which ones in the existing (old) store.
You do not need to go the route of using plists. In fact, I'd recommend against it. You can easily open two stores at the same time. Either to use both stored, or just to migrate objects from one store to the other store.