Firebase observer during all the app life - ios

I'm trying to create a firebase observer that remains alive during all the app life. What I want is to change a property of my tabBarController when some data change in firebase. Here's my code:
self.ref.child("mySubRef").observe(.value , with: {snapshot in
self.tabBarController?.tabBar.items?[3].badgeValue = "!"
})
So, I've tried creating it in the viewDidLoad of my first viewController and also in the viewDidAppear. I don't remove it since I want it to be there always. In the viewDidAppear it works only if I'm in that viewController at the moment of the change. If I want that change to happen no matter where I am (always inside the tabBar) where do I have to put that code?
Thanks for the help!

I have found the answer. The problem was that when I changed between viewControllers the reference to the observer was deallocated. So, to fix it, I have created a class like this:
class NotificationListener: NSObject {
let ref:FIRDatabaseReference = FIRDatabase.database().reference()
var user:User?
func setUpListener(tabBarController:UITabBarController){
self.user = User()
self.ref.child("users/" + self.user!.uid + "/notifications").observe(.value , with: {snapshot in
tabBarController.tabBar.items?[3].badgeValue = "!"
})
}
}
Now I have a property of that class in every viewController and every one has a reference to the same object. When I change between VC it will not deallocate the object because it will still be referenced.

I think, you can use Appdelegate didFinishLaunchingWithOptions method but I'm not sure.
Like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
FIRDatabase.database().reference().child("mySubRef").observe(.value, with: { (snapshot) in
//I'm not sure for this part.
UITabBarController.init().tabBar.items?[3].badgeValue = "!"
})
return true
}

Related

How to track changes in currently visible ViewController as user navigating the screens in iOS?

I'm trying to replicate Firebase Analytics behaviour, which automatically fire screen events whenever ViewController screen get's changed with another.
Though I'm able to find currently visible ViewController using :
UIApplication.shared.windows.first?.rootViewController?.presentedViewController
But I need some way to get notified for any change in rootViewController. I tried to observe this rootViewController using KVO, but I don't get any callback. I found that KVO only works on NSObject with dynamic properties.
Is there any way I could receive callback for change in ViewController? Since this will be a library project, I couldn't make changes in main code to support the feature.
Following solution worked for me:-
import Foundation
import UIKit
public extension UIViewController {
#objc dynamic func _tracked_viewWillAppear(_ animated: Bool) {
UserActivityTracker.startTracking(viewController: self)
}
static func swizzle() {
//Make sure This isn't a subclass of UIViewController,
//So that It applies to all UIViewController childs
if self != UIViewController.self {
return
}
let _: () = {
let originalSelector =
#selector(UIViewController.viewWillAppear(_:))
let swizzledSelector =
#selector(UIViewController._tracked_viewWillAppear(_:))
let originalMethod =
class_getInstanceMethod(self, originalSelector)
let swizzledMethod =
class_getInstanceMethod(self, swizzledSelector)
method_exchangeImplementations(originalMethod!, swizzledMethod!);
}()
}
}
In above code _tracked_viewWillAppear() is my custom function which I want to call my implementation before actual implementation called.
Then in AppDeligate class, call UIViewController.swizzle() method, as follows:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIViewController.swizzle()
return true
}

issue while accessing global func in app delegate

I am new to swift and i created one swift file with name mySession and in that file i am storing login data like below
Store Login Data
func setLoginData (data:Data) {
let preferences = UserDefaults.standard
let Key_Login = "Login"
preferences.set(data, forKey: Key_Login)
preferences.synchronize()
}
and second func for use that stored data
Get Data
func getLoginData ()->Data {
let preferences = UserDefaults.standard
let Key_Login = "Login"
if preferences.object(forKey: Key_Login) == nil {
return data
} else {
return preferences.data(forKey: Key_Login)!
}
}
So now my question is that i want to use getLoginData func in my app delegate to check user is logged in or not so not able to under stand how to use that func in app delegate
You should encapsulate these methods inside a class and use an instance of that class inside AppDelegate
class MySession {
func setLogin(_ data: Data){...}
func getLoginData() -> Data {...}
}
At the call site, instantiate MySession and use your methods to do what's needed.
class AppDelegate {
//...
let sessionHandler = MySession()
sessionHandler.getLoginData()
}
Sidenote, make sure you're using proper Swift 4 naming conventions.
First Import the file name in AppDelegate
Import mySession
Then just call the method in your desired function.
setLoginData(data:<your Data>)
For example.If you want to use it in didFinishLaunchingWithOptions method, follow this
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DefaultNetworkManager.appsConfigSetup()
window = UIWindow(frame: UIScreen.main.bounds)
setupApplication()
setLoginData(data: <Your Data>)
return true
}

How to execute certain code when returning to a view swift

When reversing the segue of a view, I would like to have certain code performed. This is unlike viewdidload, or viewdidappear, because I don't want it to run when the app first launches, as it is my initial view controller, but only when returning from another view.
How do I go about achieving this?
Thanks.
You should use a delegate for this sort of thing, here is the first tutorial i saw on google about it but can google for some different ones. delegation is a key coding pattern that all iOS devs should know about and when to use.
So in your case, your first view controller will be a delegate of the second one, which will allow the second vc to execute methods on the first vc when it sees fit, and for this case i would assume before the second vc goes back to the first
the simplest way is to keep some boolean propery (e.g. Bool firstAppear) then initialize it with true in viewDidLoad method, and then in viewDidAppear:
if firstAppear {
firstAppear = false
return
}
You can use viewWillAppear and viewDidAppear methods. They called not just one time, but every time, your view controller appearing.
viewDidLoad called just one time after view controller loading
Let us consider two viewcontrollers VC1 and VC2. You are now in VC2 and trying to navigate back. in ViewWillDisappear of VC2 try this code.
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(true, forKey: "mykey")
And now you are navigating back to VC1. In VC1 ViewWillAppear use this code
let defaults = NSUserDefaults.standardUserDefaults()
let mykey = defaults.boolForKey("mykey")
if mykey {
defaults.setBool(false, forKey: "mykey")
//execute your code here
}
First you can set this for example in your AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
...
let alreadyLaunched = NSUserDefaults.standardUserDefaults().boolForKey("alreadyLaunched")
if alreadyLaunched {
print("This is not the first app launch.")
}
else {
print("First app launch, setting NSUserDefault.")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "alreadyLaunched")
}
}
Then check wherever you want:
let alreadyLaunched = NSUserDefaults.standardUserDefaults().boolForKey("alreadyLaunched")
if alreadyLaunched {
print("This is not the first app launch.")
}

Call app delegate method from view controller

I want to know if I can call an app delegate method from another ViewController.
When the app starts, the application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool i method is called. Can I call this method a second time from another view controller?
Not sure why you want to do this. You probably shouldn't, but for the purpose of answering the question here it is:
// get a reference to the app delegate
let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
// call didFinishLaunchWithOptions ... why?
appDelegate?.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
In Swift 3.0, you can call as:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.anyAppDelegateInstaceMethod()
This method is called just once when app launches. You can't from ViewController. Instead make user defined method in AppDelegete. and call that method from ViewController. By getting object of AppDelegate.
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
[appDel <Your method>];
Constructors:
Add a constructor in AppDelegate Class at the end of code
Swift 3.x
class func shared() -> AppDelegate
{
return UIApplication.shared.delegate as! AppDelegate
}
Swift 2.x
func appDelegate () -> AppDelegate
{
return UIApplication.sharedApplication().delegate as! AppDelegate
}
and add a var like this
var boolForFun = Bool()
How to use reference in your class?
Method
for swift 3x
access functins or variables
AppDelegate.shared().boolForFun = true
for else
appDelegate().methodFoo()
Variable
appDelegate().foo
Swift 4, Swift 5
As others have said you shouldn't do that.
It would be better if you trigger it when you are in a certain application life cycle and do something specific using the Notification Center.
Example (in ViewController):
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
However, if you do have to call the app delegate method, you can use this
let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate

Swift Code in App Delegate to Load Storyboard Causes Crash

I have an universal app I am writing and I initially wrote it using two storyboard files. I had code in the App Delegate didFinishLaunchingWithOptions routine to decide which storyboard to load and away it would go. I have since realised that that was a silly thing to do because I had duplicate code, so I have deleted one storyboard and made the other universal. I have fixed up the VCs to point at the right class and everything. However, my app now refuses to launch. When I run it in the sim, it gives me the error
The app delegate must implement the window property if it wants to use a main storyboard file.
.
Here is my code in App Delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
var deviceIdiom = UIDevice.currentDevice().userInterfaceIdiom
if deviceIdiom == UIUserInterfaceIdiom.Phone {
strDevice = "iPhone"
} else if deviceIdiom == UIUserInterfaceIdiom.Pad {
strDevice = "iPad"
} else {
strDevice = "Unknown"
}
return true
}
What am I doing wrong?
(Questions I've already looked at but didn't help):
Unique Issue displaying first storyboard scene in Xcode
The app delegate must implement the window property if it wants to use a main storyboard file
Managing two storyboards in App Delegate
How to manually set which storyboard view to show in app delegate
Swift - Load storyboard programatically
Your app delegate needs to start like such:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
//...
}

Resources