Swift: Generic Type conform Protocol - ios

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>

Related

Pass a class that follows a given protocol into a method and then instantiate the class using swift

I'm looking to create a very generic service layer that looks to call Alamofire. See code:
func getRequest(from endpoint:String!, withParameters parameters:[String:Any]?,withModel model:RuutsBaseResponse, andCompleteWith handler:#escaping (RuutsBaseResponse?, NSError?) -> ()){
func generateModel(withResponse result:NSDictionary?, withError error:NSError?) -> (){
handler(model.init(fromDictionary: result),error);
}
alamoFireService.AlamoFireServiceRequest(endpoint:endpoint, httpVerb:.get, parameters:parameters!, completionHandler:generateModel);
}
This is what the RuutsBaseResponse looks like:
protocol RuutsBaseResponse {
init(fromDictionary dictionary: NSDictionary);
}
The getRequest looks to do the following:
Taken in any class so long as it conforms to RuutsBaseResponse protocol.
Make a service call using alamoFire using the parameters passed into it.
alamoFire will call the generateModel method once the service call is completed.
When it calls the generateModel the method is supposed to instantiate the model and pass into it the Dictionary received from alamoFire.
The issue is the model, i'm struggling to achieve the requirements above. I keep getting:
Error:(22, 21) 'init' is a member of the type; use 'type(of: ...)' to
initialize a new object of the same dynamic type
All I'm looking to do is make a layer generic enough to make a service call and create an object/model that is created from the Dictionary passed back from alamoFire.
What you are looking for is how to use Generics:
protocol RuutsBaseResponse {
init(fromDictionary dictionary: NSDictionary);
}
struct BaseModel: RuutsBaseResponse {
internal init(fromDictionary dictionary: NSDictionary) {
print("instantiated BaseModel")
}
}
struct SecondaryModel: RuutsBaseResponse {
internal init(fromDictionary dictionary: NSDictionary) {
print("instantiated SecondaryModel")
}
}
// state that this function handles generics that conform to the RuutsBaseResponse
// protocol
func getRequest<T: RuutsBaseResponse>(handler: (_ response: T) -> ()) {
handler(T(fromDictionary: NSDictionary()))
}
getRequest(handler: { model in
// will print 'instantiated BaseModel'
(model as! BaseModel)
})
getRequest(handler: { model in
// will print 'instantiated SecondaryModel'
(model as! SecondaryModel)
})

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

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

Return an array of type Self

I'm having trouble figuring out a way to return an array of instances of a specific dynamic class type, at runtime, in Swift.
I successfully compiled and tested this version which returns a single instance of a class:
class Generic {
class func all() -> Self {
return self.init()
}
required init() {
}
}
class A: Generic {
}
let a = A.all() // is of type A
The challenge here is to get compilation to allow the all function to be prototyped as follows: class func all() -> [Self] (i.e return an array of instances, working with subclasses, without any cast).
class Generic {
class func all() -> [Self] {
return [self.init()]
}
required init() {
}
}
class A: Generic {
}
let a = A.all() // won't compile
I could return an array of Generic instances with class func all() -> [Generic] but this requires an additional cast with as! to get the correct type A. I'd like to take advantage of begin in the context of class A and using the Self keyword, to let the compiler infer the 'real' type. Do you guys think it's possible?
It seems to be only possible to return single instances, not arrays.
EDIT: Got this to work using AnyObject. Better, but not optimal as it requires a cast to the correct type.
class Generic {
class func all() -> [AnyObject] {
return [self.init()]
}
required init() {
}
}
class A: Generic {
}
let a = A.all() as! [A]
Thanks!
PS: Any other way to do this using generics or protocols/protocol extensions is also an option. If you have a more "Swifty" version in mind, please be my guest. Can't help myself thinking there's maybe a better way to do this, but can't figure out how.
The only option I can see of doing something like that is using protocols instead of a base class, like this:
protocol Generic {
func all() -> [Self]
init()
}
extension Generic {
func all() -> [Self] {
return [self.dynamicType.init()]
}
}
final class A : Generic {
}
A().all()
You have two limitations doing it like this. First, all classes that conform to your protocol have to be final. Second, all classes must obviously implement the init defined in the protocol, otherwise we wouldn't be able to have the all method defined.
Edit: you don't actually need to define the init as long as you don't define any other initializers
Edit 2: I didn't notice you used class functions, you can modify my example to use class functions instead of instance methods by replacing func all() -> [Self] with static func all() -> [Self] and
func all() -> [Self] {
return [self.dynamicType.init()]
}
with
static func all() -> [Self] {
return [self.init()]
}
Unfortunately, there doesn't seem to be a way to do this using Self. Self cannot be used in expressions, so [Self] or Array<Self> are not allowed.
However, I think that your use case is completely valid and you should repot it as a bug.

Implement delegate using generic protocol in Swift

I'm trying to create a delegate protocol that implements a function which passes an array of a generic type. I've tried several combinations but none of them seem to do the trick.
This is the most approximate thing i've reached to. This is the protocol:
protocol APIControllerProtocol {
typealias T
func didReceiveAPIResults(results: [T])
}
And this is the the delegator object:
class APIController<U:APIControllerProtocol> {
typealias ElementType = U
var delegate: ElementType?
init(delegate: ElementType){
self.delegate = delegate
}
func getAPIResults(){
// Perform some action before delegation
// "results" is an Array of dictionaries got from NSJSONSerialization
self.delegate?.didReceiveAPIResults(results.map{dict in Album(json:dict)})
}
}
However, the last line get this error: "Album is not convertible to U.T"
"Album" is the model object used to return the results.
What am i doing wrong?
EDIT:
Following Mike S advice, i've made the protocol method didReceiveAPIResults a generic function, and specified what T is in the delegate. However, when receiving and assigning the argument of type T to a property in the delegate, i get the error: "T is not identical to T"
class TestDelegate: APIControllerProtocol {
typealias T = Album
var albums:[T] = [T]()
func didReceiveAPIResults<T>(results: [T]) {
// ...
self.albums = results //ERROR: "T is not identical to T"
}
}
Your didReceiveAPIResults declaration in APIControllerProtocol needs to be a generic function so that the generic type T is passed along to it correctly.
protocol APIControllerProtocol {
typealias T
func didReceiveAPIResults<T>(results: [T])
}
Note: This means your delegate definition will need to define what T is:
class TestDelegate: APIControllerProtocol {
typealias T = Album
func didReceiveAPIResults<T>(results: [T]) {
// ...
}
}
Update: While the code above does get rid of the original error, it turns out that it acts more like a workaround and doesn't really address the root of the problem.
The real issue seems to be that the compiler is having trouble reconciling what U.T is with no ambiguity. That's actually easy enough to fix though, we just need to give it a more precise definition (note the where clause in the APIController definition):
protocol APIControllerProtocol {
typealias T
func didReceiveAPIResults(results: [T])
}
class APIController<U:APIControllerProtocol where U.T == Album> {
typealias ElementType = U
// ...
}
Note: I removed the <T> that I added to the function in the protocol previously; that's not needed anymore and will end up causing problems later.
With that, the TestDelegate class works as expected (you don't even need the typealias anymore):
class TestDelegate: APIControllerProtocol {
var albums: [Album]? = nil
func didReceiveAPIResults(results: [Album]) {
albums = results
}
}

AnyObject doesn't recognize proper functions when called

I have a protocol StateMachineDelegate, a class DataSource that conforms to it, and a class StateMachine that has a delegate with such protocol
Both classes implement a function found on the protocol so that if the class has a delegate, let them handle the functions; otherwise the class handles it itself.
StateMachine contains a function like this:
func target() -> AnyObject {
return delegate ?? self
}
My full code goes like this:
import Foundation
#objc protocol StateMachineDelegate {
optional func stateWillChange()
optional func stateDidChange()
optional func missingTransitionFromState(fromState: String?, toState: String?) -> String
}
class StateMachine {
var delegate: StateMachineDelegate?
func target() -> AnyObject {
return delegate ?? self
}
func missingTransitionFromState(fromState: String, toState: String) -> String {
return "Hello"
}
}
class DataSource: StateMachineDelegate {
func missingTransitionFromState(fromState: String?, toState: String?) -> String {
return "Hi"
}
}
When I was running some tests in playground and the StateMachine instance did not possess a delegate the target function returned the same instance as AnyObject. But once I called missingTransitionFromState from target it crashed so I change it to missingTransitionFromState?() with returned nil
Last function line should have returned "Hello"
Once delegate was given the target returned the delegateObject and proceeded to run the function as normal
The playground test are these:
All of your calls to missingTransitionFromState have a ? at the end except for the last one that won't execute. Replacing the ! with a ? fixes the problem. I didn't really understand what the code is doing but the question mark fixes it.
Making the following changes fixes the problem:
Annotating StateMachine's missingTransitionFromState method with dynamic
Changing the parameter types in StateMachine's missingTransitionFromState method from String to String?, to match the signature of the other missingTransitionFromState method.
I believe that the method needs dynamic or #objc in order to be able to be called dynamically using AnyObject. However, after adding that, the compiler will complain that calls to missingTransitionFromState on an AnyObject is ambiguous, because there are two signatures, so you have to fix the signature.

Resources