Swift - Proper usage of AnyObject - ios

I am new to swift. I am using storyboard and according to logged in information, I am changing the UIViewController to load. Below is the snippet.
class func viewControllerWithName(name: String) -> UIViewController?
{
let storyboard = mainStoryboard()
let viewController: AnyObject! = storyboard.instantiateViewControllerWithIdentifier(name)
return viewController as? UIViewController
}
func setUpController {
let viewController: AnyObject!
if self.user() == "admin" {
viewController:AdminViewController = self.viewControllerWithName("admin") !as! AdminViewController
} else {
viewController:UserViewController = self.viewControllerWithName("user") !as! UserViewController
}
self.addChildViewController(viewController)
viewController.view.frame = container.frame
self.view.addSubview(viewController.view)
viewController.didMoveToParentViewController(self)
}
I tried many ways, but, I am unable to typecast viewController. I get error, saying
Cannot invoke 'addChildViewController' with an argument list of type '(AnyObject!)'
What is the right format to achieve this?

Why do you declare it as AnyObject when you know its going to be a view controller? declare it as UIViewController and the error you've mentioned should go away.
You don't need to do viewController:AdminViewController in the if-else section. You are anyways force typecasting the output of viewControllerWithName method.

In this case you are indicating that viewController is AnyObject type:
let viewController: AnyObject!
To pass a UIVIewController to addChildViewController you can try something like:
let viewController = self.viewControllerWithName("admin") as! AdminViewController
self.addChildViewController(viewController)
In swift you cant change a let or var type at runtime

Update your code and change AnyObject to UIViewController:
class func viewControllerWithName(name: String) -> UIViewController?
{
let storyboard = mainStoryboard()
let viewController = storyboard.instantiateViewControllerWithIdentifier(name) as? UIViewController
return viewController
}
func setUpController {
let viewController: UIViewController!
if self.user() == "admin" {
viewController = self.viewControllerWithName("admin")!
} else {
viewController = self.viewControllerWithName("user")!
}
self.addChildViewController(viewController)
viewController.view.frame = container.frame
self.view.addSubview(viewController.view)
viewController.didMoveToParentViewController(self)
}

Related

Conditional cast from UIViewController always succeeds | Swift/Xcode

What am I doing wrong here? This still functions fine, but I would like to get rid of the yellow warning if I can help it. The warning is on the "if" statement. If I remove the "?" on "as", then the code won't even run... it requires it yet complains about it.
The warning:
Conditional cast from 'UIViewController' to 'UIViewController' always succeeds
The code:
class FadeInPushSegue: UIStoryboardSegue {
var animated: Bool = true
override func perform() {
if let sourceViewController = self.source as? UIViewController, let destinationViewController = self.destination as? UIViewController {
let transition: CATransition = CATransition()
transition.type = CATransitionType.fade; sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
}
}
}
You don't need to cast it to UIViewController because properties source and destinations is UIViewController already
open var source: UIViewController { get }
open var destination: UIViewController { get }
You seeing this warning because you cast from not Optional UIViewController to optional UIViewController.
When you remove as? your code isn't running because you trying to unwrap not optional property.
Initializer for conditional binding must have Optional type, not 'UIViewController'
You should remove if do something like that:
final class FadeInPushSegue: UIStoryboardSegue {
var animated: Bool = true
override func perform() {
let sourceViewController = self.source
let destinationViewController = self.destination
let transition: CATransition = CATransition()
transition.type = CATransitionType.fade; sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
}

Type checking with generics and raw types?

I have a class
class SomeViewController<T: SomeViewModel>: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { ... }
And another class:
class AnotherViewController: UIViewController {
private weak var someVC: UIViewController?
...
func someFunc() {
if someVC is SomeViewController { ... } // attempt 1
// or
if let vc = someVC as? SomeViewController { ... } // attempt 1
...
}
...
}
I need to see if someVC is a SomeViewController so that I can access an instance variable that has nothing to do with the generic type. However, when doing a check via either attempt 1 or attempt 2, the check always fails and my inner code never executes. How do I see if it is of a type, but not specific generic type e.g. I don't have to put the type of SomeViewModel?
EDIT: Added more code for clarity.
This doesn't work because Swift wants to know the type of the generic. If you just want to access a property without dealing with the generic, you can extract that to a protocol.
protocol SomethingThatHasFoo {
var foo: String { get }
}
class SomeViewController<T>: UIViewController, SomethingThatHasFoo {
let foo = "foo"
}
class AnotherViewController {
private weak var someVC: UIViewController?
func someFunc() {
if let vc = someVC as? SomethingThatHasFoo {
print(vc.foo)
}
}
}
Here is an example that works:
class SomeViewModel{}
class A: SomeViewModel{}
class SomeViewController<T: SomeViewModel> : UIViewController{}
class AnotherViewController {
private weak var someVC: UIViewController?
init(vc: SomeViewController<A>) {
someVC = vc
}
func someFunc() {
if someVC is SomeViewController<A> {
print("\(String(describing: someVC))")
}
if let vc = someVC as? SomeViewController<A> {
print("\(vc)")
}
}
Then you have to initialize it like this:
let someVC = SomeViewController<A>()
let anotherVC = AnotherViewController.init(vc: someVC)
anotherVC.someFunc()
Hope that answered your question

How to passes data from ViewController to UITabBarController -> UINavigationController -> ViewController

I am try to send data from ViewController to UITabBarController -> UINavigationController -> ViewController and try lots of way but fail to Passes data. After click in button i have asynchronous task and then need to load UITabBarController-ViewController with some data. I already almost try all example but fail to find working code.
func AftergettingAsynchronousData(forUserID userID: String?) {
guard let tabBarController = tabBarController else { return }
let navController = tabBarController.viewControllers?[0] as! UINavigationController
let exploreViewController = navController.topViewController as! ExploreViewController
exploreViewController.userID = userID
}
receiving data
class ExploreViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
public var userID: String?
override func viewDidLoad() {
super.viewDidLoad()
print("userID \(String(describing: userID))");
}
}
Try changing navController.topViewController to navController.rootViewController

How to get the current visible viewController from AppDelegate

So, I'm using the method bellow from UIApplication extension to get the top view controller:
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
But the problem is: It always returns UIViewController. But I need to check if it is MyViewController for example. How do I achieve that?
Do conditional casting on the return value to safely check its type.
if let currentVC = UIApplication.topViewController() as? MyViewController {
//the type of currentVC is MyViewController inside the if statement, use it as you want to
}
Your whole function implementation is flawed, if it actually worked, it would lead to infinite recursion. Once you find out the type of your current top view controller in your if statements, you are calling the same function again with the current root controller as its input value. Your function only ever exists, if it reaches either a call from a view controller, whose class is none of the ones specified in your optional bindings.
Moreover, your whole implementation doesn't do anything at the moment. You find out the type of your root view controller, but than you upcast it by returning a value of type UIViewController.
You can do a conditional check with an if-let statement like this:
if let presented = controller?.presentedViewController as? MyViewController {
// it is a MyViewController
}
You can also just directly check if the UIViewController is that type of class like this:
if controller?.presentedViewController is MyViewController {
// it is a MyViewController
}
Try this:
if let presented = controller?.presentedViewController as? MyViewController {
...
You can check it in following ways
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
else if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
else if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
// Answer
if let topVC = AppDelegate.topViewController() as? MyViewController {
// Here your topVC is MyViewController
}
// or
if let topVC = AppDelegate.topViewController() {
if topVC is MyViewController {
// Here your topVC is MyViewController
}
}
To use the UIViewController as MyViewController:
if let myViewController = UIApplication.topViewController() as? MyViewController { ... }
or if you just want to check that the UIViewController is of type MyViewController:
if UIApplication.topViewController() is MyViewController { ... }

type casting when creating an instance from a parent class

I would like to ask something about type-casting in Swift.
There are 2 classes.
RootViewController
MyViewController
and the class hierarchy is like below:
class RootViewController: UIViewController {
}
class MyViewController: RootViewController {
}
and, I want to simply call instance function to create an instance from xib file.
so I implemented below function in RootViewController.
Objective-C
+ (instancetype)instance {
return [[[self class] alloc] initWithNibName:NSStringFromClass([self class]) bundle:nil];
}
Swift
public class func instance<T:RootViewController>() -> T {
let type = self as UIViewController.Type
let name = NSStringFromClass(type).components(separatedBy: ".").last!
let instance = type.init(nibName: name, bundle: nil)
return instance as! T
}
and, usage is like below.
Objective-C
MyViewController *vc = [MyViewController instance];
Swift
let vc = MyViewController.instance() as! MyViewController
Question:
Do I have to always cast the type of instance using as! MyViewController in Swift?
Or can anybody advise me a better approach in Swift?
Any help would be appreciated!
You can Also use like this way
let vc:MyViewController = MyViewController.instance()
class RootViewController: UIViewController {
public class func instance() -> Self {
func inner<T: RootViewController>(type: T.Type) -> T {
let name = NSStringFromClass(type).components(separatedBy: ".").last!
let type1 = type as UIViewController.Type
let instance = type1.init(nibName: name, bundle: nil)
return instance as! T
}
return inner(type: self)
}
}
I would suggest creating an extension method:
extension UIViewController {
public class func instance() -> Self {
func inner<T: UIViewController>(type: T.Type) -> T {
let name = NSStringFromClass(type).components(separatedBy: ".").last!
return T(nibName: name, bundle: nil)
}
return inner(type: self)
}
}
Okay you can instantiate in these three ways:
Swift inference to Type:
let myVC = RootViewController.instance() //Swift will automatically infer the type
Explicitly telling the Type:
let myVC: RootViewController = RootViewController.instance()
Casting to your Type:
let myVC = RootViewController.instance() as! RootViewController
All these three are valid.

Resources