How to test a swift generic type another one? - swift

I have some generic type class but no instance of object to test. What I would like to do is to alter the behavior of the function according to the runtime type.
class MyGenericUtility<SomeGenericClass> {
func myFunction() {
// so far I have tested "is", "==" and "==="
if SomeGenericClass is SomeRealClass {
println("some special stuff there")
}
println("some generic stuff as the name tells")
}
}

You can compare the class type, using SomeGenericClass.self == SomeRealClass.self as,
class MyGenericUtility<SomeGenericClass> {
func myFunction() {
if SomeGenericClass.self == SomeRealClass.self {
print("SomeRealClass stuffs")
} else if SomeGenericClass.self == String.self {
print("String stuffs")
}
}
}
let someRealUtility = MyGenericUtility<SomeRealClass>()
someRealUtility.myFunction()
let stringUtility = MyGenericUtility<String>()
stringUtility.myFunction()

Rather than testing at runtime, you should generally handle this at compile time with constrained extensions (this assumes Swift 2). Doing it this way avoids any need to do unsafe as! casting when you need to access type-specific parts of the instance.
class MyGenericUtility<SomeGenericClass> {
}
// Special handling for `SomeRealClass`
extension MyGenericUtility where SomeGenericClass: SomeRealClass {
func myFunction() {
print("SomeRealClass stuffs")
}
}
// Default handling for any unspecified class
extension MyGenericUtility {
func myFunction() {
print("Other stuffs")
}
}
let someRealUtility = MyGenericUtility<SomeRealClass>()
someRealUtility.myFunction()
let stringUtility = MyGenericUtility<String>()
stringUtility.myFunction()
Note that this is based on inheritance, not equality, so any subclass of SomeRealClass would get the SomeRealClass behavior.

You can't use the generic type directly, you need to use a property of that type when comparing with "is".
class MyGenericUtility<T> {
var a: T
func myFunction() {
if a is Int {
println("some special stuff there")
}
println("some generic stuff as the name tells")
}
init(value: T) {
a = value
}
}
let test = MyGenericUtility(value: 5)
test.myFunction()
// Output: some special stuff there
// some generic stuff as the name tells
let test2 = MyGenericUtility(value: "foo")
test2.myFunction()
// Output: some generic stuff as the name tells

Related

Type constraints on contained generic type parameter

I expected the following code to print "extension" in both cases. But the type constraint on the extension does not take effect on the contained generic type. I see the same behavior when constraining on protocols too.
class Generic1<T1> {
func doSomething() {
print("base")
}
}
extension Generic1 where T1 == String {
func doSomething() {
print("extension")
}
}
class Generic2<T2> {
private let generic1 = Generic1<T2>()
func doSomething() {
generic1.doSomething()
}
}
Generic1<String>().doSomething() // prints extension
Generic2<String>().doSomething() // prints base
The only workaround I currently have is to constrain the outer generic as well like so:
extension Generic2 where T2 == String {
func doSomething() {
generic1.doSomething()
}
}
Why does this happen? Are there better solutions?
Edit: Just for completeness, the workaround that suited my case was the following:
class Generic1<T1> {
func doSomething() {
print("base")
}
}
class StringGeneric1: Generic1<String> {
override func doSomething() {
print("extension")
}
}
class Generic2<T2> {
private let generic1: Generic1<T2>
init (_ generic1: Generic1<T2>) {
self.generic1 = generic1
}
func doSomething() {
generic1.doSomething()
}
}
Generic1<String>().doSomething() // prints "base"
Generic2<String>(StringGeneric1()).doSomething() // prints "extension"
The problem is that methods defined in extensions are statically dispatched. So when you have:
class Generic2<T2> {
private let generic1 = Generic1<T2>()
func doSomething() {
generic1.doSomething()
}
}
The compiler cannot know here whether T2 is going to be a String or not, so it generates a call to the method in the base class. When you explicitly specify that T2 is String, then with that information, the compiler can generate a call to the extension's method here. Otherwise, though, the type of T2 isn't known until runtime, so you can't reach the extension method via static dispatch.
This may be solved when conditional conformances are added with Swift 4.2

swift - Pass generic type to method with more specific extension requirements

So the title is a little weirdly worded, but here is the basis of what I am looking to do. I want to make a function that can determine if the generic type given extends from a specific protocol and then pass through the type to the more specific method for processing. This would be using the swift programming language to do so.
Psuedo code of what I want to achieve below:
func doStuff<T>(callback: Callback<T>) {
// Pseudo code of what I want to achieve as I'm not sure the syntax
// nor if it's even possible
if T extends Protocol {
let tExtendsProtocolType = T.Type as Protocol
mapStuffSpecific<tExtendsProtocolType>(callback: callback)
} else {
// Standard Use Case
}
}
func doStuffSpecific<T: Protocol>(callback: Callback<T> {
}
Thanks in advance
EDIT 1
typealias Callback<T> = (T) -> Void
protocol Protocol {}
struct A {}
struct B: Protocol {}
// I want to be able to use this to do some common set up then call into either doStuff<T> or doStuff<T: Protocol>
func tryDoStuff<T>(callback: Callback<T>) {
// Do some common setup then call this
doStuff(callback: callback)
}
func doStuff<T>(callback: Callback<T>) {
print("doStuff")
}
func doStuff<T: Protocol>(callback: Callback<T>) {
print("doStuffSpecific")
}
let callbackA: Callback<A> = { _ in } // Just an empty closure
let callbackB: Callback<B> = { _ in }
tryDoStuff(callback: callbackA) // prints doStuff
tryDoStuff(callback: callbackB) // prints doStuffSpecific
Swift's overload resolution algorithm already prioritizes the most specific overload available. Here's an example:
typealias Callback<T> = (T) -> Void
protocol Protocol {}
struct A {}
struct B: Protocol {}
func doStuff<T>(callback: Callback<T>) {
print("doStuff")
}
func doStuff<T: Protocol>(callback: Callback<T>) {
print("doStuffSpecific")
}
let callbackA: Callback<A> = { _ in } // Just an empty closure
let callbackB: Callback<B> = { _ in }
doStuff(callback: callbackA) // prints doStuff
doStuff(callback: callbackB) // prints doStuffSpecific

Is there some workaround to cast to a generic base class without knowing what the defined element type is?

I am trying to achieve a design where I can have a base class that has a generic property that I can change values on by conforming to a protocol.
protocol EnumProtocol {
static var startValue: Self { get }
func nextValue() -> Self
}
enum FooState: EnumProtocol {
case foo1, foo2
static var startValue: FooState { return .foo1 }
func nextValue() -> FooState {
switch self {
case .foo1:
return .foo2
case .foo2:
return .foo1
}
}
}
enum BarState: EnumProtocol {
case bar
static var startValue: BarState { return .bar }
func nextValue() -> BarState {
return .bar
}
}
class BaseClass<T: EnumProtocol> {
var state = T.startValue
}
class FooClass: BaseClass<FooState> {
}
class BarClass: BaseClass<BarState> {
}
Is it possible to end up with a solution similar to this where the element type is unknown and the value relies on the nextValue() method.
let foo = FooClass()
let bar = BarClass()
if let test = bar as? BaseClass {
test.state = test.state.nextValue()
}
This works but BarState will be unknown in my case and a lot of classes will be subclasses of BaseClass and have different state types.
let bar = BarClass()
if let test = bar as? BaseClass<BarState> {
test.state = test.state.nextValue()
}
This is a simplified example. In my case I will get a SKNode subclass that has a state property that is an enum with a nextvalue method that have defined rules to decide what the next value will be. I am trying to have a generic implementation of this that only relies on what is returned from the nextValue method. Is there a better pattern to achieve this?
This will not work for this exact scenario because EnumProtocol can not be used as concrete type since it has a Self type requirement, however, in order to achieve this type of behavior in other cases you can create a protocol that the base class conforms to and try to cast objects to that type when you are trying to determine if an object is some subclass of that type.
Consider the following example
class Bitcoin { }
class Ethereum { }
class Wallet<T> {
var usdValue: Double = 0
}
class BitcoinWallet: Wallet<Bitcoin> {
}
class EthereumWallet: Wallet<Ethereum> {
}
let bitcoinWallet = BitcoinWallet() as Any
if let wallet = bitcoinWallet as? Wallet {
print(wallet.usdValue)
}
This will not work, due to the same error that you are referring to:
error: generic parameter 'T' could not be inferred in cast to 'Wallet<_>'
However, if you add the following protocol
protocol WalletType {
var usdValue: Double { get set }
}
and make Wallet conform to that
class Wallet<T>: WalletType
then you can cast values to that protocol and use it as expected:
if let wallet = bitcoinWallet as? WalletType {
print(wallet.usdValue)
}

Swift generic function calling function with return type overload

just a quick question. I have the following code, which works just fine:
class obA: Printable {
var description: String { get { return "obA" } }
}
class obB: Printable {
var description: String { get { return "obB" } }
}
func giveObject() -> obA { return obA() }
func giveObject() -> obB { return obB() }
var a: obA = giveObject()
var b: obB = giveObject()
println(a)
println(b)
The right variant of giveObject is being called and all is well. Of course this is just a simplified case, in reality in my project there are several dozens of overloads of 'giveObject', all differing in return type. Now, I want to make a generic function to parse all these things. So, next step:
func giveGeneric<T>() -> T {
return giveObject()
}
var c: obA = giveGeneric()
println(c)
And this complains about ambiguous use of giveObject. I can understand where the error comes from, but I don't see right away how I can solve it and use a construct like this...
First of all just a note.
If the generic type of giveGeneric is simply T, then it can be anything (a String, an Int, ...). So how should giveObject() react in this case?
I mean, if you write:
let word : String = giveGeneric()
internally your generic function calls something like:
let result : String = giveObject() // Ambiguous use of giveObject
My solution
I declared a protocol as follow:
protocol MyObject {
init()
}
Then I made your 2 classes conform to the protocol
class obA: Printable, MyObject {
var description: String { get { return "obA" } }
required init() {}
}
class obB: Printable, MyObject {
var description: String { get { return "obB" } }
required init() {}
}
Finally I can write this
func giveGeneric<T:MyObject>() -> T {
return T()
}
Now I can use it:
let a1 : obA = giveGeneric()
let b1 : obB = giveGeneric()
You decide if this is the solution you were looking for or simply a workaround.
That cannot work, even if you implement a giveObject function for any possible type. Since T can be any type, the giveGeneric method cannot determine the correct overload to invoke.
The only way I can think of is by creating a huge swift with as many cases as the number of types you want to handle:
func giveGeneric<T>() -> T? {
switch "\(T.self)" {
case "\(obA.self)":
return giveObject() as obA as? T
case "\(obB.self)":
return giveObject() as obB as? T
default:
return .None
}
}
But I don't think I would use such a solution even with a gun pointed at my head - it's really ugly.
If in all your cases you create instances using a parameterless constructor, then you might create a protocol and constraint the T generic type to implement it:
protocol Instantiable {
init()
}
func giveGeneric<T: Instantiable>() -> T {
return T()
}
You can use with built-in as well as new types - for instance:
extension String : Instantiable {
// `String` already implements `init()`, so nothing to add here
}
let s: String = giveGeneric()
Alternatively, if you prefer you can make the protocol declare a static giveObject method rather than a parameterless constructor:
protocol Instantiable {
static func giveObject() -> Self
}
func giveGeneric<T: Instantiable>() -> T {
return T.giveObject()
}
extension String : Instantiable {
static func giveObject() -> String {
return String()
}
}
let s: String = giveGeneric()

How do I make a referential type comparison in Swift using 'is'?

I can't figure out how to make a type comparison in Swift using the is operator, if the right side is a reference and not a hard-coded type.
For example,
class GmBuilding { }
class GmOffice: GmBuilding { }
class GmFactory: GmBuilding { }
class GmStreet {
var buildings: [GmBuilding] = []
func findAllBuildingsOfType(buildingType: GmBuilding.Type) -> [GmBuilding] {
var result: [GmBuilding] = []
for building in self.buildings {
if building is buildingType { // complains that buildingType is not a type
result.append(building)
}
}
return result
}
}
let myStreet = GmStreet()
var buildingList: [GmBuilding] = myStreet.findAllBuildingsOfType(GmOffice.self)
It complains that 'buildingType is not a type'. How can it be made to work?
A generic method may do what you want:
func findAllBuildingsOfType<T: GmBuilding>(buildingType: T.Type) -> [GmBuilding] {
// you can use `filter` instead of var/for/append
return buildings.filter { $0 is T }
}
This will work so long as you really do only want to determine the type at compile time:
let myStreet = GmStreet()
let buildingList = myStreet.findAllBuildingsOfType(GmOffice.self)
// T is set at compile time to GmOffice --------^
However, often when this question comes up, the follow-up question is, how do I store GmOffice.self in a variable and then have the type be determined at runtime? And that will not work with this technique. But if statically fixed types at compile time are enough for you, this should do it.
If AirSpeed Velocity's answer doesn't work for you, you can also accomplish this by bridging to Objective-C.
Make GmBuilding inherit from NSObject:
class GmBuilding: NSObject { }
And use isKindOfClass(_:) to check the type:
for building in self.buildings {
if building.isKindOfClass(buildingType) {
result.append(building)
}
}
Not as Swifty, but it works.
I'm sure there must be a better way than this, but it doesn't require inheritance from NSObject and it works at runtime - according to my playground
class GmBuilding { }
class GmOffice: GmBuilding { }
class GmFactory: GmBuilding { }
func thingIs(thing: GmBuilding, #sameTypeAs: GmBuilding) -> Bool
{
return thing.dynamicType === sameTypeAs.dynamicType
}
var foo: GmOffice = GmOffice()
thingIs(foo, sameTypeAs: GmOffice()) // true
thingIs(foo, sameTypeAs: GmFactory()) // false
The main reason I instantiate an object (you can use a singleton instead) is because I can't figure out how to declare a parameter to be a metatype.
It also doesn't work for
thingIs(foo, sameTypeAs: GmBuilding()) // false :=(
As a final resort, using Obj-C reflect function:
import ObjectiveC
func isinstance(instance: AnyObject, cls: AnyClass) -> Bool {
var c: AnyClass? = instance.dynamicType
do {
if c === cls {
return true
}
c = class_getSuperclass(c)
} while c != nil
return false
}
class GmBuilding { }
class GmOffice: GmBuilding { }
class GmFactory: GmBuilding { }
isinstance(GmOffice(), GmOffice.self) // -> true
isinstance(GmOffice(), GmFactory.self) // -> false
isinstance(GmOffice(), GmBuilding.self) // -> true