I have an iOS app that uses Parse as backend. There, some cloud code is executed.
Both have to share the same constants.
I can share these constants on the client side via obj-c #import, and I can share it on the cloud code side via module.exports / require.
But how can I share it between client code and cloud code? It is simply error prone to define the same constants twice.
Parse offers a config object that can be queried like a class (returning an NSDictionary in iOS). Moreover, it can be configured via the web UI at parse.com. See docs here.
Upon startup, your app can retrieve the config and cache it locally. You may choose to cache it semi-permanently (say, with NSUserDefaults) and then use local copy indefinitely. I usually opt for some fixed expiration period (like weekly, so my constants are not-quite constant). Start up logic is, if the interval between now and my last config fetch exceeds the week, fetch again and replace.
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
from what I understood Cloud Code is like a server, we can declare functions (only javascript?) to make app running lighter. Should I put every query on Cloud Code instead of iOS app?
You don't have to put every query on Cloud Code but you can use it to host functions that should rest in a sever or you would like to change without requiring a app update.
As a quick example, you can create a function in cloud code to send a mail to a user friend, calculate some number with a variable that you don't know and want to change from time to time, or make some safety check with other servers before a payment.
I am completely dependent on Rest Kit for my app for network calls. I want to see the logs of how much
1) Time taken by each API to get a response
2) Size of Request/Response Payload
3) URL of the API
Is there any way I can enable such logging in Restkit. My app is calling like 50-60 API an dI don't want to dig into entire code base an add manual logs. Also I don't want to use network profiling tool since I will be tracking this data when an actual user is using the application.
Cant also use any third party paid tool so want to log these values in application database.
RestKit does have a log you can enable, but that isn't what you want to do if you plan to actually release this. It also writes to the log, not a value you can actually process and save.
Your likely best option is to subclass the RKObjectManager and intercept the requests that are being placed and the NSURLRequests which are being generated.
My app relies on an external service that might change its output any time without warning, so I would need a completly new function to parse it. Is there anyway to update my service parser without having to re-submit the whole app for review? Otherwise part of my app would be broken during the time to develop and review the new parser. I was told I cannot use bundles for this, so I really am clueless how to solve this problem.
You can't solve that problem completely on the client side.
Depending on the output format of the external service, and the methods you use to parse its output, you might have the option to store a file in a server that contains information about the current output format of the external service. Then your app can use the meta-data in that file to determine how to do the parsing.
You can also develop a simple web service that wraps the external service. Then your app can use the web service instead of the original service, and whenever the output of the original service is changed, you can quickly update your web service to make your app continue functioning properly.
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