I have developed an iOS application using RealmSwift.
This works fine so far.
Now, as I'm getting closer to publishing into App Store, I wanted to set up some cloud configuration to be able to connect to a cloud database, but I just got totally confused.
A couple of weeks ago I saw Realm Cloud as an option. Now I only see MongoDB Realm - or something like that. I digged into a bit, and found that there are three components: Realm DB, Realm Sync and Mongo DB Atlas.
If my understanding is correct, I have to create an Atlas Cluster, on which my Realm Database will be hosted and to which I will be able to connect and sync. Am I correct?
My main problem is that I have no idea how to connect it to my existing code. I don't want user authentication or anything from MongoDB, I have my own, I basically have a DB only, which I want to sync and connect to. So, currently in the code I usually use:
let realm = try! Realm()
try! realm.write {
...
}
How can I update it to use the MongoDB in the Atlas Cloud? I went through their 'tutorials' but I'm still way too confused.
I see a Realm(configuration: Realm.Configuration) init function, but if I should use that one, how should I get a Realm.Configuration object?
Also, what does partition key means?
Thanks a lot.
Your confusion is justified. The docs and tutorials are still a work in progress and a bit disjointed. I think over time it will improve.
SO is not a good place for a full tutorial but here's a very high level overview.
A link to the tutorial - iOS Swift Tutorial
Go through the Cocoapods install
1) Your going to create a Cluster in the MongoDB console
2) Within that cluster you're create a Realm 'app'
3) Within that Realm 'app' you're going to set up:
Sync (development mode)
Users->Providers->Email/Password Authentication
Your app will have an AppId, which can be found in the Atlas console on the left, right next to the app name (it's a document button you can click on to copy).
Then, in you XCode Realm project, you'll set it up using cocoapods to install RealmSwift.
Now to your question:
how to connect it to my existing code.
Add a struct, which is the connection string to you Atlas Realm project
import RealmSwift
struct Constants {
// Set this to your Realm App ID found in the Realm UI.
static let REALM_APP_ID = "your app id"
}
then, when you want to authentication, you'll do this
let app = RealmApp(id: Constants.REALM_APP_ID)
app.login(withCredential: AppCredentials(username: username, password: password)) { user, error in
once you've authenticated, to access realm use this
guard let user = app.currentUser() else {
fatalError("Must be logged in to access this view")
}
let realm = try! Realm(configuration: user.configuration(partitionValue: user.identity!))
I've never used Realm as anything other than an embedded database inside the iOS app. If you need to store something on the server side you should implement an API that does the writing to your MongoDB (or whatever database you use). Connecting the iOS app directly to a server side database seems like an anti-pattern to me.
Realm Sync connects to the Atlas deployment as needed to synchronize the data. My understanding is you do not need to connect to Atlas directly in your code.
Documentation provides some information and a few pages down there is sample code.
Related
Which is the correct way(best practice) of adding secret API keys in flutter in case I want to push the code on github. I've made a simple app that consumes an API but I used the key in a crud way just to test whether the app is working. Usually from my experience developing applications in the back-end, Keys are stored somewhere and in different file then one would simply import it to the required file that needs the API_KEY and exclude the file in .gitignore file.
So far I have also implemented this approach:
Folder tree
-lib
-auth
-keys.dart
-secrets.json
secrets.json
This is where I will add the KEY and specify this file in .gitignore to be excluded from being added in github when I push my code.
//Add API KEY HERE
{
"api_key": "ee4444444a095fc613c5189b2"
}
keys.dart
import 'dart:async' show Future;
import 'dart:convert' show json;
import 'package:flutter/services.dart' show rootBundle;
class Secret {
final String apikey;
Secret({this.apikey=""});
factory Secret.fromJson(Map<String, dynamic>jsonMap){
return new Secret(apikey:jsonMap["api_key"]);
}
}
class SecretLoader {
final String secretPath;
SecretLoader({this.secretPath});
Future<Secret> load() {
return rootBundle.loadStructuredData<Secret>(this.secretPath,
(jsonStr) async {
final secret = Secret.fromJson(json.decode(jsonStr));
return secret;
});
}
}
I feel like this approach is too much. I would like to get suggestions of a better approach.
EDIT: Look at J. Saw's comment below.
EDIT 2: The issue in described at the bottom has been fixed in firebase-config 19.0.2.
Use Firebase Remote Config. Inside the Firebase console, inside the menu, scroll down to Grow and then Remote Config. Here you can add a parameter with a value. When you're done don't forget to publish the changes. It's kind of subtle.
Now install firebase_remote_config for Flutter.
After importing everything, you can retrieve your value using this code:
RemoteConfig remoteConfig = await RemoteConfig.instance;
await remoteConfig.fetch(expiration: Duration(hours: 1));
await remoteConfig.activateFetched();
remoteConfig.getValue('key').asString();
This way, the API key or token is never part of your application.
Note: there is currently an issue where you get a warning stating the application's name is not set, but this won't affect functionality.
For secure storage you have to rely on the corresponding native platforms, both iOs and Android provide a mechanism to securely store keys. You can implement it by yourself and use the flutter channels to obtain and store the keys. Information about this mechanism can be read here:
Android Keystore
iOs KeyChain
Also, you can use this flutter plugin, which uses the services mentioned above and provides a dart object to access the secure storage.
API Keys must be hard coded.
Why?
Because it's impossible to retrieve the key from an external source without having a key to that source, and you can't store it securely at the OS level without having the key within the app.
Example
When you connect your app to a Firebase project or Google Cloud servers, you basically authenticate using a hard-coded API key, that you have downloaded into your app when initiating your cloud project (read more).
Security
There are two essential steps to secure your critical assets:
Keep your secret keys out of version control.
Use obfuscation to make it difficult for attackers to reverse engineer your application, and reveal your API Key.
IMO, those are the only steps you can take to secure your app API Key.
As mentioned, if the key is a secrete and you would like to protect it then simply do not put it in the client app. The app can be de-compiled and the key can be extracted for person willing to target your client.
I would delegate the task of communicating with this API to your Application Server. You can put the key in your server and have your server communicate with this external API and relay the response to the client.
Edit: Another approach, which is less secure but more convenient is to obfuscate your code using something like proguard. See this page for flutter instruction on android app: https://flutter.io/android-release/
The best practice should be to create a Firebase function, and then have that function use the secrets storage. This approach keeps the secrets out of your source code so there are no issues with sharing the code, and stores them securely. Firebase authenticates your app (and users if you use the login feature).
Secret Parameters
Call functions from your app
You can use flutter_secure_storage from the oficial Flutter Packages
Is it impossible to get Realm from the server?
I tried this, but it doesn't work.
var url = "http://address/default.realm"
var rlm = RLMRealm(path: url)
Error message says 'No such file or directory'.
What you're trying isn't supported in this way.
If you want to use a Realm on the server side to seed your database, you would need to download it first and save it to the local filesystem of the device to load it from there.
If you want to sync data between the device and your server or across multiple devices, then you may be interested that this is a feature, on which we are working on. Subscribe this issue to keep up-to-date about it's current stage.
I’m developing a iOS App and I want to have a level of offline support and I’m struggling out of local datastore or cache which approach to use as It appears that you can’t use these two feature together.
My query is quite basic and doesn’t change only the data that is retrieved can change.
if i used one of the cache policies, i get connection errors and nothing appears to be returned from the cache.
The workflow i’m after is on the lines of below.
->When connected to the internet perform query and store objects locally.
->if there is no internet retrieve previously downloaded objects.
For the workflow you describe I think you're looking for a cache. If you would like the user could modify the data without connection and then, when there is wifi again, synchronise the local data with the remote data then you'll need the local datastore behavior.
The problem for me is when you want both in different parts of the same app because in parse in you use local datastore you can't use the cache. I don't really understand why!
I have an application write in golang,and it will load the basic data to global var,so make the application response fast,and export a http interface to update the var when user make changes to the database.
But i deploy another server,and used proxy.There comes a problem,when user send http request to the update url,it will load traffic to one of the servers.So that server update this var,but others not.
such as utils.go:
package utils
var BasicDatas map[string]*MyModel
func UpdateVar(){
// do some work
}
func PreLoadVar(){
// preload data to basicDatas
}
and the main.go
package main
import(
"codebase/utils"
)
func main(){
utils.PreLoadVar()
}
so if there anyway to share the var between multi hosts?Or any libiary can help do this work?
Nsq.io seem a good choice,but i want to seek a more simple one if there is.
thanks:)
Your global variable is a very simple form of caching, which is well known to be problematic when you are running your application on multiple servers. There are several ways to fix this problem:
As suggested by Bill Nelson: use your database as the central storage and do not cache your data in your application at all.
Externalize your cache with a simple key-value store, for example using Redis together with one of the many Redis clients in Go.
Use a distributed cache such as groupcache.
If you have too much time on your hands you could use a messaging solution like nsq to implement your own distributed cache, but I would advise against it - this is not an easy problem.
It seems that Firebase iOS implementation doesn't support offline caching of the client model. What this means in practice that:
For Firebase apps requiring an authentication, you need to first authenticate and wait Firebase finish the login (check the user identity, open a socket, etc.) before you can start moving the data. This will require 1-8 seconds (usually 2-5) depending on the network conditions, at least here in Finland.
After authenticating, Firebase first downloads the initial set of data and initializes the client cache. The time to perform this depends on the size of the data you add listeners for, but it's usually quite fast.
The problem here is that if you're using Firebase to implement, for example a messaging app, you'd most likely want to show the user a previously cached version of the message threads and messages, before the actual connection with the backend server is established.
I'd assume the correct implementation for this would need to handle:
The client-side model <-> Firebase JSON mapping (I use Mantle for
this)
Persisting the client-side model to disk (manual implementation using NSKeyedArchiver, or Core Data or such?)
Synchronizing the on-disk model with the Firebase-linked model in memory, when the connection is available (manual implementation?)
Has anyone come up with a solution (own or 3rd party) to achieve 2) and 3)?
It seems Firebase has solved this problem since this question was asked. There are a lot of resources on Offline Capabilities now with Firebase, including disk persistence.
For me, turning on persistence was as simple as the following in my AppDelegate:
Firebase.defaultConfig().persistenceEnabled = true
Assuming your app has been run with an internet connection at least once, this should work well in loading the latest local copy of your data.
There is a beta version of this technology within the client for iOS described here: https://groups.google.com/forum/#!topic/firebase-talk/0evB8s5ELmw give it a go and let the group know how it goes.
Just one line required for persistence with Firebase in iOS
FIRDatabase.database().persistenceEnabled = true
Can be found here in Firebase Docs