Problem
I want my iOS application to connect to my locally running Emulators, and NEVER bypass the emulator to hit my production services. I've configured everything properly according to hours and hours of scouring across Firebase documentation, and even dipping into StackOverflow questions and GitHub PRs.
Currently, I've got Emulators running for Auth, Functions, and Firestore. As far as I'm aware, the hosts and ports are configured properly. My local functions run fine. I can use the UI.
The moment my authentication code runs, it authenticates using my production service, not my locally running emulator suite. It never even acknowledges the emulator suite.
Code Snippets
In my emulator suite's firebase.json
, I have the following code:
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
},
"emulators": {
"auth": {
"host": "0.0.0.0",
"port": 9099
},
"firestore": {
"host": "0.0.0.0",
"port": 8080
},
"functions": {
"host": "0.0.0.0",
"port": 5001
},
"ui": {
"enabled": true
}
}
}
When the emulator suite launches, I see the following output:
┌────────────────┬──────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼──────────────┼─────────────────────────────────┤
│ Authentication │ 0.0.0.0:9099 │ http://localhost:4001/auth │
├────────────────┼──────────────┼─────────────────────────────────┤
│ Functions │ 0.0.0.0:5001 │ http://localhost:4001/functions │
├────────────────┼──────────────┼─────────────────────────────────┤
│ Firestore │ 0.0.0.0:8080 │ http://localhost:4001/firestore │
└────────────────┴──────────────┴─────────────────────────────────┘
In my iOS app, I do the following:
// FirebaseOptions is populated with a GoogleService-Info.plist per environment,
// then passed into this function.
// However, even using FirebaseApp.configure() changes nothing.
private func initialize(options: FirebaseOptions) {
FirebaseApp.configure(options: options)
// If we aren't running on local emulator, do nothing else.
guard isRunningLocalEmulator else {
return
}
let host = "0.0.0.0"
// Auth
Auth.auth().useEmulator(withHost: host, port: 9099)
// Firestore
let settings = Firestore.firestore().settings
settings.host = "\(host):8080"
settings.isPersistenceEnabled = false
settings.isSSLEnabled = false
Firestore.firestore().settings = settings
}
Additional Context
- I'm using multiple
GoogleService-Info.plist
files per environment. However, I have tested my code using only a singleGoogleService-Info.plist
just to ensure this wasn't causing an issue. There was no change to my issue.
Links
What I've Tried
- Using both
localhost
and0.0.0.0
as my emulator hosts. - Running on different local simulators.
- Upgrading and downgrading Firebase to various versions, just to check if something broke.
- Building on both Xcode 12.5.1 and Xcode 13 RC.
- Running on both iOS 14.9 and iOS 15.0.
Questions
- How do I initialize a
FirebaseApp
such that it could NEVER have any context of my real projects, so that if local emulation fails for any reason, it will return errors instead of using my production services? - There are two ways to initialize Firestore local emulator. I'm currently using the snippet shown here, but there is also the
Firestore.firestore().useEmulator(withHost:port:)
method, which is used by theAuth
service. I can't tell which is more appropriate. - Why is my application bypassing my local emulators to begin with? (Maybe 1 + 2 will answer this, but I'm stumped.)