Changing Firebase configuration at runtime on iOS - ios

For my project, I have 2 environments:
test
prod
and 2 localizations:
en
tr
and an app for each combination: (these names do not mirror Firebase project ids)
test_en
test_tr
prod_en
prod_tr
And I have a separate plist file for each of these apps in the project, with the names Firebase-(app_type).plist ie Firebase-test_en.plist or Firebase-prod_en.plist
I am initializing FIRApp using this way:
private func ensureLoaded() {
let options = FIROptions(contentsOfFile: Bundle.main
.path(forResource: "Firebase-\(firebaseApplicationName)", ofType: "plist"))!
if nil == FIRApp(named: firebaseApplicationName) {
FIRApp.configure(withName: firebaseApplicationName, options: options)
}
}
And it seems to work ok, until I try to actually connect to firebase database, where it says:
Firebase is not configured correctly, please call [FIRApp configure]
So my question is, how do I make firebase work where I want to dynamically change configurations? There are some other questions that describe how to change configuration based on some environment variables / compiler flags but they are all referring to changing the plist file, which changes the configuration during build time, but I want to change it dynamically during runtime. Is it supported at all, is there a way to make it work?
Thanks in advance.

I found what the problem was, we were using FIRApp.auth().currentUser somewhere in the app, which returns the auth for "default" app and since we had multiple configs, we had no notion of a "default" app, so the configuration was incomplete. I changed that to FIRAuth(app: myApp)?.currentUser and it works as expected now.

Related

configure firebase with options is crashing in Xcode 11 and Firebase 6.19

I'm trying to register a second firebase application in my app. The first one is correctly registered inside didFinishWithLaunchingWithOptions method (by default is using the GoogleService-Info.plist)
FirebaseApp.configure()
Then I need to retrieve a new googleAppId from a server and configure a new application using this new googleAppId and using my iOSID of curse. I am doing all this registration inside AppDelegate. I'm registering this way:
let options = FirebaseOptions(googleAppID: String(format: "1:%a:ios:%a", googleID, iOSID), gcmSenderID: googleID)
//Deleting the first app in order to register the second
let app = FirebaseApp.app()
app?.delete { _ in }
FirebaseApp.configure(options: options)
It was working well until I update my firebase version from 6.2 to 6.19, now the app is crashing in FirebaseApp.configure(options: options)
The log says:
Terminating app due to uncaught exception 'com.firebase.installations', reason
'[Firebase/Installations][I-FIS008000] Could not confiure Firebase Installations die to invalid
Firebase options. The following parameters are nil or empty: `FirebaseOptions.APIKEY`. If you
use GoogleServices-Info.plist please download the most recent version from the Firebase Console.
If you configure Firebase in code, please make sure you specify all required paramaters.
Is worth mencioning that a new Framework was added to Analytics in Firebase version 6.15 so I had to added it in order to make my app compile again.
I also tried to add a new parameter inside the FirebaseOptions but is still asking for the same parameters, it is not expecting an APIKEY which i don't know where to get it
The message comes from here as far as I can see. The check done there shows that these three values must always be specified:
if (appName.length < 1) {
[missingFields addObject:#"`FirebaseApp.name`"];
}
if (appOptions.APIKey.length < 1) {
[missingFields addObject:#"`FirebaseOptions.APIKey`"];
}
if (appOptions.googleAppID.length < 1) {
[missingFields addObject:#"`FirebaseOptions.googleAppID`"];
}
From the error message and your code it looks like you're not specifying the APIKEY in your FirebaseOptions, which is required according to the SDK.

How to get App name in UITesting swift 4 iOS

I tried to get my app name in UITesting. I tried the code below:
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
But it gives me the nil value result.
When I was trying to find out if the 'debugDescription' provides any information about the actual app (and not the test app) I stumbled across a label that looked exactly like the app's display name.
I don't know if it helps you, but the following simple line of code helped me:
XCUIApplication().label
Are you sure, that you've set Display name in your Info.plist file?
Just set this name in Project settings or in Info.plist:
Try to go to File > Workspace Settings if you are in a workspace environment or File > Project Settings for a regular project environment.
Then click over the little grey arrow under Derived data section and select your project folder to delete it.
And Product > Clean.
It helps in many situations.
I am getting app name in UI test like this:
let app = XCUIApplication()
// ...
let appName = app.label

Trying to use Realm Object Server Tutorial

I have created an Amazon Web Services EC2 instance and deploy one of the AMIs with a Realm Object Server as its documentation explains:
https://realm.io/docs/realm-object-server/#install-realm-object-server
Once installed and created my admin user, I have completed the iOS tutorial: https://realm.io/docs/tutorials/realmtasks/, just until point 7, enough for creating task, but when I add new task in app, nothing happens. Debugging, I notice that next sentence try, is not executing:
let items = self.items
try! items.realm?.write {
items.insert(Task(value: ["text": text]), at: items.filter("completed = false").count)
}
The items collection seems to be initialized properly:
In the ROS dashboard, can see the database referenced in Xcode:
In image can be see "Default permissions" property is no access, is this the reason of not creating new task? If so, how can I change that permissions? If that is not the reason, anyone could help me?
thanks in advance
The problem was that I did not follow al the complete tutorial because I do not want to use the desktop application, just mobile example, but realm init objects in desktop app, so I never got a valid realm where perform actions.
For a quick and simple start with this realm tutorial pointing to an online server, not local, you must initialize the TaskList object and add it to self.realm on setup
// Show initial tasks
func updateList() {
if self.realm.objects(TaskList.self).count == 0 {
let list = TaskList()
list.id = "000001"
list.text = "lista de prueba"
// Add to the Realm inside a transaction
try! self.realm.write {
self.realm.add(list)
}
}
if self.items.realm == nil, let list = self.realm.objects(TaskList.self).first {
self.items = list.items
}
self.tableView.reloadData()
}
checking if there is not a TaskList with if self.realm.objects(TaskList.self).count == 0 {, you can create one and init realm.
You probably forgot to launch Mac demo app first or login with a different user. The tutorial assumes that existing data will be synced at login. If you have never launched the Mac app or logged in a different user, it may happen that items are not managed by Realm.
The tutorial says the following:
First, please follow the Get Started instructions to get set up with the Realm Mobile Platform and to launch the macOS version of RealmTasks.
Also, you attempt to try this tutorial with ROS on AWS. The tutorial assumes running ROS on a same local machine.
So you should modify the Mac app code to connect to the AWS, then run it to synchronize the initial data. Then run the tutorial iOS app.
The default permissions here show whether all other users can access the Realm or not, which isn't the case here. We already have an internal issue around clarifying this.
The registered user who owns the Realm has individual permissions to it by default. If you wouldn't have permissions opening the synchronized Realm from the client would also fail because of insufficient permissions, so this isn't the problem here.
So going back to your code:
try! items.realm?.write { … }
My guess would be that the problem here is that the collection isn't already attached to a Realm, so that items.realm? evaluates to null. In that case the write transaction wouldn't be executed.
You can resolve this by making sure to add the items first to a Realm or executing the write directly on a synchronized Realm.

Unable to open a realm at path

I am trying to use a bundled realm file without success. I know that my realm file was copied successfully to my application’s Directory but I ca not read it.
fatal error: 'try!' expression unexpectedly raised an error: "Unable
to open a realm at path
'/Users/…/Library/Developer/CoreSimulator/Devices/…/data/Containers/Data/Application/…/Documents/default-v1.realm'.
Please use a path where your app has read-write permissions."
func fullPathToFileInAppDocumentsDir(fileName: String) -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,NSSearchPathDomainMask.UserDomainMask,true)
let documentsDirectory = paths[0] as NSString
let fullPathToTheFile = documentsDirectory.stringByAppendingPathComponent(fileName)
return fullPathToTheFile
}
In didFinishLaunchingWithOptions:
let fileInDocuments = fullPathToFileInAppDocumentsDir("default-v1.realm")
if !NSFileManager.defaultManager().fileExistsAtPath(fileInDocuments) {
let bundle = NSBundle.mainBundle()
let fileInBundle = bundle.pathForResource("default-v1", ofType: "realm")
let fileManager = NSFileManager.defaultManager()
do {
try fileManager.copyItemAtPath(fileInBundle!, toPath: fileInDocuments)
} catch { print(error) }
}
And setting the configuration used for the default Realm:
var config = Realm.Configuration()
config.path = fileInDocuments
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm(configuration: config) // It fails here!!! :-)
As the documentation suggests, I have tried as well to open it directly from the bundle path by setting readOnly to true on the Realm.Configuration object. I am not sure if this is something related to Realm or if I am overlooking something with the file system…. I have also tried to store the file in the Library folder.
Realm 0.97.0
Xcode Version 7.1.1
I tried to open the realm file using Realm's browser app and the file does not open anymore. It has now new permissions: Write only (Dropbox). So, I decided to change the file permission back to read/write using file manager’s setAttributes method. Here is how I did it:
// rw rw r : Attention for octal-literal in Swift "0o".
let permission = NSNumber(short: 0o664)
do {
try fileManager.setAttributes([NSFilePosixPermissions:permission], ofItemAtPath: fileInDocuments)
} catch { print(error) }
The realm file can now be open at this path.
That exception gets thrown whenever a low level I/O operation is denied permission to the file you've specified (You can check it out on Realm's GitHub account).
Even though everything seems correct in your sample code there, something must be set incorrectly with the file location (Whether it be the file path to your bundle's Realm file, or the destination path) to be causing that error.
A couple of things I can recommend trying out.
Through breakpoints/logging, manually double-check that both fileInDocuments and fileInBundle are being correctly created and are pointing at the locations you were expecting.
When running the app in the Simulator, use a tool like SimPholders to track down the Documents directory of your app on your computer, and visually ensure that the Realm file is being properly copied to where you're expecting.
If the Realm file is in the right place, you can also Realm's Browser app to try opening the Realm file to ensure that the file was copied correctly and still opens properly.
Try testing the code on a proper iOS device to see if the same error is occurring on the native system.
If all else fails, try doing some Realm operations with the default Realm (Which will simply deposit a default.realm file in your Documents directory), just to completely discount there isn't something wrong with your file system
Let me know how you go with that, and if you still can't work out the problem, we can keep looking. :)
This will occur if you have your realm file open in Realm Studio at same time you relaunch your app. Basically in this case Realm can't get write permissions if Studio already has them.
To add to the solution based on what I've discovered, make note of what error Realm reports when it throws the exception, as well as the type of Error that is passed.
As of writing, Realm documents their errors here:
https://realm.io/docs/objc/latest/api/Enums/RLMError.html
What this means is that you can find out if your Realm file has permissions problems and react to those based on Realm passing you a RLMErrorFilePermissionDenied. Or if the file doesn't exist with RLMErrorFileNotFound.
The tricky thing I'm finding is when you get a more generic RLMErrorFileAccess, but that's for another question on Stack Overflow...
I had the same issue and tried too many ways to fix it. The easiest way to fix this problem is manually creation of the folder XCode cannot reach '/Users/…/Library/Developer/CoreSimulator/Devices/…/data/Containers/Data/Application/…/Documents/...' as explained at https://docs.realm.io/sync/using-synced-realms/errors#unable-to-open-realm-at-path-less-than-path-greater-than-make_dir-failed-no-such-file-or-directory
Once you created this folder and run the project, XCode creates the Realm files inside this folder automatically.

Unable to access Realm database in app group folder

I am unable to access data stored in my realm database after moving it into an app group container in order to use with a watch kit extension. I tried following this Guide, I can successfully print the path to the default Realm after moving it into the app group file, but If I try to print out the amount of objects in the realm it returns 0, which causes my Table to be empty. I have app groups enabled in both my ios, and watch targets with the correct app group name. Heres where I set the default realm folder inside the app delegate.
let directory: NSURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.io.github.brady131313.Deck-of-Death")!
let realmPath = directory.path!.stringByAppendingPathComponent("db.realm")
Realm.defaultPath = realmPath
Verify that the directory returned by NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.io.github.brady131313.Deck-of-Death") points to a path containing Containers/Shared/AppGroup. It will happily return a path within your normal container if you've made a typo in the App Group ID or don't have the necessary entitlements / provisioning profile set up. You should see the same path returned within both your extension and your parent application.
You may want to review Realm's sample WatchKit app for a working example of using Realm with App Groups. You can find it in the Xcode project at https://github.com/realm/realm-cocoa/tree/master/examples/ios/objc.
The problem was that I had set the default realm path after let realm = Realm(), so it was defaulting to the old path instead of the new one.
You set realmPath as directory.path but it isn`t. Change the realmPath difinition.

Resources