getting an erro when trying invoke a generic function parameter, swift - ios

My function is taking a generic type parameter which conforms to a certain of protocol
// This is my function
func instantiateViewController<T: ViewControllerIdentifier>(viewController: UIViewController) -> T
{
let controller = instantiateViewControllerWithIdentifier(viewController.identifier) as! T
return controller
}
// This is an extension of my protocol
extension ViewControllerIdentifier where Self: UIViewController
{
var identifier: String
{
return String(self)
}
}
At another class, I am instantiating a view controller by invoking above function like following:
let editAssignmentViewController = storyboard.instantiateViewController(MyTestClass)
However, xcode is giving me an error like below
Cannot convert value of type '(MyTestClass).Type' (aka 'MyTestClass') to expected argument type 'UIViewController'
Does anyone know what I am missing and how to fix this error

I'm gathering that your intent is that you want to be able to do something like:
let controller = storyboard!.instantiateViewController(SomeViewController)
If that's what you want to do, it seems that it can be accomplished without any protocol, with simply:
extension UIStoryboard {
func instantiateViewController<T: UIViewController>(viewController: T.Type) -> T {
return instantiateViewControllerWithIdentifier(String(T)) as! T
}
}
I must confess that I'd prefer to stick with the standard
let controller = storyboard!.instantiateViewControllerWithIdentifier("SomeViewController") as! SomeViewController

Related

Extension that makes wrapper that dispenses self typed as self

Sorry for the weird title. Here's a toy sketch of my code:
extension UIControl {
func makeHolder() -> ControlHolder {
ControlHolder(control: self)
}
}
struct ControlHolder {
let control : UIControl
init(control: UIControl) {
self.control = control
}
func retrieve() -> UIControl {
return self.control
}
}
I admit this is a toy reduction, and I don't like when people do that, but it illustrates the syntactical issue perfectly, so let's go with it.
Okay, so we have an extension on UIControl that is a method that returns a wrapper object. Now, the problem is this:
let holder = UISwitch().makeHolder()
let output = holder.retrieve()
The result, output, is typed as UIControl, obviously. But that isn't what I want. I want it to be typed as UISwitch, because I started with a UISwitch. OK, so that sounds like a generic. The problem is, I can't figure out how to make that generic.
It's easy, I think, to make ControlHolder a generic:
struct ControlHolder<T:UIControl> {
let control : T
init(control: T) {
self.control = control
}
func retrieve() -> T {
return self.control
}
}
I'm pretty sure I've got that part right. But then how do I write the extension declaration in such a way as to resolve that generic to the actual type of self, the UIControl on which makeHolder is called?
I tried introducing a generic to the extension, obeying the compiler until I got it to compile:
extension UIControl {
func makeHolder<T>() -> ControlHolder<T> {
ControlHolder<T>(control: self as! T)
}
}
But that's pretty silly, and output is still typed as UIControl.
Obviously I can add another parameter passing the type explicitly into makeHolder and thus resolving it:
extension UIControl {
func makeHolder<T>(ofType: T.Type) -> ControlHolder<T> {
ControlHolder<T>(control: self as! T)
}
}
Now when I call makeHolder I pass in the type:
let holder = UISwitch().makeHolder(ofType: UISwitch.self)
let output = holder.retrieve()
And now of course output is typed as UISwitch. But this is idiotic! I want the extension to just know that the type is UISwitch, because I'm calling makeHolder on a UISwitch.
I feel like I'm coming at this all wrong. Maybe someone can straighten me out? Or am I aiming for something that's just impossible?
The trick to this is to define a protocol, an extension of that protocol, and put the makeHolder method in that extension. That way, you can use Self as the generic type for the returned ControlHolder.
First define a new protocol (let's call it "HoldableControl") and require that conformers must be UIControls. It doesn't need any other requirements because we just care about adding the makeHolder function to an extension.
protocol HoldableControl: UIControl {}
Then, add an extension of HoldableControl and define makeHolder in it, returning ControlHolder<Self>. We are allowed to use Self here because it is allowed in protocol extensions, unlike in an extension to UIControl.
extension HoldableControl {
func makeHolder() -> ControlHolder<Self> {
ControlHolder(control: self)
}
}
Then, we just need to have UIControl conform to this protocol:
extension UIControl: HoldableControl {}
And make your ControlHolder generic, as you've already done:
struct ControlHolder<T: UIControl> {
let control: T
init(control: T) {
self.control = control
}
func retrieve() -> T {
control
}
}
And now it will work:
let holder = UISwitch().makeHolder() // type is ControlHolder<UISwitch>
let output = holder.retrieve() // type is UISwitch

Storing UIViewController generic with a protocol as a property

Okay, so I'm pretty sure I'm overthinking this.
I am passing through a viewController that conforms to a protocol as a generic like so:
static func sortPage<T: UIViewController>(controller: T, err: NSError) where T: SortAlertDelegate { }
What I want to be able to do is store that controlleras a property so I can access all the functions UIViewController gives me and the functions thats the SortAlerDelegate gives me.
Any ideas?
You can't specify a type and protocol conformance for a property. You'll need to cast your property to the correct type whenever you want to use specific features. However, you can make this less painful with a bit of boilerplate:
let myProperty: UIViewController? = nil {
willSet(newValue) {
if (newValue as? SortAlertDelegate != nil) {
myProperty = newValue
} else {
myProperty = nil
}
}
}
This way, if you try to set the property to an object which doesn't conform to the protocol, the set will be aborted and the property will be set to nil.
You can also write read-only properties in order to get your property as the type you need at the moment:
let myPropertyAsViewController: UIViewController? {
get { return myProperty }
}
let myPropertyAsDelegate: SortAlertDelegate? {
get {
if let myProperty = myProperty {
return myProperty as! SortAlertDelegate
} else {
return nil
}
}
}
In general it is not possible unless you move the generic constraint on top of your class definition like:
class ViewController<T: UIViewController> : UIViewController where T: SortAlertDelegate {
let delegateController: T
}
But you can also make two references in your class like
class ViewController: UIViewController {
let controller: UIViewController
let delegate: SearchAlertDelegate
}
And then store the same object as two different references.

Swift: Generic Type conform Protocol

i have some problem with generic type. I want to check if generic type conform some protocol and after pass to another generic function. For example i have function:
func requestSignal<T:Mappable>(target:Api) -> SignalProducer<[T], NSError>
And i want to do something like this:
func request<T>(target:Api, withObjectType type: T.Type) {
if let mappableType = type as? Mappable.Type {
let requestSignal: SignalProducer<[?????], NSError> = self.requestSignal(target) }
But if i tried pass T - it doesn't conform Mappable.
if pass mappableType - it is not a type
You can define T as Mappable, just like you do in the first function.
func request<T: Mappable>

Passing closures to Private API's

I'm trying to fetch all the available airplay devices from the private API MPAVRoutingController. I'm using a third party perform selector library for swift called performSelector-Swift. The method I am trying to call is fetchAvailableRoutesWithCompletionHandler. This takes one parameter, an objective-c block. - (void)fetchAvailableRoutesWithCompletionHandler:(id /* block */)arg1; When I try and pass in a closure I get a compile error and if I don't pass anything in my app crashes. I'm not releasing this app and thats why I'm using the priv API.
let MPAVRoutingController = NSClassFromString("MPAVRoutingController")! as! NSObject.Type
let routingController = MPAVRoutingController.init()
if let availableRoutes = routingController.swift_performSelector("fetchAvailableRoutesWithCompletionHandler:", withObject: {
object in
}) {
print(availableRoutes)
}
First.. How I found the correct completion block signature: http://i.imgur.com/UGVayPE.png
That shows that it allocates an NSMutableArray as the parameter to the completion block when it invokes it. That's the only parameter. You don't have to do this (disassemble it). Upon an exception being thrown, you can print the signature. Sometimes it will also tell you which kind of block is expected.
Next, my opinion on invoking selectors dynamically..
Your best option is to not perform selectors.. It's a pain especially when the call contains MULTIPLE parameters..
What you can do is invocation through interface/extension pointers.. I do this in C++ (Idea from the Pimpl idiom.. COMM interfaces do this too) all the time and it works with Swift, Objective-C, Java.. etc..
Create a protocol that has the same interface as the object. Create an extension that inherits that protocol. Then cast the object instance to that extension/interface/protocol.
Call whatever function you want via the interface/extension/protocol pointer.
import UIKit
import MediaPlayer
#objc
protocol MPAProtocol { //Functions must be optional. That way you don't implement their body when you create the extension.
optional func availableRoutes() -> NSArray
optional func discoveryMode() -> Int
optional func fetchAvailableRoutesWithCompletionHandler(completion: (routes: NSArray) -> Void)
optional func name() -> NSString
}
extension NSObject : MPAProtocol { //Needed otherwise casting will fail!
//Do NOT implement the body of the functions from the protocol.
}
Usage:
let MPAVRoutingControllerClass: NSObject.Type = NSClassFromString("MPAVRoutingController") as! NSObject.Type
let MPAVRoutingController: MPAProtocol = MPAVRoutingControllerClass.init() as MPAProtocol
MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
print(routes);
}
If you were to do it with a Bridging header instead of creating the extension + protocol, you'd just do a single Objective-C category:
#import <Foundation/Foundation.h>
#interface NSObject (MPAVRoutingControllerProtocol)
- (void)fetchAvailableRoutesWithCompletionHandler:(void(^)(NSArray *routes))completion;
#end
#implementation NSObject (MPAVRoutingControllerProtocol)
#end
Then:
let MPAVRoutingControllerClass: NSObject.Type = NSClassFromString("MPAVRoutingController") as! NSObject.Type
let MPAVRoutingController = MPAVRoutingControllerClass.init()
MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
print(routes);
}
Finally, if you can use protocol injection, you can do this much easier:
func classFromString(cls: String, interface: Protocol?) -> NSObject.Type? {
guard let interface = interface else {
return NSClassFromString(cls) as? NSObject.Type
}
if let cls = NSClassFromString(cls) {
if class_conformsToProtocol(cls, interface) {
return cls as? NSObject.Type
}
if class_addProtocol(cls, interface) {
return cls as? NSObject.Type
}
}
return nil
}
func instanceFromString<T>(cls: String, interface: Protocol?) -> T? {
return classFromString(cls, interface: interface)?.init() as? T
}
#objc
protocol MPAProtocol {
optional func availableRoutes() -> NSArray
optional func discoveryMode() -> Int
optional func fetchAvailableRoutesWithCompletionHandler(completion: (routes: NSArray) -> Void)
optional func name() -> NSString
}
let MPAVRoutingController: MPAProtocol = instanceFromString("MPAVRoutingController", interface: MPAProtocol.self)!
MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
print(routes);
}

How to define class type as param in func?

I want to define enclosure method,which can goto an view controller
like
gotopage(currentController,TargetViewController,"targetidentify")
class func gotoPage<T: UIViewController>(currentController:ViewController,targetControllerClass: T.Type,identify:String){
var mTargetViewController:targetControllerClass = currentController.storyboard?.instantiateViewControllerWithIdentifier(identify) as! targetControllerClass
currentController.showViewController(mTargetViewController, sender: currentController)
}
Here is another similar question I have referenced.
The question is as! targetControllerClass maybe not correct.
and error while building: "targetControllerClass" is not a type.
How can I define this method with Class Type?
All view controllers will inherit from UIViewController, given that you can adjust your method as follows:
func gotoPage<T>(currentController: UIViewController, targetControllerClass: T.Type, identify: String) {
var newController = currentController.storyboard?.instantiateViewControllerWithIdentifier(identify) as! UIViewController
if newController is T {
currentController.showViewController(newController, sender: currentController)
}
}
You can then call it as follows:
gotoPage(self, targetControllerClass: UIPageViewController.self, identify: "test")
However, the addition of generics isn't very beneficial here.

Resources