Realm in appdelegate.swift not working on other files - ios

This is a simple one probably but am I doing something wrong here :
I have this in my appdelegate.swift :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//configure Firebase
FirebaseApp.configure()
let realm = try! Realm()
return true
}
then I want to use refer to it in my view controller files like this :
do {
try realm.write {
realm.add(workoutData)
}
} catch {
print("Sorry no good")
}
but it says "use of unresolved identifier 'realm'".
But I thought the point of putting in app delegate was so you didn't need to do the 'let realm = try! Realm()' on every single view controller?
I have a firebase instance which is configured in appdelegate by doing :
FirebaseApp.configure()
and that works fine.
Am I missing something here? I can't find another answer that covers this so basically (there are some relating to migrations etc) so I'm assuming this is something really simple!

David Pastor answered this for me..
For any noobs that come this way....
You shouldn't declare it as a global variable anyway because that leads to issues as your app scales and if you did then the way i've done it wouldn't be right anyway!

Related

UITabBarItem in Xcode app has fatal error because found nil while implicitly unwrapping optional value. Not sure what's going wrong/ how to fix

Edit: Not sure if I can mark a question as solved but it was thanks to Alex. It was an issue with the newest version of Xcode but instructions meant for an older one. Moving the code fragments from AppDelegate to SceneDelegate fixed it.
I'm very new to Xcode and IOS app development. I'm taking my first course and we receive "guided labs" where we follow step by step instructions and copy paste bits of code to create an app. Following the instructions, I've gotten stuck at a certain spot and have tried googling/ researching to no avail.
The app is called Restaurant and essentially allows a user to look at a menu (see items/ pricing/ details) and add certain items to an order, which then shows in another tab called "Your Order". Those parts have been completed but the step I'm on is "to update the badge value of the Order tab to match the number of items in the order. Then the user can see that they've successfully added an item without needing to switch tabs." The UITabBarItem is an optional as stated in the instructions inside AppDelegate but is nil.
This line produces the error: Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
The error trail seems to originate in updateOrderBadge() specifically with adjusting the orderTabBarItem being the main issue. Commenting out that single line, everything runs although of course I don't get the desired badge notification. Not sure why that line which comes directly from the guide is having issues:
#objc func updateOrderBadge() {
orderTabBarItem.badgeValue = String(MenuController.shared.order.menuItems.count)
}
I've seen this or a similar error before when I forgot to connect IBOutlets/ Actions from button to code, is there supposed to be something like that for UITabBarItem?
For some overall context, here are the instructions and the code in screenshots:
Screenshot of specific lab instructions that I thought I followed step by step/ copy and pasted directly
A screenshot of the storyboard for context
Code inside AppDelegate that I'm altering
Otherwise here is the code in app delegate in text:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var orderTabBarItem: UITabBarItem!
#objc func updateOrderBadge() {
orderTabBarItem.badgeValue = String(MenuController.shared.order.menuItems.count)
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
orderTabBarItem = (self.window?.rootViewController as? UITabBarController)?.viewControllers?[1].tabBarItem
NotificationCenter.default.addObserver(self, selector: #selector(updateOrderBadge), name: MenuController.orderUpdatedNotification, object: nil)
return true
}
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
It seems like you're using Xcode 11 for your project, which is why you have a SceneDelegate.swift file in your project, and UIScene-related methods in your AppDelegate.swift file.
By default the template in Xcode 11 is using a SceneDelegate and all the related methods that came with iOS 13, so the app launch process differs a lot from the one in iOS 12.
In iOS 13, the window is no longer a property of the AppDelegate, but is instead managed by default by the SceneDelegate.
I think the project for the course you're following now, has been created with an earlier version of Xcode, which is why they're asking you to add a window property in AppDelegate, and initialize it in didFinishLaunchingWithOptions method, and the problem is that this is not the correct way to do it anymore in iOS 13.
So long story short, by default the app delegate now uses the default scene configuration, and the scene delegate is responsible for setting the UIWindow object via the scene(_:willConnectTo:options:) delegate method. By default, it uses the Main.storyboard to create your first screen.
In your case to solve your problem, you would simply need to access the window property from the scene(_:willConnectTo:options:) delegate method of the SceneDelegate like so:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var orderTabBarItem: UITabBarItem!
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
orderTabBarItem = (self.window?.rootViewController as? UITabBarController)?.viewControllers?[1].tabBarItem
}
// ...
}
If you want to make your project compatible for iOS 12 as well (and keep a window property in the AppDelegate), you could also do that, but I would not recommend.
Anyway, you can find more interesting information at the link below, which shows the difference between the old launch process, and the new launch process, and more importantly explains how you can set up the new launch process in iOS 13, whether you decide to use Storyboards or not:
https://learnappmaking.com/scene-delegate-app-delegate-xcode-11-ios-13/#using-scene-delegate-with-storyboards
Feel free to ask if you still have a doubt, and good luck for your course :)
You seem to have missed a step where window is initialized. Try initializing the window inside didFinishLaunchingWithOptions method. Also, add the observer after the orderTabBarItem is set. Like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
if let tabBarController = window?.rootViewController as? UITabBarController {
if let tabBarItem = tabBarController.viewControllers?[1].tabBarItem {
orderTabBarItem = tabBarItem
print("Successfully set orderTabBarItem")
} else {
print("TabBarController didn't have any viewControllers")
}
} else {
print("rootViewController isn't a UITabBarController")
}
NotificationCenter.default.addObserver(self, selector: #selector(updateOrderBadge), name: MenuController.orderUpdatedNotification, object: nil)
return true
}

How to convert this method from Objective-C to Swift?

I'm trying to convert a method from the React-native Firebase documentation, from Objective-c code to Swift. But it's not working, can someone lend me a help?
The doc I'm following: https://rnfirebase.io/docs/v4.3.x/notifications/ios
This is the Object C part I want to convert and add to the Swift file:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
}
At the moment, this is my code, see the commented part:
import UIKit
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var bridge: RCTBridge!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
RNFirebaseNotifications.configure()
let jsCodeLocation: URL
jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index", fallbackResource:nil)
let rootView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "Zepplin", initialProperties: nil, launchOptions: launchOptions)
let rootViewController = UIViewController()
rootViewController.view = rootView
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
return true
}
/*func application(_ application: UIApplication, didReceiveLocalNotification notification: [UILocalNotification: Any]?) -> Any {
RNFirebaseNotifications.instance(_, didReceiveLocalNotification, notification)
}*/
}
But of corse, it's not working, because the syntax is wrong... I'm not familiar with Swift.
Can someone lend me a hand?
I would be glad! Thankss in before-hand!!!
EDIT:
Sorry if my question was not clear enough. For me, the question was, "how to fix the syntax?", I knew that the syntax was wrong. I had nothing to add to my question, the error Xcode was giving me was expected, obvious because I don't know Objective-C, nor Swift, I was trying in my way. Even if I added the error to the question, I'm sure that something else would error too. That's why I asked if someone could convert it for me, it's only a few lines, so I think I was not asking too much.
Thanks #bsod, for mentionating the tool you posted in the comment!! This is exactly the tool I needed. I didn't know that something like this tool existed.
Thanks for everyone that helped me.
func application(_ application: UIApplication,
didReceive notification: UILocalNotification)
But this method is deprecated. You might want to consider migrating to the "UNUserNotificationCenter" related APIs.

how to call func application from didFinishLaunchingWithOptions [duplicate]

This question already has answers here:
loading url from scheme not processing first time - appdelegate vs viewcontroller
(2 answers)
Closed 3 years ago.
I would like to know how to call a function from another function within AppDelegate. It would be better to call this function from ViewController but could not get it to work.
I have in my AppDelegate.m the following code:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let geturl = url.host?.removingPercentEncoding;
UserDefaults.standard.set(geturl, forKey: "DeepLinkUrl")
return true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//I WANT CALL the upper function to set the URL IN HERE
return true
}
Since I don't know how to call the open url function from the ViewController.m I did this calling didFinishLaunchingWithOptions func from AppDelegate.m
My ViewController.m looks like:
#objc func appWillEnterForeground() {
print("app on foreground")
let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
appDelegate?.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
//ACTUALLY I WANT TO CALL THE SET URL FUNCTION IN STEAD OF didFinishLaunchingWithOption BUT DON'T KNOW HOW. SO I FOUND THIS WHICH IS BEING CALLED
let user = UserDefaults.standard
if user.url(forKey: "DeepLinkUrl") != nil{
let str = user.value(forKey: "DeepLinkUrl") as! String
print(str)
}
}
Any ideas?
You don't call this method at all. It is called by the operating system when your application is launched. You are absolutely not supposed to ever call it yourself.
Same with the other method, which will be called by the operating system when your application is asked to open a URL. Which might be the URL of a file, or a URL with a scheme that you registered for.
Set a breakpoint on appDidFinishLaunching, then on your viewDidLoad method, start debugging to get some idea what is going on. You might also consider reading Apple's documentation.

Swift crashes - SIGABRT - Also how to track crashes

I am completely new to Swift, Xcode and programming on Mac.
I have started working on some kind of app, and all I really did was add Firebase (Which worked), and then try to add Google Sign In. Nothing else exists really. So There really was a Google Sign In Button. I think the crash started happening when I connected a property to the Google Sign In Button.
Alas, I am new to this, and I don't even know how to debug properly. Can someone please be of assistant ?
My code :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
// Override point for customization after application launch.
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID =
FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
override func viewDidLoad()
{
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
}
This is the property:
#IBOutlet weak var googleLoginButton: GIDSignInButton!
And then try to run your project so you get that line where you app crashes

The default app has not been configured yet

I'm trying to upgrade my app to the new version of Firebase. I went through the setup guide and edited all of my code to match the new syntax. However, when I run the app, I get these two errors.
The default app has not been configured yet.
Terminating app due to uncaught exception 'MissingDatabaseURL', reason: 'Failed to get FIRDatabase instance: FIRApp object has no databaseURL in its FirebaseOptions object.'
I have FIRApp.configure() in the AppDelegate and I have the GoogleServices-Info.plist imported into my project. The plist has all of the correct info as well. Anyone else running into this or know how to fix it?
Here's the answer to your problem:
To configure Firebase you have to execute FIRApp.configure() somewhere. After this is done you can use let firebaseDatabaseReference = FIRDatabase.database().reference() to get a reference to that database and start using it. The problem isn't with Firebase "per se" but with how Swift behaves.
If you put FIRApp.configure() in your AppDelegate func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool and then in the MyDatabase class you use let firebaseDatabaseReference = FIRDatabase.database().reference() outside of your declared functions sometimes the code FIRDatabase.database().reference() executes before the application didFinishLaunchingWithOptions function is executed.
Essentially your class is trying to get a reference to the Firebase database before it has a chance to configure itself, generating the error in the console "The default app has not been configured yet."
Note: This doesn't happen all the time, sometimes the application is slow to start, in iOS Simulator for example, and it doesn't have a chance to finish before MyDatabase "let" executes and tries to get a reference.
That is why moving the FIRApp.configure() code to override init() in AppDelegate works, essentially it makes sure the configure code gets executed when AppDelegate is initialised (in this and most cases, before MyDatabase is initialised)
override init() {
super.init()
FIRApp.configure()
// not really needed unless you really need it FIRDatabase.database().persistenceEnabled = true
}
Also make sure you super.init() (so you super classes get the "message") so you override doesn't do more harm than good.
I'm also using Fabric and in my case it was the order of Fabric and Firebase initializations. I had to initialize Firebase first.
So changing
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Fabric.with([Crashlytics.self])
FirebaseApp.configure()
...
}
to:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
Fabric.with([Crashlytics.self])
...
}
fixed the problem.
In AppDelegate.m, outside of didFinishLaunchingWithOptions,
override init() {
FIRApp.configure()
FIRDatabase.database().persistenceEnabled = true
}
Make sure you are having DATABASE_URL key in your GoogleService-Info.plist
Swift 5 - Easy Solution
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
//MARK:- This function will auto run and firebase will configure successfully
override init() {
super.init()
FirebaseApp.configure()
// not really needed unless you really need it
FIRDatabase.database().persistenceEnabled = true
}
Happy Coding
iOS 9.2
Swift 2.1.1
Xcode 7.2.1
Mac OSX 10.10.5
Same error here using the following code:
AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
return true
}
ViewController.swift:
import UIKit
import Firebase
class ViewController: UIViewController {
var db = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Create some data in Firebase db:
db.child("key").child("subkey").setValue("hello world")
}
I also added the file GoogleService-Info.plist to my project directory as described in the Firebase Getting Started Guide.
And I made my Firebase db public with the following Rules:
{
"rules": {
".read": true,
".write": true
}
}
Making the following changes to ViewController.swift is what worked for me:
class ViewController: UIViewController {
var db: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
db = FIRDatabase.database().reference()
db.child("key").child("subkey").setValue("hello world")
}
Prior to running my app, my Firebase db looked like this:
myiosdata-abc123: null
After running my app, my Firebase db looked like this:
myiosdata-abc123
- key
|
+--- subkey: “hello world”
I had several normal working projects with FIRApp.configure () code in AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
return true
}
Everything worked great for quite some time, but yesterday and today I opened a Swift 3 project inside my Xcode 7.3.1 (I am still working in Swift 2, but opened Swift 3 project to see what is changed), and suddenly in all my Swift 2 apps and projects that I am still working on, got the same error:
The default app has not been configured yet
Every project now when I open in XCode, getting same error, I didn't know what to do, but after implementing #MichaelWilliams' code, everything works fine again.
I have debug my Xcode (clear and reload console), but nothing works beside this new approach by Michael.
This one resolved my problem:
override init() {
FIRApp.configure()
FIRDatabase.database().persistenceEnabled = true
}
Can this new code somehow make my app's unstable and can I be afraid to see problems with connecting/working with Firebase database now?
Try re-download GoogleService-Info.plist from your console and add it to your project, That worked for me!
If you are using Xcode 9, Swift 4 and Firebase 4 please do the following:
override init() {
FirebaseApp.configure()
Database.database().isPersistenceEnabled = true
}
The cleanest solution to me here is to have lazy properties in case you want to have the db on top of your file. So let's say you have a FirebaseService class where you want to have Firestore.firestore() db constant to use it in all of the functions in that class:
private lazy var db = Firestore.firestore()
Or if you are using Firebase Storage:
private lazy var storage = Storage.storage().reference()
Also keep in mind that if you are referencing the Database/Storage in init() of your classes, you still might have the same problem so avoid that.

Resources