I have a specific use case for orbit-db but I am a bit fuzzy about a certain security implication.
I'm developing a web app where a user logs in through a Tronweb wallet account.
People can post questions, whereas other people can answer them and get paid for it.
In order to do so, I add the public key of the respondent to the question and save it to Orbit db.
Now it is my understanding that default access to any orbit-db instance is given to the app (identity) that creates it per default, or you can add custom access controllers. Let's say, if I want to create a db to manage tags, I could create an identity based on my own tronlink account and require a login to create those tags. Nobody else would be able to access that db.
Now what I am a bit fuzzy about is what happens when:
An OP creates a question, and a respondent registers an answer.
When the OP accepts the answer, and payment through the Tronlink plugin will be executed.
But since OrbitDB can run without a server (i.e. p2p based on a local IPFS node), what prevents anybody to set a breakpoint in the client-side Javascript code, get a handle to the db instance, and execute an update call to update the respondent's address locally in that question document, after which it will be synced to other nodes?
I store the public key of the respondent in orbitdb, but the transaction is still confirmed by the OP through Tronlink wallet plugin of course. But still, you cannot assume all users to check it every time.
Since there is no server involved, I don't see how you can prevent corruption of the db client-side by unwanted parties.
Could you enlighten? Let me know if my question is not clear.
what prevents anybody to set a breakpoint in the client-side Javascript code, get a handle to the db instance, and execute an update call to update the respondent's address locally in that question document
Nothing.
after which it will be synced to other nodes?
This is what makes OrbitDB secure. Each document that is synced to a new node, needs to pass through the validator function of this node. So, honest (the ones that have a correct AccessController) nodes will filter out malicious changes.
What that means? A node can be "corrupted" but it can't "corrupt" other nodes in the network.
I have a React Native app integrated with Relay and I want to delivery an offline-first experience for users.
So, in the first app launch a placeholder should be shown while data is being loaded. After that, every time the app be launched I want to show the last cached data while a fresh data is loaded.
I found this issue from 2015 and based on eyston's answer I've tried to implement a CacheManager based on relay-cache-manager using the AsyncStorage. With the CacheManager I can save and load relay records from cache but when the network is disabled the app isn't able to show cached data.
Is there any way of use relay cached data while relay is fetching fresh data?
We have a production app which uses Relay and RealmDB for offline experience. We took a separate approach from CacheManager because CacheManager was not quite ready at that time. We used relay-local-schema for this.
We defined the entire schema required for mobile using relay-local-schema. This could be the same file as what your backend server would be using for defining graphql schema and change the resolve function to resolve the data from realm db. For this we also created schema in realmdb which had nearly same structure as the graphql schema for simplicity of writing data returned by backend server to realmdb. You can also automate generating of this schema by using the graphql introspection query. We defined a custom network layer where we made sure that all Relay queries always touch the local db. In the sendQueries function all queries are resolved with relay-local-schema which gets resolved very fast and react views shows the old data and at same time a network request is made for each request in sendQueries function. On receiving data from network request it is written in realmdb and Relay in-memory is store is also populated with the new data, this automatically refreshes all the react views whose data changed. To write data to Relay in-memory store we used the following undocumented method
Relay.Store.getStoreData().handleQueryPayload(query, response);
You can get query object from request that you receive in sendQueries function using request.getQuery().
Our current implementation is bit tied up with our business logic and hence it is difficult to open source this logic. I'll try to provide a demo app is possible.
I have a use case where I have to update a class in the local storage with the changes that have been made in my parse server. I have deleted some entries in my parse server and want those to be deleted in the local storage of the app on the user device. What is the best way to handle this. For now, I
Unpin all the objects for that class from my local storage.
Try to fetch the data from my parse server and pin them to the local storage.
Is there a better way to do this?
Parse pin to local datastore is not made as a framework for synching data between device and server, but rather as a way to speed up your app by providing a local version of your data, and to avoid your app becoming unusable if the device is temporarily without a data connection. Therefore, there are no streamlined ways of synching your data between the device and the backend.
You can go about this in a couple of ways. For most situations, I would say that just unpinning and refetching is the way to go. In almost all other scenarios, you end up creating your own synching service, which can quickly become quite complex.
You can, of course, keep track of all objects that have been removed or changed since last synch, and then only unpin/re-fetch those, but this gets very hard to handle for multiple users. By far, the easiest way is to unpin all and fetch all again from the server. If this means fetching a lot of objects, you might want to rethink your logic and maybe not keep that many locally pinned objects.
I'm developing an app extension that needs to share data with the containing app. I created an app group and moved the core data store of the main app to that folder. From the extension I can create the managed object context and save data to the store and I can also access it from the containing app. Now I have two independent applications accessing the same core data store. This sounds like a recipe for disaster to me. Is what I have set up sufficient for sending data from the extension to the containing app or should I look for another way?
In this situation you'll have two entirely independent Core Data stacks accessing the same persistent store file.
Assuming that you're using SQLite, you're fine, at least as far as data integrity. Core Data uses SQLite transactions to save changes and SQLite is fine with multiple processes using the same file. Neither process will corrupt data for the other or mess up the file.
You will have to deal with keeping data current in the app. For example if someone uses the share extension to create new data while the app is running. You won't get anything like NSManagedObjectContextDidSaveNotification in this case. You'll need to find your own way to ensure you get any new updates.
In many cases you can make this almost trivial-- listen for UIApplicationDidBecomeActiveNotification, which will be posted any time your app comes to the foreground. When you get it, check the persistent store for new data and load it.
If you want to get a little more elegant, you could use something like MMWormhole for a sort-of file based IPC between the app and the extension. Then the extension can explicitly inform the app that there's new data, and the app can respond.
Very interesting answer from Tom Harrington. However I need to mention about MMWormhole. I've found that MMWormhole uses NSFileCoordinator and apple tells that:
Using file coordination in an app extension to access a container
shared with its containing app may result in a deadlock in iOS
versions 8.1.x and earlier.
What Apple suggests for safe save operations you can read here:
You can use CFPreferences, atomic safe save operations on flat
files, or SQLite or Core Data to share data in a group container
between multiple processes even if one is suspended mid transaction.
Here is the link to the Apple Technical Note TN2408.
I'm in the middle of brainstorming a cloud sync solution for a Core Data app that I am currently developing. I'm planning to open source the code for this once its done, for anyone to use with their Core Data apps, so input from the community on how this system should work is much appreciated :-) Here's what I'm thinking:
Server Side
Storage Provider
As with all cloud sync systems, storage is a major piece of the puzzle. There are many ways to handle this. I could set up my own server for storage, or use a service like Amazon S3, but because I'm starting out with $0 capital, at this moment, a paid storage solution isn't a viable option. After some thought, I decided to settle with Dropbox (an already well established cloud sync application and storage provider). The pros of using Dropbox are:
It's free (for a limited amount of space)
In addition to being a storage service, it also handles cloud sync
They recently released an Objective-C SDK which makes it much easier to interface with it in Mac and iPhone apps
In case I decide to switch to a different storage provider in the future, I intend to add "services" to this cloud sync framework, basically allowing anyone to create a service class to interface with their choice of storage provider, which can then simply be plugged into the framework.
Storage Structure
This is a really difficult part to figure out, so I need as much input as I can here. I've been thinking about a structure like this:
CloudSyncFramework
======> [app name]
==========> devices
=============> (device id)
================> deviceinfo
================> changeset
==========> entities
=============> (entity name)
================> (object id)
A quick explanation of this structure:
The master "CloudSyncFramework" (name undecided) folder will contain separate folders for each app that uses the framework
Each app folder contains a devices folder and an entities folder
The devices folder will contain a folder for each device that is registered with the account. The device folder will be named according to the device ID, obtained using something like [[UIDevice currentDevice] uniqueIdentifier] (on iOS) or a serial number (on Mac OS).
Each device folder contains two files: deviceinfo and changeset. deviceinfo contains information about the device (e.g. OS version, last sync date, model, etc.) and the changeset file contains information about objects that have changed since the device last synchronized. Both files will just be simple NSDictionaries archived into files using NSKeyedArchiver.
Each Core Data entity has a subfolder under the entities folder
Under each entity folder, every object that belongs to that entity will have a separate file. This file will contain a JSON dictionary with the key-value pairs.
Simultaneous Sync
This is one of the areas where I am almost completely clueless. How would I handle 2 devices connecting and syncing with the cloud at the same time? There seems to be a high risk of things getting out of sync here, or even data corruption.
Handling migrations
Once again, another clueless area here. How would I handle migrations of the Core Data managed object model? The easiest thing to do here seems to be just to wipe the cloud data store clean and upload a new copy of the data from a device which has undergone the migration process, but this seems somewhat risky, and there may be a better way.
Client Side
Converting NSManagedObjects into JSON
Converting attributes into JSON isn't a very hard task (theres lots of code for it floating around the web). Relationships are the key problem here. In this stackoverflow post, Marcus Zarra posts code in which the relationship objects themselves are added to the JSON dictionary. However, he mentions that this can cause an infinite loop depending on the structure of the model, and I'm not sure if this would work with my method, because I store each object as an individual file.
I've been trying to find a way to get an ID as a string for an NSManagedObject. Then I could save relationships in JSON as an array of IDs. The closest thing I found was [[managedObject objectID] URIRepresentation], but this isn't really an ID for an object, its more of a location for the object in the persistent store, and I don't know if its concrete enough to use as a reference for an object.
I suppose I could generate a UUID string for each object and save it as an attribute, but I'm open for suggestions.
Syncing changes to the cloud
The first (and still best) solution that popped into my head for this was to listen for the NSManagedObjectContextObjectsDidChangeNotification to get a list of changed objects, then update/delete/insert those objects in the cloud data store. After the changes have been saved, I would need to update the changeset file for every other registered device to reflect the newly changed objects.
One problem that comes up here is, how would I handle a failed or interrupted sync?. One idea I have is to first push changes to a temporary directory on the cloud, then once that has been confirmed as successful, to merge it with the master data on the cloud so that an interruption in the middle of the sync won't corrupt data. Then I would save records of the objects that need to be updated in the cloud into a plist file or something, to be pushed during the next time the app is connected to the internet.
Retrieving changed objects
This is fairly simple, the device downloads its changeset file, figures out which objects need to be updated/inserted/deleted, then acts accordingly.
And that sums up my thoughts for the logic that this system will use :-) Any insight, suggestions, answers to problems, etc. is greatly appreciated.
UPDATE
After lots of thinking, and reading TechZens suggestions, I have come up with some modifications to my concept.
The largest change I've thought up is to make each device have a separate data store in the cloud. Basically, every time the managed object context saves (thanks TechZen), it will upload the changes to that device's data store. After those changes are updated, it will create a "changeset" file with change details, and save it into the changeset folders of the OTHER devices that are using the application. When the other devices connect to sync, they will go through the changeset folder and apply each changeset to the local data store, then update their respective data stores in the cloud as well.
Now, if a new device is registered with the account, it will find the newest copy of the data out of all the devices and download that for use as its local storage. This solves the problem of simultaneous sync and reduces the chances for data corruption because there is no "central" data store, each devices touches only its data and just updates changes rather than every device accessing and modifying the same data at the same time.
There's some obvious conflict situations to deal with, mainly in relation to deleting objects. If a changeset is downloading instructing the app to delete an object that is currently being edited, etc. there needs to be ways to deal with this.
You want to look at this pessimistic take on cloud sync: Why Cloud Sync Will Never Work.
It covers a lot of the issues that you are wrestling with. Many of them are largely intractable.
It is very, very, very difficult to synchronize information period. Adding in different devices, different operating systems, different data structures, etc snowballs the complexity often fatally. People have been working on variants of this problem since the 70s and things really haven't improve much.
The fundamental problem is that if you leave the system flexible and customizable, then the complexity of synchronizing all the variations explodes exponentially as a function of the number of customization. If you make it rigid, you can sync but you are limited in what you can sync.
How would I handle 2 devices
connecting and syncing with the cloud
at the same time?
If you figure that out, you will be rich. It's a big issue for current cloud sync providers. They real problem here is that your not "syncing" your merging. Software sucks at merging because its very hard to establish a predefined rule set to describe all the possible merges.
The simplest system is to establish either a canonical device or a device hierarchy such that the system always knows which input to choose. This however, destroys flexibility.
How would I handle migrations of the
Core Data managed object model?
The migration of the Core Data model is largely irrelevant to the server. That's something that Core Data manages internally to itself. Model migration updates the model i.e. the entity graph, not the actual data.
Converting NSManagedObjects into JSON
Modeling relationships is hard especially with tools that don't support it as easily as Core Data does. However, the URI of a permanent managed object ID is supposed to serve as a UUID that nails the object down to a specific location in a specific store on a specific device. It's not technically guaranteed to be universally unique but its close enough for all practical purposes.
Syncing changes to the cloud
I think you're confusing implementation details of Core Data with the cloud itself. If you use NSManagedObjectContextObjectsDidChangeNotification you will evoke network traffic every time the observed context changes regardless of whether those changes are persisted or not. Depending on the app, this could drive connections thousands of times in a few minutes. Instead, you only want to sync when context is saved at the most.
One problem that comes up here is, how
would I handle a failed or interrupted
sync?
You don't commit changes until the sync completes. This is a big problem and leads to corrupt data. Again, you can have flexibility, complexity and fragility or inflexibility, simplicity and robustness.
Retrieving changed objects: This is
fairly simple, the device downloads
its changeset file, figures out which
objects need to be
updated/inserted/deleted, then acts
accordingly
It's only simple if you have an inflexible data structure. Describing changes to a flexible data structure is a nightmare.
Not sure if I have helped any. None of the problems have elegant solutions. Most designer end up with rigidity and/or slow, brute force iterative merging.
Take a serious look at RestKit.
It is an open source project that aims to help with integrating iOS apps with cloud data, including but not limited to the scenario where there is a core-data model for that data on the client.
I have recently started to use it in one of my projects, and found it to be quite useful. In the core-data scenario, you implement declarative mappings between your data model and the content you GET from and POST to the server, and it takes care of things like injecting objects from the cloud into your client model, posting new objects to the server and incorporating server-generated objects IDs into your client-side model, doing all of this in a background thread and taking care of all the core-data context threading issues and so on.
RestKit by no means is a mature product, but is has a fairly good foundation and quite a few things that can use help from other contributors. Especially, if your goal is to create an open source solution, it would be great to contribute and improve something like this rather than re-invent a new solution. Unless of course, your see serious differences between what you have in mind and other existing solutions :-)
Since this post was current, there are several new options available. It is possible to develop a solution, and there are apps shipping with these solutions.
Here is a short list of the main Core Data sync options:
Apple's native Core Data/iCloud sync. (Had a rocky start. Seems better now.)
TICDS
Wasabi Sync, a paid service.
Simperium (Seems abandoned.)
ParcelKit with Dropbox Datastore API
Ensembles, the most recent. (Disclosure: I am the founder of the project)
It's like Apple answered my question for me with the announcement of the iCloud SDKs, which come complete with Core Data integration. Win!