I have the following Swift code. My goal is to be able to pass in the type or class of either FirstItem or SecondItem into the Manager to be able to instantiate later on (by calling the createItem function).
protocol CustomItem {
static func instantiate() -> CustomItem
}
class FirstItem: CustomItem {
static func instantiate() -> CustomItem {
return FirstItem()
}
}
class SecondItem: CustomItem {
static func instantiate() -> CustomItem {
return SecondItem()
}
}
class Manager {
var itemClass: CustomItem
func createItem() {
let itemInstance = itemClass.instantiate()
}
init(itemClass: CustomItem) {
self.itemClass = itemClass
}
}
let manager = Manager(itemClass: FirstItem)
How can I fix the code above to allow for this so that the manager accepts the class itself instead of an instance of the class.
By changing the parameter type that you are accepting in the init function to CustomItem.Type and changing the value you are passing in to FirstItem.self this works. For example changing the code to something like the following should work.
protocol CustomItem {
static func instantiate() -> CustomItem
}
class FirstItem: CustomItem {
static func instantiate() -> CustomItem {
return FirstItem()
}
}
class SecondItem: CustomItem {
static func instantiate() -> CustomItem {
return SecondItem()
}
}
class Manager {
var itemClass: CustomItem.Type
func createItem() {
let itemInstance = itemClass.instantiate()
}
init(itemClass: CustomItem.Type) {
self.itemClass = itemClass
}
}
let manager = Manager(itemClass: FirstItem.self)
Related
I want to access function in protocol, but XCode complaint
Instance member 'createColumns' cannot be used on type 'T'; did you
mean to use a value of this type instead?
What I have done:
Create protocol:
protocol StorageModelDelegate {
func createColumns(for tableBuilder: TableBuilder)
}
Create class generic that receive StorageModelDelegate:
class SQLiteStorage<T: StorageModelDelegate> {
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder) // -> this is the error comes up.
}
}
}
Create class that implement SQLiteStorage:
final class InfoStorageModel {
private let sqlite: SQLiteStorage = SQLiteStorage<Info>()
}
so, how to fix the error in SQLiteStorage class?
The error indicates that you need an instance of T, not the type itself.
So you need something like:
class SQLiteStorage<T: StorageModelDelegate> {
var delegate:T
init (delegate:T) {
self.delegate = delegate
}
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
self.delegate.createColumns(for: builder) // -> this is the error comes up.
}
}
}
You want to call static method instead of instance method.
In order to fix, you should add instance parameter:
First of all, use weak var delegate in order to prevent retain cycles.
protocol StorageModelDelegate: class {
func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
delegate?.createColumns(for: builder)
}
}
}
Or use static protocol methods:
protocol StorageModelDelegate {
static func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder)
}
}
}
I want to reach this goal:
func parse<T>(element: Any?) -> [T] {
// if T is kind of MyProtocol, return get result
// else
let array = [T]()
//do some stuff
return array
}
func get<T: MyProtocol>(obj: Any?) -> [T] {
return //some other stuffs
}
Is it possible in Swift language?
EDIT:
I have a class, let's say Parser, with some properties. I want a unique function signature, but the executed code must vary in base of property type.
class Parser: ParserProtocol {
let property1 : [MyClass1] = parse(element: elem1)
let property2 : [MyClass2] = parse(element: elem2)
}
protocol ParserProtocol {
func parse<T>(element: Any?) -> [T]
}
is this something you could use?
protocol GenericsTypeProtocol {
func callParseLogic() -> Void
}
protocol MyProtocol : GenericsTypeProtocol {}
extension GenericsTypeProtocol {
func callParseLogic() -> Void {
print("Generic logic called")
}
}
extension GenericsTypeProtocol where Self : MyProtocol {
func callParseLogic() -> Void {
print("MyProtocol logic called")
}
}
class GenericClass : GenericsTypeProtocol { }
class MyClass : MyProtocol { }
class MixedClass : GenericsTypeProtocol, MyProtocol {}
let a = GenericClass()
a.callParseLogic() //prints: Generic logic called
let b = MyClass()
b.callParseLogic() //prints: MyProtocol logic called
let c = MixedClass()
c.callParseLogic() //prints: MyProtocol logic called
How can I restrict a generic type to be a type, not an instance of a type?
If I have a class:
class SomeClass<T: SomeProtocol> {}
how can I ensure that T is only an instance of AnyClass (which is just AnyObject.Type)
My protocol only has static methods and in order to call those methods I have to do instance.dynamicType.protocolMethod whereas I want to do someType.protocolMethod
AFAIK, Swift does not allow you to use a metatype as a generic type. (I believe this is along the lines of what Sam Giddins wished for in Swift 3.)
You can, however, use it as a value. Instead of making T a type parameter, make it a property:
protocol SomeProtocol {
static func foo()
}
struct Concrete: SomeProtocol {
static func foo() {
print("I am concrete")
}
}
class SomeClass {
let T: SomeProtocol.Type
init(T: SomeProtocol.Type) {
self.T = T
}
func go() {
T.foo() // no need for dynamicType
}
}
SomeClass(T: Concrete.self).go()
If, as you say, your protocol contains only static methods, then this is sufficient. However, if you need to tie a generic parameter to the type, that’s possible too:
class SomeClass<U: SomeProtocol> {
let T: U.Type
init(T: U.Type) {
self.T = T
}
func go(value: U) {
T.foo()
}
}
SomeClass(T: Concrete.self).go(Concrete())
protocol P {
static func foo()
}
class A : P {
static func foo() {
print("A class")
}
}
class B : P {
static func foo() {
print("C class")
}
}
var type1 = A.self
var type2 = B.self
func check(cls: AnyClass)->Void {
switch cls {
case is A.Type:
A.foo()
case is B.Type:
B.foo()
default:
break
}
}
check(type1) // A class
check(type2) // C class
let i = Int.self // Int.Type
let ao = AnyObject.self // AnyObject.Protocol
let ac = AnyClass.self // AnyClass.Protocol
let p = P.self // P.Protocol
let f = check.self // (AnyClass)->Void
Edit
protocol P {
static func f()
}
class A : P {
static func f() {
print("A")
}
}
class B : P {
static func f() {
print("B")
}
}
func f(cls: P.Type) {
cls.f()
}
f(A) // A
f(B) // B
class Test<T: P> {
func foo() {
T.f()
}
}
Test<A>().foo() // A
Test<B>().foo() // B
I am subclassing a Typhoon assembly such that stubbed implementations are returned, for unit testing purposes.
My assembly looks something like this:
class RealAssembly : TyphoonAssembly {
public dynamic func instanceToStubOut() -> AnyObject {
return TyphoonDefinition.withClass(SomeRealWorldClass.self)
}
public dynamic func instanceToTest() -> AnyObject {
return TyphoonDefinition.withClass(ClassToTest.self, configuration: { (definition : TyphoonDefinition!) -> Void in
definition.useInitializer("initWithObjectToStub:", parameters: { (initializer : TyphoonMethod!) -> Void in
initializer.injectParameterWith(self.instancetoStubOut())
})
})
}
}
My test class is solely for testing the instance of type ClassToTest, and I want to test it with the initializer-injected dependency on the object of type SomeRealWorldClass to be stubbed out. So I subclass RealAssembly so that instanceToStubOut() is overridden to return my stub object.
class MyTestClass : XCTestCase {
var assembly : TestAssembly!
class TestAssembly : RealAssembly {
override dynamic func instanceToStubOut() -> AnyObject {
return TyphoonDefinition.withClass(TestClass.self)
}
}
#objc
class TestClass : NSObject, ClassToStubOut {
func methodToStubOut() { /* do nothing */ }
}
override func setUp() {
super.setUp()
self.assembly = TestAssembly().activate()
}
override func tearDown() {
self.assembly = nil
super.tearDown()
}
func testStuff() {
let testingInstance = self.assembly.instanceToTest()
XCTAssertTrue(testingInstance.doStuff(), "doStuff returns true")
}
}
I expected this to work, but it doesn't. Typhoon seems to inject an uninitialized object instead of ever calling TestAssembly.instanceToStubOut()
Am I doing something wrong? Should I take a different approach?
EDIT: Here is some code you can paste into a Swift Playground that demonstrates the problem. The last line shows c.otherInstance returning nil :
import Typhoon
#objc
class BaseClass : NSObject {
var otherInstance : OtherProtocol!
func doIt() -> String {
return self.otherInstance.doStuff()
}
}
#objc
protocol OtherProtocol {
func doStuff() -> String
}
#objc
class OtherImpl : NSObject, OtherProtocol {
func doStuff() -> String {
return "OtherClass"
}
}
#objc
class StubClass : NSObject, OtherProtocol {
func doStuff() -> String {
return "Stubbed out"
}
}
class BaseAssembly : TyphoonAssembly {
dynamic func baseObject() -> AnyObject {
return TyphoonDefinition.withClass(BaseClass.self,
configuration: { (def : TyphoonDefinition!) -> Void in
def.injectProperty("otherInstance", with: self.otherObject())
})
}
dynamic func otherObject() -> AnyObject {
return TyphoonDefinition.withClass(OtherImpl.self)
}
}
var assembly = BaseAssembly()
assembly.activate()
var b = assembly.baseObject() as! BaseClass
b.doIt()
#objc
class TestAssembly : BaseAssembly {
override func otherObject() -> AnyObject {
return TyphoonDefinition.withClass(StubClass.self)
}
}
var testAssembly = TestAssembly()
testAssembly.activate()
var c = testAssembly.baseObject() as! BaseClass
c.otherInstance // this shouldn't be nil
Edit:
While patching is an option, as outlined in #Herman's answer below, what was attempted in the question is a supported feature, however there was a regression bug preventing it from working correctly.
The regression bug has been fixed in Typhoon 3.2.2 and so now both patching and overriding an assembly are again options for configuring Typhoon for a particular use-case.
Patching
There is a patching feature for this purpose in Typhoon. Look here.
For example:
class StubClass : NSObject, OtherProtocol {
#objc func doStuff() -> String {
return "Stubbed out"
}
}
let assembly = BaseAssembly()
assembly.activate()
let b = assembly.baseObject() as! BaseClass
print(b.doIt())
let testAssembly = BaseAssembly().activate()
let patcher = TyphoonPatcher()
patcher.patchDefinitionWithSelector("otherObject") { () -> AnyObject! in
return StubClass()
}
testAssembly.attachPostProcessor(patcher)
let c = testAssembly.baseObject() as! BaseClass
print(c.doIt())
I have the following class:
class BaseCache<T: Equatable>: NSObject {
var allEntities = [T]()
// MARK: - Append
func appendEntities(newEntities: [T]) {
....
}
}
Now I want to subclass it, but I get annoying error, that my type "does not conform to protocol 'Equatable'":
It seems generics in Swift are real pain-in-the-ass.
Your class definition of TrackingCache is wrong. It repeats the generic parameter:
class TrackingCache<AftershipTracking>: BaseCache<AftershipTracking> { }
It should be left out:
class TrackingCache: BaseCache<AftershipTracking> { }
This triggers the underlying swift error Classes derived from generic classes must also be generic. You can work around this issue by specifying a type parameter that is required to be or inherit from AftershipTracking:
class TrackingCache<T: AftershipTracking>: BaseCache<AftershipTracking> { }
Full example:
class BaseCache<T: Equatable>: NSObject {
var items: [T] = []
func appendItems( items: [T]) {
self.items += items
didAppendItems()
}
func didAppendItems() {} // for overriding
}
class AftershipTracking: NSObject {
var identifier: Int
init( identifier: Int) {
self.identifier = identifier
super.init()
}
}
extension AftershipTracking: Equatable { }
func ==( lhs: AftershipTracking, rhs: AftershipTracking) -> Bool {
return lhs.identifier == rhs.identifier
}
class TrackingCache<T: AftershipTracking>: BaseCache<AftershipTracking> {
override func didAppendItems() {
// do something
}
}
let a = TrackingCache<AftershipTracking>()
let b = TrackingCache<AftershipTracking>()
a.appendItems( [AftershipTracking( identifier: 1)])
b.appendItems( [AftershipTracking( identifier: 1)])
let result = a.items == b.items // true
this should work: < swift 4 >
class TrackingCache<T: AftershipTracking>: BaseCache<T>
Another example:
protocol P {
}
class C: P {
}
class CS: C {
}
class L<T:P> {
let c: T
init(_ c: T) {
self.c = c
}
}
class LS<T:CS>:L<T> {
}
let i = LS(CS())
i.c
c is CS now.