Keep property in memory forever - ios

I want to add Reachability library to my app, and there is what we can read in doc:
//declare this property where it won't go out of scope relative to your listener
let reachability = try! Reachability()
So, what I did is:
Create Singleton object to do stuff:
class ReachabilityService {
static let shared = ReachabilityService()
private init() {}
private let reachability = try! Reachability()
}
Did add property to AppDelegate to keep it in memory for application life:
class AppDelegate: UIResponder, UIApplicationDelegate {
let reachability = ReachabilityService.shared
}
My question is, will reachability property in AppDelegate be in memory for all of application lifetime?

Yes, the property declared in AppDelegate will be available throughout the lifecycle of the application.

Related

Swift: avoid to force unwrap constant variable

I'm trying to avoid a force unwrap of global variables. How can I do that in this particular example:
let AppDelegate = UIApplication.shared.delegate as! AppDelegate
If you don't want to force-unwrap (which is fine here in this case), then use if let.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
// Do something with appDelegate
}
But the force-unwrap is fine for this. You want the app to crash during development if you make a typo on the type of the app delegate. It will never fail at runtime unless you make a change to the code and ship it to Apple without at least running your app once.
Made this an extension so no copy paste anymore. This is as type safe as it gets since it only makes sense for the AppDelegate to implement UIApplicationDelegate and the one is always reachable via UIApplication.shared.delegate.
#if os(macOS)
import Cocoa
typealias ApplicationDelegate = NSApplicationDelegate
typealias Application = NSApplication
#else
import UIKit
typealias ApplicationDelegate = UIApplicationDelegate
typealias Application = UIApplication
#endif
public extension ApplicationDelegate {
static var shared: Self {
return Application.shared.delegate as! Self
}
}
If you want to use the variable locally then:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
//logic here
}
or
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
I don't think there is a way to use AppDelegate globally without using force casting.
Edited
To create a global variable, just declare it anywhere outside a class. For example:
var globalVariable = 1
class Person {
//logic here
}

UIApplication.delegate must be used from main thread only [duplicate]

This question already has answers here:
-[UIApplication delegate] must be called from main thread only
(2 answers)
Closed 5 years ago.
I have the following code in my app delegate as a shortcut for working with CoreData in my other viewControllers:
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
However, I now get the error message:
"UI API called from background thread" and "UIApplication.delegate must be used from main thread only".
I am working with CoreData while my app is in the background, but this is the first time I've seen this error message. Does anybody know what's going on here?
Update: I tried to move this inside the appDelegate class itself, and using the following code -
let dispatch = DispatchQueue.main.async {
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
}
Now, I can no longer access the ad and context variables outside the AppDelegate. Is there something I'm missing?
With ref to this (-[UIApplication delegate] must be called from main thread only) in Swift (for your query resolution)
DispatchQueue.main.async(execute: {
// Handle further UI related operations here....
//let ad = UIApplication.shared.delegate as! AppDelegate
//let context = ad.persistentContainer.viewContext
})
With edit: (Where is the correct place to declare ad and context? Should I declare these in my viewControllers in the main dispatch)
Place of variables (ad and context) declaration defines scope for it. You need to decide what would be scope of these variable. You can declare them Project or application level (Globally), class level or particular this function level.
If you want to use these variable in other ViewControllers then declare it globally or class level with public/open/internal access control.
var ad: AppDelegate! //or var ad: AppDelegate?
var context: NSManagedObjectContext! //or var context: NSManagedObjectContext?
DispatchQueue.main.async(execute: {
// Handle further UI related operations here....
ad = UIApplication.shared.delegate as! AppDelegate
context = ad.persistentContainer.viewContext
//or
//self.ad = UIApplication.shared.delegate as! AppDelegate
//self.context = ad.persistentContainer.viewContext
})

Swift: getting AppDelegate instance as a class func results in Thread 1: EXC_BAD_INSTRUCTION

I want to get my AppDelegate reference from a class func in my AppDelegate. Why is this throwing a Thread 1: EXC_BAD_INSTRUCTION?
class func getDelegate() -> AppDelegate {
return UIApplication.sharedApplication().delegate as! AppDelegate
}
I have also tried to move this to another utility class and as a regular func, but getting the same crash.
Is there a way I can access the AppDelegate as a class func instead of having to write
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
in every class?
Declare class function to get appDelegate in AppDelegate class as
class func getDelegate() -> AppDelegate {
return UIApplication.sharedApplication().delegate as! AppDelegate
}
To access and use appDelegate in other class, call
let delegate = AppDelegate.getDelegate()
delegate.printHello()
Create a class as shown below[in a new class file or in existing class file outside the previous class. No need to put it inside AppDelete.swift file]
class Delegate
{
static var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
}
and you can use it as Delegate.appDelegate in any class.

How to make data visible for all view controllers?

Let's consider the following case:
I have a tab bar application where tapping each tab bar item takes user to other view that is handled by different view controller(typical pattern).
In one of my controllers I have method that downloads the important data and I want them to be global for whole application. What design pattern should I use?
One way to do that is to store this data using persistence such as core data, but is it the only way to make data visible for all view controllers? Maybe app delegate is able to perform such actions?
How in general you solve such situation where you have some data or variable which should be visible for all view controllers in your project?
Note that I'm not asking about persisting data across launches of the app, I just wonder how to make some data global in terms of the whole project.
Dont (emphasize DON'T) use following:
Singletons
AppDelegate (just another Singleton)
NSUserDefaults
Rather Don't:
Core Data
Do:
pass in either during instantiation or via properties
Why?
The DON'Ts messes up your memory
the Rather Don't messes with several principals of SOLID.
How would you do it correctly:
Create a base view controller that has a property that takes your data, make all your view controller inherit from it.
subclass UITabBarController
if a new view controller is selected, set the data to the view controller
the implementation is a bit tricky, this is from a real world app
class ContentTabBarController : UITabBarController {
private var kvoSelectedViewControllerContext: UInt8 = 1
required init(coder aDecoder: NSCoder) {
self.addObserver(self, forKeyPath: "selectedViewController", options: .New | .Old | .Initial , context: &kvoSelectedViewControllerContext)
}
deinit{
self.removeObserver(self, forKeyPath: "selectedViewController")
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if context == &kvoSelectedViewControllerContext {
var targetVC : UIViewController?
if let viewController = change["new"] as? UIViewController{
if let oldViewController = change["old"] as? UIViewController{
if viewController != oldViewController {
targetVC = viewController
}
}
} else {
targetVC = self.viewControllers![0] as? UIViewController
}
self.configureTargetViewController(targetVC)
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.translucent = false
}
func configureTargetViewController(viewController: UIViewController?){
//setup data
}
}
How does the tab bar controller get the data.
Well, that is up to you. It could fetch core data, it could actually pass a fetching manager as data. It could read from disc or network. But for a sane design it should not do it itself but use another class for it. and an instance of this fetcher class should be set from outside the tab bar controller, i.e. from the App Delegate.
One easy way would be to make a struct and make it hold variables. Then, you can edit it anytime you would want to. For example:
struct Variables {
static var number = 4
}
Then you can edit the data inside Variables in any view controller you want by doing this code.
Variables.number = 6 //or any other number you want
A cleaner and efficient, although not necessarily different, way to do this is to create a singleton class, e.g. AppData, which you can access in a variety of ways, and which would be available to all your other classes. It has the benefit of separating your app-specific stuff from the app delegate stuff. You might define the class this way:
#interface AppData : NSObject
// Perhaps you'll declare some class methods here & objects...
#end
you can define ivars for the AppData class, and then manage a singleton instance of AppData. Use a class method, e.g. +sharedInstance, to get a handle to the singleton on which you could then call mehods. For example,
[[AppData sharedInstance] someMethod:myArgument];
Your implementation of +sharedInstance can be where you manage the actual creation of the singleton, which the method ultimately returns.
Try this simple method,
1) Create a variable in appdelegate.swift that could be visible to all viewcontroller.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
...
...
var Check:String!="" // to pass string
...
...
}
2) Create appdelegate instance in any viewcontroller
viewcontroller1.swift
import UIKit
class ViewController: UIViewController {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
...
...
override func viewDidLoad() {
super.viewDidLoad()
...
var tmp = "\(appDelegate.Check)"
appDelegate.Check="Modified"
}
}
Viewcontroller2.swift
import UIKit
class ViewController: UIViewController {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
...
...
override func viewDidLoad() {
super.viewDidLoad()
...
var getdata = "\(appDelegate.Check)"
println("Check data : \(getdata)") // Output : Check data : Modified
}
}

Swift Passing data from appDelegate to another class

I need to pass a variable from the AppDelegate to another class that I have created to hold global variables of the project and I'm not able to find a way to make it work.
This is the code in the AppDelegate:
func application(application: UIApplication!, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData!) {
println("Device's token is: \(deviceToken)")
//Global Variables Class Instance
let globals:Globals = Globals()
globals.setDeviceToken("test1") //method1 not working
globals.deviceToken = "test2" //method2 not working
}
This is my Globals Class:
public class Globals {
var deviceToken = String()
init() {
//nothing
}
func setDeviceToken(s:String){
deviceToken = s
}
func getDeviceToken() -> String {
return deviceToken
}
}
If i try to print the value, from other files of the project, I'm not able to get anything, just an empty string.
class ViewController: UIViewController {
//Global Variables Class Instance
let globals:Globals = Globals()
override func viewDidLoad() {
println(globals.getDeviceToken()) //return empty string
println(globals.deviceToken) //return empty string
There are several patterns you can use to achieve what you want
You could access the AppDelegate through the UIApplication:
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
let deviceToken = delegate.deviceToken
Look into singletons. A quick google search for 'Swift singleton' will get you a long way. The first result:
class SingletonB {
class var sharedInstance : SingletonB {
struct Static {
static let instance : SingletonB = SingletonB()
}
return Static.instance
}
}
Then use sharedInstance to instantiate the singleton anywhere and access the same variables.
The first one is quick and dirty, so for more serious projects I would recommend the singleton pattern.
There are probably a million ways to do this, but this should get you started
(More at this link, which explores a few ways to implement singletons: https://github.com/hpique/SwiftSingleton )
I simply solved my problem using NSUserDefaults
in the AppDelegate:
NSUserDefaults.standardUserDefaults().setObject(deviceToken, forKey: "deviceToken")
NSUserDefaults.standardUserDefaults().synchronize()
From other classes:
NSUserDefaults.standardUserDefaults().objectForKey("deviceToken")
Honestly I don't know if this is a good way to do it but it's working

Resources