Firebase exception error for macOS app Xcode - swift

When I try to run the simulator for my macOS app, which is using Firebase, it gives this error: "Thread 1: "The default FirebaseApp instance must be configured before the default Authinstance can be initialized. One way to ensure this is to call FirebaseApp.configure() in the App Delegate's application(_:didFinishLaunchingWithOptions:) (or the #main struct's initializer in SwiftUI)." I notice this happened after I created an environment object.
Here is my code:
import SwiftUI
import FirebaseCore
#main
struct testagainApp: App {
#NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
let viewModel = AppViewModel()
ContentView()
.environmentObject(viewModel)
}
.windowStyle(HiddenTitleBarWindowStyle())
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
FirebaseApp.configure()
}
}
If I get rid of let viewModel = AppViewModel() and .environmentObject(viewModel), the simulator runs just fine. If I put the app delegate first, the simulator runs but nothing appears. I am new to Swift and am unsure how to fix this.

Option 1:
I faced same issue a while ago, then noticed that I have not linked the
GoogleService-Info.plist file to the project, if you see its already added then better remove and add it to the project via Xcode again.
Hope that solves your issue.
Option 2:
Make sure you don't have more two copies of Firebase linked into your app. If so you can remove one. See this doc on the Firebase iOS SDK for more details.

I solved it. Instead of using the AppDelegate I used the App's initializer:
struct testagainApp: App {
init() {
FirebaseApp.configure()
}
// ...
}

Related

How to add code to AppDelegate using SwiftUI Life Cycle

I'm following Google's tutorial on how to integrate AdMob ads into my app. In the tutorial, they put some code into the App Delegate. I've only ever used the SwiftUI life cycle for apps, so I'm not even sure what the App Delegate is. However, the issue is that in the tutorial, they instruct to put this code in the App Delegate file:
[[GADMobileAds sharedInstance] startWithCompletionHandler:nil];
I'm trying to put this code into the init() of my app (aka the SwiftUI version of putting it into my App Delegate). However, as this doesn't seem to be Swift code, I can't use that line of code without receiving errors. What should I do to insert this code into my app init()?
Following this tutorial at around 3:10 into the video.
The init might be too early, try in app delegate as follows
import GoogleMobileAds
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
GADMobileAds.sharedInstance().start(completionHandler: nil) // << here !!
return true
}
}
#main
struct YourApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

Swift Command Line Tool Not Receiving DistributedNotificationCenter Notifications

I am trying to create a very basic Swift command-line application that signals to another application using a WebSocket when the macOS UI changes to/from light/dark mode.
For some reason, the command-line tool is not receiving any notifications from DistributedNotificationCenter, in particular, AppleInterfaceThemeChangedNotification. However, running the exact same code in a Cocoa UI app on applicationDidFinishLaunching works perfectly fine.
I found an old Obj-C CLI project on Github that is meant to print out every notification, but that doesn't do anything either. It makes me suspect Apple perhaps changed something, but I cannot seem to find anything online about it. Are there certain Xcode project settings I need to set?
// main.swift
import Foundation
class DarkModeObserver {
func observe() {
print("Observing")
DistributedNotificationCenter.default.addObserver(
forName: Notification.Name("AppleInterfaceThemeChangedNotification"),
object: nil,
queue: nil,
using: self.interfaceModeChanged(notification:)
)
}
func interfaceModeChanged(notification: Notification) {
print("Notification", notification)
}
}
let observer = DarkModeObserver.init()
observer.observe()
RunLoop.main.run()
I managed to get iTunes notifications working, so it was just the theme change notifications that weren't working. Given this, I suspect Apple only sends the notifications to UI/NSApplication applications. As such, replacing the last 3 lines from above with the following works:
let app = NSApplication.shared
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
let observer = DarkModeObserver.init()
observer.observe()
}
}
let delegate = AppDelegate()
app.delegate = delegate
app.run()

Swift: Perform Segue in AppDelegate.swift

I've successfully integrated Google Sign-In into my iOS app in my AppDelegate.swift file, and can successfully detect a successful sign-in (by printing "success" out onto the console). The issue is that after a successful sign-in, I'm back at the Google Sign-In page when I want to be in the next screen of the app.
The AppDelegate.swift file did not recognize the performSegue function, as it is a function of the UIViewController class (please correct me if I'm wrong). To get around this, I created a global variable in the ViewController file such that the segue would be performed whenever this value would be changed:
AppDelegate.swift:
var userSignedInGlobal = "n/a"
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
// A bunch of code that implements the Google Sign in ...
print("Successfully logged into Google.", user)
userSignedInGlobal = "success"
}
And then my InitialViewController.swift file:
class InitialViewController: UIViewController, GIDSignInUIDelegate {
var userSignedIn = userSignedInGlobal {
didSet {
performSegue(withIdentifier: "segueOne", sender: self)
}
}
// A bunch of irrelevant code.
}
This did not work, and the reason I think this didn't work is that userSignedInGlobal in the InitialViewController.swift file is being passed by reference - so even when its value changes, the value of userSignedIn does not change (again, please do correct me if I'm wrong).
To get around this, I changed my InitialViewController.swift file as follows:
var userSignedIn = userSignedInGlobal {
didSet {
doSegue()
}
}
class InitialViewController: UIViewController, GIDSignInUIDelegate {
func doSegue() {
performSegue(withIdentifier: "segueOne", sender: self)
}
// A bunch of irrelevant code.
}
This gave me an error in the third line: "Use of unresolved identifier 'doSegue()' "
I do not know how to go about performing the segue when the sign in is successful. Any help will be greatly appreciated, thanks in advance.
There are a few problems here:
Global Variables in App Delegate
There are numerous answers demonstrating how to store a global variable in your AppDelegate and then subsequently reference that variable in your view controllers, e.g. getting a reference to app delegate
Perform Segue only from Storyboard based controllers
You won't be able to use performSegue unless your current View Controller was loaded from a StoryBoard (see performSegue). This probably is not the case with whatever Google view you are loading from your AppDelegate.
Are you doing the login in the right place?
You might want to reconsider the division of duties between your AppDelegate and your initial view controller. Perhaps you are trying to do too much in the AppDelegate. Perhaps you should move some of this code (showing the Google form) to your initial VC and then either segue or reset your Navigation Controller based on the result.
Hope this helps.

Setting Realm schema version in MacOS app

Unlike iOS, the app delegate's applicationWillFinishLaunching and applicationDidFinishLaunching are called after ViewDidLoad on the initial NSViewController.
Main.c (or main.swift) seems to be retired and without changing AppDelegate too much, the only place I can think of to call schemaVersion is in the ViewDidLoad of the initial view controller, which seems ugly to me.
What is recommended way to set Realm's schemaVersion in MacOS apps written in Swift?
The init() of the MacOS AppDelegate seems to be working great.
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
super.init()
let info = Bundle.main.infoDictionary!
let version = info["RealmSchemaVersion"] as! UInt64
Realm.Configuration.defaultConfiguration.schemaVersion = version
}
...
}

WatchKit didReceiveApplicationContext not being called

I can't get didReceiveApplicationContext to be called. Any ideas?
InterfaceController:
import WatchKit
import Foundation
import WatchConnectivity
class InterfaceController: WKInterfaceController, WCSessionDelegate {
#IBOutlet var colorLabel: WKInterfaceLabel!
private let session: WCSession? = WCSession.isSupported() ? WCSession.defaultSession() : nil
override init() {
super.init()
session?.delegate = self
session?.activateSession()
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
}
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]){
let colors : String = applicationContext["color"] as! String
colorLabel.setText(colors)
NSLog("session did receive application context")
}
}
I've been following along with this tutorial: http://www.kristinathai.com/watchos-2-how-to-communicate-between-devices-using-watch-connectivity/
No NSLog or setting of the colorLabel happens. No idea what I'm missing. Thanks!
This seems to be a typical 'development phase' problem!
The WCSession.defaultSession.applicationContext is buffered on the iOS device, so it is only transferred to the watch (extension) once if it doesn't change.
This lead to the strange finding, that watch extensions 'didReveiveApplicationContext:' seems not to be called, when WCSession.defaultSession.updateApplicationContext is called in the iOS app again. (Try to call WSSession.defaultSession.receivedApplicationContext in the extension to find, that the earlier transferred context is in fact available)!
In test situations, it is very helpful to add a 'changer' object to the context dictionary (like an UUID object, or - maybe even better - a NSDate.date). This will ensure, that the context has changed (compared to the buffered one) and gets transferred again (leading to a call to didReceiveApplicationContext) :-)
NSError* error = nil;
[WCSession.defaultSession updateApplicationContext:#{ #"yourKey" : #"your content",
#"forceTransfer" : NSDate.date }
error:&error];
And: Don't forget to remove this in the production version of your app, as - of course - this leads to unneeded data transfer between your app and your watch extension!
PS: The checked answer solves this problem by creating a new app. And flushing all buffers this way...
I had the same problem. In my case it helped to just close both simulators and then run the Watch scheme. This opens up both simulators again in connected state.
Hoping it helps!
I copied the above code into a new watch app and it works fine. The error must lay on the sending side. Are you certain the code in the iOS app is being called? I presume you are using Xcode and two simulators, one for the iOS app and one for the WatchApp.
The code on the iOS side is not run unless you open the app on the phone simulator. Where and how on the iOS side are you issuing the updateAppContext call?
In my test, this is all that I added to my ViewController.swift on the iOS side (This code will not be triggered until I start the iOS app on my iPhone.)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
do {
try session.updateApplicationContext( ["color" : "Red" ])
} catch _ {
}
}
For me, when doing some testing/debugging i tried to fire updateApplicationContext in AppDelegate. This caused the didReceiveApplicationContext not being called.
Moving this logic to a later point, like in a UIViewController made it work for me at least.
Check the session to make sure its isPaired and watchAppInstalled properties are both YES. It seems like updating the shared context while these are NO will not work.
I was having this issue. Made a change to not update the context when either condition was NO. Added an implementation of sessionWatchStateDidChange:, and if both conditions were YES, updated the context. It worked.
I suspect this in combination with another issue where the phone will not send the context if the data is not different causes the "never updating" issue. A workaround of passing a "uuid" did help but I suspect the above is a better fix.
In my case, I used the following code to send my application context:
do {
try session.updateApplicationContext(applicationContext)
} catch let error {
throw error
}
and neither didReceiveApplicationContext was called, nor an error was thrown.
My problem was that applicationContext contained a custom object, whereas only property list items are allowed.
The strange point is that no error was thrown.