I want to backup the Data of an UITableView to Parse. First the User logs in. Now every user should have it's own cloud Storage save of their UITableView, but how to upload this UITableView or the UITableViewCell when pressing on a Button (Save)?
You can create separate Model objects for populating each cell of the UITableView . Saving objects into parse is very well in explained in the documentation:
Also take a look at this tutorial if you still need more explanation:
Tutorial
Parse is just a backend which allows you to save data by calling their functions.
The concept of parse is to show the data in your app by using their api and database.
An example to save a PFObject (A parse object) in the parse database:
let testObject = PFObject(className: "TestObject") // Create the PFObject with the classname/tablename on parse
testObject["foo"] = "bar" // set the property "foo" to "bar"
// save the object in background (not on the gui thread to prevent lags on the UI)
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved.")
}
ATTENTION: Before to be able to call these functions and create there objects you need to import the Parse Library and initialize your app to parse.
Related
I need to observe changes of an Entity after import occurred.
Currently I have next logic:
Save Entity with temp identifier (NSManagedObject.objectId) to local core data storage.
Send Entity to the server via Alamofire POST request.
Server generates JSON and reply with the almost the same Entity details but with modified identifier which was NSManagedObject.objectId previously. So the local one Entity id will be updated with server id.
Now when I received new JSON I do transaction.importUniqueObjects.
At this step I want to inform my datasource about changes. And refetch data with updated identifiers.
So my DataSource has some Entities in an array, and while I use this datasource to show data it's still static information in that array which I fetched before, but as you see on the step number 4 I already updated core data storage via CoreStore import and want DataSource's array to be updated too.
I found some information regarding ListMonitor in CoreStore and tried to use it. As I can see this method works when update comes
func listMonitorDidChange(_ monitor: ListMonitor)
but I try to refetch data somehow. Looks like monitor already contains some most up to date info.
but when I do this:
func listMonitorDidChange(_ monitor: ListMonitor<MyEntity>) {
let entities = try? CoreStore.fetchAll(
From<MyEntity>()
.orderBy(.ascending(\.name))
) // THERE IS STILL old information in database, but monitor instance shows new info.
}
And then code became like this:
func listMonitorDidChange(_ monitor: ListMonitor<MyEntity>) {
var myEntitiesFromMonitor = [MyEntity]()
for index in 0...monitor.numberOfObjects() {
myEntitiesFromMonitor.append(monitor[index])
}
if myEntitiesFromMonitor.count > 0 {
// HERE we update DataSource
updateData(with: myEntitiesFromMonitor)
}
}
not sure if I am on the right way.
Please correct me if I am wrong:
As I understood each time core data gets updated with new changes, monitor gets updated as well. I have not dive deep into it how this was made, via some CoreData context notification or whatever but after you do something via CoreStore transaction, such as create or update or delete object or whatever you want, monitor gets update. Also it has callback functions that you need to implement in your class where you want to observe any changes with data model:
Your classes such as datasource or some service or even some view controller (if you don't use any MVVP or VIPER or other design patterns) need to conform to ListObserver protocol in case you want to listen not to just one object.
here are that functions:
func listMonitorDidChange(monitor: ListMonitor<MyPersonEntity>) {
// Here I reload my tableview and this monitor already has all needed info about sections and rows depend how you setup monitor.
// So you classVariableMonitor which I provide below already has up to date state after any changes with data.
}
func listMonitorDidRefetch(monitor: ListMonitor<MyPersonEntity>) {
// Not sure for which purposes it. I have not received this call yet
}
typealias ListEntityType = ExerciseEntity
let classVariableMonitor = CoreStore.monitorSectionedList(
From<ListEntityType>()
.sectionBy(#keyPath(ListEntityType.muscle.name)) { (sectionName) -> String? in
"\(String(describing: sectionName)) years old"
}
.orderBy(.ascending(\.name))
.where(
format: "%K == %#",
#keyPath(ListEntityType.name),
"Search string")
)
All other thing documented here so you can find info how to extract info from monitor in your tableview datasource function.
Thanks #MartinM for suggestion!
I am running parse server on AWS. Data is being stored on mlab. At app launch I make a query:
let query = PFQuery(className: foodDataClassName_)
query.findObjectsInBackgroundWithBlock({(objects : [PFObject]?,error : NSError?) -> Void in
})
It returns me all the rows of data. I save them locally using CoreData. When any row of data is updated, I delete the previous locally stored data and download all the data again and save it. This is not a good approach. What I want is that I only download the rows which are updated not all the rows. How can I achieve that? Thanks.
what you can do is the following:
The first time the user log in to your app you need to query for all the user rows from the server so in this case you will execute the query without any condition. In this query you can use limit in order to limit the results that will be returned by the server. When you get results from the server you will need to:
Store all rows in your local database
Store the current NSDate inside NSUserDefaults
The next call to your server will be to get only the updated rows. In order to achieve it you will need to:
Get the last sync date from your NSUserDefaults (the one that we save above)
Execute the query but this time with a condition of greaterThan your lastSyncDate
at the end your code to fetch the items should look like the following:
// check if we synced the date before
let lastSyncDate = NSUserDefaults.standardUserDefaults().objectForKey("lastSyncDate")
let query = PFQuery(className: "MyParseObjectClassName")
if (lastSyncDate != nil){
// get only records that were created/updated since the last sync date
query.whereKey("updatedAt", greaterThan: lastSyncDate!)
}
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
// store objects in parse local data store
if (objects?.count > 0){
PFObject.pinAllInBackground(objects)
}
// save the last sync date in user defaults
NSUserDefaults.standardUserDefaults().setValue(NSDate(), forKey: "lastSyncDate")
NSUserDefaults.standardUserDefaults().synchronize()
}
Please notice here i used parse local data store which allows you to store parse objects to your local data base easily without using core data. Parse local data store is provided by parse iOS SDK and save a lot of time and effort for you so i strongly recommend you to leverage it.
local data store also take care for you to objects that were created/updated and it will automatically create new object and will update existing ones.
you can read more about it in here
I'm using Parse in my iOS app.
In my app I'm using a lot of saveEventually() functions to store data in Parse without there needing to be an internet connection available.
I know that saveEventually() returns a BFTask object.
It is possible to get all the tasks created in order to check their status in any given moment? Also, could this same technique be used after an app restart?
Thanks!
Parse is using Boltz framework, which in return, you will have a BFTask object upon completion.
Example:
yourPFObject.saveEventually().continueWithBlock {
(task: BFTask!) -> BFTask in
if task.isCancelled() {
// the save was cancelled.
} else if task.error() {
// the save failed.
} else {
// the object was saved successfully.
var object = task.result() as PFObject
}
}
With this in mind, you can check their status by storing the completed task in coredata. In appDelegate applicationDidBecomeActive or didFinishLaunchingWithOptions, you can attempt to retrieve them from coredata depending on your logic.
Of course if you want to just keep track on the status, you can keep in a dictionary and stores in NSUserDefault. It is completely up to your choice and needs.
More example can be found in this link : https://github.com/BoltsFramework/Bolts-iOS
Right now I have a JSON file that is around 30 MBs, the JSON file is a dictionary in the format:
{"chinesePhrase":[
{"traditional": "21三體綜合症", "simplified": "21三体综合症","pinyinRead": "èr shí yī sān tǐ zōng hé zhèng", "pinyinType": "er4 shi2 yi1 san1 ti3 zong1 he2 zheng4", "definition": ["trisomy","Downs syndrome"]},...
]}
Each entry is then put into a table cell, and there is a search bar for users to search which word they want and then when selected it is saved into CoreData.
My question is:
Every time I open the app it has to re-parse the JSON file to populate the table cells; is there a better way to do this so that the JSON file wouldn't have to be re-parsed every time the view controller that has all the dictionary entries is opened?
You could try this. In the appdelegate "applicationDidBecomeActive" section. Check if this is the initial launch of the app. If so, parse the JSON data.
Then if the JSON parse was successful set the NSUserDefault to indicate this and store the data in CoreData. Or what ever you need to do.
func applicationDidBecomeActive(application: UIApplication) {
let firstLaunch = NSUserDefaults.standardUserDefaults().boolForKey("FirstLaunch")
if firstLaunch {
println("Not first launch.")
}
else {
//Parse the JSON file here.
//if success set NSUserDefault
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "FirstLaunch")
//if success, store data in CoreData.
}
}
If you want to update the data each time the app is launched. Lets say you added/edited data and wanted user to get this new updated data during next launch. You would have to remove the NSUserDefault settings and firstLaunch logic. And just parse the JSON file and store in CoreData.
Then when ever you needed to display data in your TableView you could easily just fetch it from CoreData.
I would like to have synchronized http request results with my Core Data database. For example very basic class:
class Category: NSManagedObject {
#NSManaged var id: NSNumber
#NSManaged var name: String
#NSManaged var imageUrl: String
}
And I have this method for getting results from request:
apiManager.getRequestJsonParse(Constants.Server.BackendUrl + "categories?lang=cs", completion: { (result, error) -> Void in
completion(categories: [], error: error)
})
Result is dictionary parsed from json. It's okay but now I want to parse dictionary to my class, check if exists in database, if it exists update properties and if not than add new category to database.
My approach that I was using in apps before was that I have two classes: Category and CategoryCD (CD like Core Data) and first I parse json to Category class than for all categories I check if any CategoryCD (saved in CD) has same Id and then update, or add or other things.
Now I am thinking if there is better way how can I do it. I could each time when I download new results delete my database for this class and then add all results. The problem with this way is that what if I have something for some classes that I want keep. Like if I download and save images then I would rather still have connection between class and saved image.
I was thinking about my old approach but reduce 2 almost same classes (1 for Core Data and 1 same but for parsing) to 1 Core Data class. But then there is a problem when I init this class I must always create in database right? So It could be complicated.
What are you using for this? I think it's very common problem. You have list of items and I would like to have them available offline (with all data I downloaded before) but than when I connect to internet I would like to update with new results (not download all from server, just responses which I requested).
It is a very common problem and it has been solved hundreds of ways. BTW, This is not "synchronizing" it is "caching" for offline use.
Do not create two objects as that is redundant. Create just the Core Data objects as part of the parse.
A standard JSON parse would look like this:
Convert to Objects using NSJSONSerializer.
Fetch all unique IDs from the JSON objects using KVO
Fetch all existing objects from Core Data based on uniqueIDs
Insert all objects that do not currently exist
If you are going to update objects then #4 would do both.
Search on Stackoverflow and you will find plenty of examples of how to do this.
I do something similar in one of my apps.
Here is some generic code to fetch an item and update it. If it doesn't exist, it creates it.
func insertOrUpdateManagedObject(id: Int16, name: String) -> YourManagedObject? {
var managedObject: YourManagedObject?
if let context = self.managedObjectContext {
if let fetchResult = fetchManagedObjectWithId(id) {
managedObject = fetchResult
managedObject?.name = name
}
else {
managedPhrase = NSEntityDescription.insertNewObjectForEntityForName("YourManagedObject", inManagedObjectContext: context) as? YourManagedObject
managedObject?.id = id
managedObject?.name = name
}
}
println("Created a managed object \(managedObject)")
return managedObject
}