I have a generic protocol, TwoWayBindDelegate, that uses the generic associated type to determine the parameters of the function twoWayBind()
protocol TwoWayBindDelegate: class {
associatedtype BindType
func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?)
}
I then created a class, Reactive<Base: UIView, Type> (which conforms to TwoWayBindDelegate) where you have to initialize it with the generic Base. For eg: let reactiveSlider = Reacive<UISlider>(slider).
My issue is when I am extending Reactive and conforming to TwoWayBindDelegate, I get an error Invalid redeclaration of 'BindType' because I am declaring BindType and twoWayBind() in both my extensions. Is there a way I can have both extensions provide different implementations for TwoWayBindDelegate
class Reactive<Base: UIView>: TwoWayBindDelegate {
public var base: Base
init(base: Base) {
self.base = base
}
}
extension Reactive where Base == UISlider {
typealias BindType = Float
func twoWayBind(to observable: Observable<Float>?, observableChanged: ((Float) -> ())?) {
// implement two way bind for UISlider
}
}
extension Reactive where Base == UITextField {
typealias BindType = String
func twoWayBind(to observable: Observable<String>?, observableChanged: ((String) -> ())?) {
// implement two way bind for UITextField
}
}
I did some research and found out that it may be a bug https://bugs.swift.org/browse/SR-5392. Does there happen to be a workaround
I don't quite see what the typealias is for. The function declaration alone is sufficient to tell the compiler what BindType must be.
The problem I found with your code (apart from the missing Observable declaration, of course) is that the Reactive class itself doesn't conform to TwoWayBindDelegate. To get around that, I threw in an arbitrary implementation of twoWayBind. When I did, and when I deleted the unnecessary typealias declarations, your code compiled for me:
struct Observable<T> {}
protocol TwoWayBindDelegate: class {
associatedtype BindType
func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?)
}
class Reactive<Base: UIView>: TwoWayBindDelegate {
public var base: Base
init(base: Base) {
self.base = base
}
func twoWayBind(to observable: Observable<Int>?, observableChanged: ((Int) -> ())?) {
}
}
extension Reactive where Base == UISlider {
func twoWayBind(to observable: Observable<Float>?, observableChanged: ((Float) -> ())?) {
}
}
extension Reactive where Base == UITextField {
func twoWayBind(to observable: Observable<String>?, observableChanged: ((String) -> ())?) {
}
}
Related
I am new to Swift, but I have plenty of experience in other languages like Java, Kotlin, Javascript, etc. It's possible that what I want to do is not supported by the language, and I've poured over the Swift Language Guide looking for the answer.
I want to implement the decorator pattern, using generics. I easily did this in Kotlin, and I'm porting the library to Swift.
class Result<T> {
let result: T?
let error: NSError?
init(result: T?, error: NSError?) {
self.result = result
self.error = error
}
}
protocol DoSomething {
associatedtype T
func doSomething() -> Result<T>
}
protocol StoreSomething {
associatedtype T
func storeSomething(thing: Result<T>)
}
/*
* DOES NOT COMPILE
*/
class StoringSomething<T> {
private let delegate: DoSomething
private let store: StoreSomething
init(delegate: DoSomething, store: StoreSomething) {
self.delegate = delegate
self.store = store
}
func doSomething() -> Result<T> {
let result = delegate.doSomething()
store.storeSomething(thing: result)
return result
}
}
I get a Protocol 'DoSomething' can only be used as a generic constraint because it has Self or associated type requirements error from the compiler. I've tried using a typealias and other ideas from SO and the Swift manual.
Thanks to #Sweeper's suggestion on associatedtype erasure you can implement the Decorator pattern with generics like so:
class AnyDoSomething<T>: DoSomething {
func doSomething() -> Result<T> {
fatalError("Must implement")
}
}
class AnyStoreSomething<T>: StoreSomething {
func storeSomething(thing: Result<T>) {
fatalError("Must implement")
}
}
class StoringSomething<T>: DoSomething {
private let delegate: AnyDoSomething<T>
private let store: AnyStoreSomething<T>
init(delegate: AnyDoSomething<T>, store: AnyStoreSomething<T>) {
self.delegate = delegate
self.store = store
}
func doSomething() -> Result<T> {
let result = delegate.doSomething()
store.storeSomething(thing: result)
return result
}
}
class DoSomethingNice<T>: AnyDoSomething<T> {
override func doSomething() -> Result<T> {
}
}
I have this protocols:
One to instantiate a ViewController from Storyboard:
protocol Storyboarded {
static func instantiate() -> Self
}
extension Storyboarded where Self: UIViewController {
static func instantiate() -> Self {
// this pulls out "MyApp.MyViewController"
let fullName = NSStringFromClass(self)
// this splits by the dot and uses everything after, giving "MyViewController"
let className = fullName.components(separatedBy: ".")[1]
// load our storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// instantiate a view controller with that identifier, and force cast as the type that was requested
return storyboard.instantiateViewController(withIdentifier: className) as! Self
}
}
One to inject Dependencies in to Viewcontrollers:
protocol DependencyInjection where Self: UIViewController {
associatedtype myType: DependencyVC
func injectDependencys(dependency: myType)
}
Now I want to add another one, so I can create the ViewController from the Dependency itself:
protocol DependencyVC {
associatedtype myType: DependencyInjectionVC & Storyboarded
func createVC() -> myType
}
extension DependencyVC {
func makeVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self {
let viewController = T.instantiate()
viewController.injectDependencys(dependency: self)
return viewController
}
}
But I get this error for self:
Cannot invoke 'injectDependencys' with an argument list of type
'(dependency: Self)'
This is a DependencyClass I have:
class TopFlopDependency: DependencyVC {
typealias myType = TopFlopVC
var topFlopState: TopFlopState
lazy var topFlopConfig: TopFlopConfig = {
let SIBM = StatIntervalBaseModel(stat: "ppc", interval: "24h", base: "usd")
return TopFlopConfig(group: Groups.large, base: "usd", valueOne: SIBM)
}()
init(state: TopFlopState) {
self.topFlopState = state
}
func createVC() -> TopFlopVC {
let topflopVC = TopFlopVC.instantiate()
topflopVC.injectDependencys(dependency: self)
let viewController: TopFlopVC = makeVC()
return topflopVC
}
}
I get this error when using makeVC:
'TopFlopDependency' requires the types 'TopFlopDependency.myType' and
'TopFlopDependency.myType' (aka 'TopFlopVC') be equivalent to use
'makeVC'
other Solution:
protocol DependencyVC {
}
extension DependencyVC {
func makeVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self {
let viewController = T.instantiate()
viewController.injectDependencys(dependency: self)
return viewController
}
}
When trying to use:
let viewController: TopFlopVC = makeVC()
I get the error that T could not be inferred.
Why can I not do this? Do you have a solution how I would get it to work?
Thank you!
You need to add another constraint. Your DependencyInjection protocol requires a very specific type of DependencyVC (myType). But your DependencyVC extension works with any DependencyVC. So you need to constrain T’s myType to be the same type with a where clause: func createVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self
So a complete example would look like this:
protocol Storyboarded {
static func instantiate() -> Self
}
extension Storyboarded where Self: UIViewController {
static func instantiate() -> Self {
...
}
}
protocol DependencyVC {
}
protocol DependencyInjection where Self: UIViewController {
associatedtype myType: DependencyVC
func injectDependencys(dependency: myType)
}
extension DependencyVC {
func makeVC<T: Storyboarded & DependencyInjection>(type _: T.Type? = nil) -> T where T.myType == Self {
let viewController = T.instantiate()
viewController.injectDependencys(dependency: self)
return viewController
}
}
struct MyDependency: DependencyVC {}
class MyVC: UIViewController, Storyboarded, DependencyInjection {
func injectDependencys(dependency: MyDependency) {
print(dependency)
}
}
When you call viewController.injectDependencys(dependency: self), self is known to be of some subtype of DependencyVC. However, DependencyInjection's associatedtype myType: DependencyVC just says that a type conforming to DependencyInjection will use some type for myType (that conforms to DependencyVC). So there's no guarantee that its actual type will be a subtype of myType.
associatedtypes don't quite work the same way as generic type parameters in that associatedtypes are given when "defining" a type, while generic type parameters are given when "using" a type.
It all boils down to the fact that you probably don't want to have an associatedtype myType, instead taking a DependencyVC directly.
Update
In light of the additional information you've provided, I believe this would be the best solution:
protocol DependencyInjection where Self: UIViewController {
func injectDependency(_ dependency: DependencyVC)
}
protocol DependencyVC {
func makeVC<T: Storyboarded & DependencyInjection>() -> T
}
extension DependencyVC {
func makeVC<T: Storyboarded & DependencyInjection>() -> T {
let viewController = T.instantiate()
viewController.injectDependency(self)
return viewController
}
}
As you may notice, I took the liberty of renaming injectDependencys(dependency: DependencyVC) to injectDependency(_ dependency: DependencyVC), because you're only injecting one dependency and the dependency: label doesn't really add anything at the call site.
Anyway, this allows you to create instances of view controllers using your dependency. Say you have the dependency stored in a variable named dependency, then you can create a view controller from it by going let topFlopVC: TopFlopVC = dependency.makeVC()
I've tried to use generic type with protocol:
class Weak<T: AnyObject> {
weak var value: AnyObject?
init (value: AnyObject) {
self.value = value
}
}
protocol SomeProtocol: AnyObject {
func doSomething()
}
func createWeak(object: SomeProtocol) -> Weak<SomeProtocol> {
return Weak<SomeProtocol>(value: object)
}
class SomeClass: SomeProtocol {
func doSomething() {
print("Some 2")
}
}
let temp = SomeClass()
let weakObject = createWeak(object: temp)
weakObject.value?.doSomething()
And got the compiler error:
error: 'SomeProtocol' is not convertible to 'AnyObject'
return Weak(value: object)
But without AnyObject constraint it works fine
class Weak<T> {
var value: T?
init (value: T) {
self.value = value
}
}
protocol Protocol {
func doSomething()
}
class Class: Protocol {
func doSomething() {
print("This is class")
}
}
func createWeak(object: Protocol) -> Weak<Protocol> {
return Weak(value: object)
}
let temp = Class()
let weakObject = createWeak(object: temp)
weakObject.value?.doSomething()
Why I can't use protocols inherited form AnyObject in generic classes?
Swift protocols are incomplete types, which means that you can't use them in places like generic arguments, as the compiler needs to know the whole type details so it can allocate the proper memory layout.
Your createWeak function can still be used if you make it generic:
func createWeak<T: SomeProtocol>(object: T) -> Weak<T> {
return Weak<T>(value: object)
}
The above code works because the compiler will generate at compile time a function mapped to the concrete type you pass.
Even better, you can make the initializer generic, and convert Weak to a struct (value types are preferred Swift over reference ones):
struct Weak<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
which you can use it instead of the free function:
let weakRef = Weak(temp)
I want to implement something like "registerClassForAction".
For that purpose, I have defined a protocol:
#objc protocol TestProt {
func testMe() -> String
}
Let's do a class declaration:
class TestClass: NSObject, TestProt {
func testMe() -> String {
return "test"
}
}
I define the function to register the object in another class:
func registerClassForAction(aClass: AnyClass) { ... }
Switching to the REPL, I'd simulate the register method:
let aClass: AnyClass = TestClass.classForCoder() //or .self
let tClass = aClass as NSObject.Type
let tInst = tClass() as TestProt
tInst.testMe()
This currently works but is there another way to instantiate tClass, other than with
let tClass = aClass as NSObject.Type
Reason for asking, I'd like to explore the chance of getting rid of the NSObject so my TestClass does not to inherit from NSObject. Delegation was considered, but I'd like to control the lifetime of tInst and be able to dealloc it at a specific point in time.
thanks for helping
Ron
This is possible in Swift 2.0 without requiring #objc or subclassing NSObject:
protocol TestProt {
func testMe() -> String
}
class TestClass: TestProt {
// This init is required in order
// to construct an instance with
// a metatype value (class.init())
required init() {
}
func testMe() -> String {
return "Hello from TestClass"
}
}
let theClass = TestClass.self
let tInst: TestProt = theClass.init()
tInst.testMe()
I have a class that needs to call out to a delegate when one of its properties changes. Here are the simplified class and protocol for the delegate:
protocol MyClassDelegate: class {
func valueChanged(myClass: MyClass)
}
class MyClass {
weak var delegate: MyClassDelegate?
var currentValue: Int {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: Int) {
currentValue = initialValue
}
}
This all works just fine. But, I want to make this class generic. So, I tried this:
protocol MyClassDelegate: class {
func valueChanged(genericClass: MyClass)
}
class MyClass<T> {
weak var delegate: MyClassDelegate?
var currentValue: T {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: T) {
currentValue = initialValue
}
}
This throws two compiler errors. First, the line declaring valueChanged in the protocol gives: Reference to generic type 'MyClass' requires arguments in <...>. Second, the call to valueChanged in the didSet watcher throws: 'MyClassDelegate' does not have a member named 'valueChanged'.
I thought using a typealias would solve the problem:
protocol MyClassDelegate: class {
typealias MyClassValueType
func valueChanged(genericClass: MyClass<MyClassValueType>)
}
class MyClass<T> {
weak var delegate: MyClassDelegate?
var currentValue: T {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: T) {
currentValue = initialValue
}
}
I seem to be on the right path, but I still have two compiler errors. The second error from above remains, as well as a new one on the line declaring the delegate property of MyClass: Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements.
Is there any way to accomplish this?
It is hard to know what the best solution is to your problem without having more information, but one possible solution is to change your protocol declaration to this:
protocol MyClassDelegate: class {
func valueChanged<T>(genericClass: MyClass<T>)
}
That removes the need for a typealias in the protocol and should resolve the error messages that you've been getting.
Part of the reason why I'm not sure if this is the best solution for you is because I don't know how or where the valueChanged function is called, and so I don't know if it is practical to add a generic parameter to that function. If this solution doesn't work, post a comment.
You can use templates methods with type erasure...
protocol HeavyDelegate : class {
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}
class Heavy<P, R> {
typealias Param = P
typealias Return = R
weak var delegate : HeavyDelegate?
func inject(p : P) -> R? {
if delegate != nil {
return delegate?.heavy(self, shouldReturn: p)
}
return nil
}
func callMe(r : Return) {
}
}
class Delegate : HeavyDelegate {
typealias H = Heavy<(Int, String), String>
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
let h = heavy as! H // Compile gives warning but still works!
h.callMe("Hello")
print("Invoked")
return "Hello" as! R
}
}
let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))
Protocols can have type requirements but cannot be generic; and protocols with type requirements can be used as generic constraints, but they cannot be used to type values. Because of this, you won't be able to reference your protocol type from your generic class if you go this path.
If your delegation protocol is very simple (like one or two methods), you can accept closures instead of a protocol object:
class MyClass<T> {
var valueChanged: (MyClass<T>) -> Void
}
class Delegate {
func valueChanged(obj: MyClass<Int>) {
print("object changed")
}
}
let d = Delegate()
let x = MyClass<Int>()
x.valueChanged = d.valueChanged
You can extend the concept to a struct holding a bunch of closures:
class MyClass<T> {
var delegate: PseudoProtocol<T>
}
struct PseudoProtocol<T> {
var valueWillChange: (MyClass<T>) -> Bool
var valueDidChange: (MyClass<T>) -> Void
}
Be extra careful with memory management, though, because blocks have a strong reference to the object that they refer to. In contrast, delegates are typically weak references to avoid cycles.