Swift Generics: Extending a non-generic type with a property of generic type where the generic parameter is the extended type - swift

Problem
I have a type that takes one generic parameter that is required to inherit from UIView:
class Handler<View: UIView> {
...
}
Now, I want write a UIView extension to provide a property that returns Handler and uses Self as the generic parameter, so that in subclasses of UIView I'd always get the handler of type Handler<UIViewSubclass>:
extension UIView {
var handler: Handler<Self>? { return nil }
}
However this does not compile:
Covariant 'Self' can only appear at the top level of property type
I have also tried to define a protocol HandlerProvider first:
public protocol HandlerProvider {
associatedtype View: UIView
var handler: Handler<View>? { get }
}
(so far so good) and then extend UIView with that protocol:
extension UIView: HandlerProvider {
public typealias View = Self
public var handler: Handler<View>? { return nil }
}
But that does not compile either:
Covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'UIView'?
Question
Is there a way in Swift to use Self as a generic parameter for properties in extension?

Here is possible approach (to think with generics in a bit different direction).
Tested with Xcode 11.4 / swift 5.2
// base handling protocol
protocol Handling {
associatedtype V: UIView
var view: V { get }
init(_ view: V)
func handle()
}
// extension for base class, will be called by default for any
// UIView instance that does not have explicit extension
extension Handling where V: UIView {
func handle() {
print(">> base: \(self.view)")
}
}
// extension for specific view (any more you wish)
extension Handling where V: UIImageView {
func handle() {
print(">> image: \(self.view)")
}
}
// concrete implementer
class Handler<V: UIView>: Handling {
let view: V
required init(_ view: V) {
self.view = view
}
}
// testing function
func fooBar() {
// handlers created in place of handling where type of
// handling view is know, so corresponding handle function
// is used
Handler(UIView()).handle()
Handler(UIImageView()).handle()
}
Output:

Related

How to write a generic Swift class function to initialize classes using a closure?

I'm trying to write a generic class function for Swift classes that would allow me to initialize classes using trailing closure syntax.
I have already got it working for specific classes, like for example UILabel.
// Extension for UILabel
extension UILabel {
class func new(_ initialization: (inout UILabel) -> Void) -> UILabel {
var label = UILabel()
initialization(&label)
return label
}
}
// Initialize new UILabel using trailing closure syntax and "new" function
let label = UILabel.new {
$0.textColor = .red
}
However, I want to have this functionality for all subclasses of NSObject, so I'm trying to implement a generic version of the "new" function above. So far I have come up with this:
extension NSObject {
class func new(_ initialization: (inout Self) -> Void) -> Self {
var newSelf = Self()
initialization(&newSelf)
return newSelf
}
}
But this produces the following error: 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'NSObject'?
I am trying this in a playground with Swift 5.1 (Xcode 11 beta).
As Hamish said, maybe it's better to leave the init outside so it is more flexible. However there could be a kind of workaround for this using a protocol.
protocol Initializable {
init()
}
extension Initializable {
static func new(_ initialization: (inout Self) -> Void) -> Self {
var newSelf = Self()
initialization(&newSelf)
return newSelf
}
}
extension NSObject: Initializable {}
NSObject already have an init so it automatically conforms to Initializable.
Then write your extension on the protocol.
The only thing to be aware is that you cannot use class modifier now, as you're in a protocol.
Not sure if this can lead to problem for the NS_UNAVAILABLE modifier, I think it could crash at runtime when you use this on a class that is hiding the init.

Swift require protocol extension

If I have
protocol AppearingTextContainer {
func clipBounds() -> ()
}
extension AppearingTextContainer where Self: UIView {
func clipBounds() { self.clipsToBounds = true }
}
then a class that adopts my protocol CustomView: AppearingTextContainer is not forced by the compiler to implement clipBounds. If I remove the extension it won't compile. Is there any way to enforce CustomView to call clipBounds without having to delete the default clipBounds implementation?
Thanks
You've only provided a method that will become available to any UIView that chooses to conform to AppearingTextContainer.
So now ideally you would create a UIView subclass that conforms
class AppearingTextView: UIView, AppearingTextContainer {
// you can now call clipBounds() on this class
}
or
extension UIView: AppearingTextView { }
what I think you should consider is whether or not what you really want is a protocol extension vs. just an extension on UIView:
extension UIView {
func clipsBounds() -> Void { clipsToBounds = true }
}
if you want a protocol, I suggest going the route of:
protocol ClippingView {
var clipsToBounds: Bool { get set }
}
extension ClippingView {
func clipsBounds() -> Void { clipsToBounds = true }
}
extension UIView: ClippingView { }
let clippingViews: [ClippingView] = [......]
clippingViews.forEach { $0.clipsBounds() }
If your custom view class has its own clipBounds function, then you would have to cast your custom view to an AppearingTextContainer in order to get the AppearingTextContainer protocol extension implementation.
But keep in mind that Objective-C cannot see your protocol extension, so Cocoa will never call the protocol extension clipBounds for you; only a direct Swift call can call it.

Swift implement multiple protocols with a delegate

I'm trying to implement a protocol that itself inherits multiple protocols that both have a delegate member. Is there a clean way to do this without needing different names for the delegate of each protocol?
protocol ProtocolOne {
var delegate: ProtocolOneDelegate?
}
protocol ProtocolTwo {
var delegate: ProtocolTwoDelegate?
}
protocol CombinedProtocol: ProtocolOne, ProtocolTwo {
}
protocol CombinedDelegate: ProtocolOneDelegate, ProtocolTwoDelegte {
}
class ProtocolImpl: CombinedProtocol {
// How can I implement delegate here?
// I've tried the following options without success:
var delegate: CombinedDelegate?
var delegate: protocol<ProtocolOneDelegate, ProtocolTwoDelegate>?
}
You should be able to combine them in one:
var delegate: (ProtocolOneDelegate & ProtocolTwoDelegate)?
You can now use both protocols.
In your code, delegate is just a normal property. You can have multiple protocols declaring a property with the same name and same type, and have a class directly or indirectly implement it.
If different protocols define a property with the same name but different type, you won't be able to make it compile, because the compiler will complain for redeclaration of a property and class not confirming to one of the protocols.
There are 2 possible solution. The most obvious one is to avoid using names having high probability of being used in other protocols - delegate is a typical case. Use a different naming convention, such as protocol1Delegate, dataSourceDelegate, apiCallDelegate, etc.
The 2nd solution consists of replacing properties with methods. For example:
protocol P1 {
func test() -> String?
}
protocol P2 {
func test() -> Int?
}
protocol P3: P1, P2 {
}
class Test : P3 {
func test() -> String? { return nil }
func test() -> Int? { return nil }
}
Swift consider functions with the same parameters list but different return type as overloads. Note however that if 2 protocols use the same function signature (name, parameters and return type), when implementing in the class you will implement that function once - that might be the wanted behavior in some cases, but unwanted in other cases.
A solution might be to use protocol extensions (check extension Combined). The benefit is that Combined only declares delegate and oneDelegate and twoDelegate are computed cross-implementation. Unfortunately, it's a requirement to have the three variables exposed out of the class, that might be inconvenient.
// MARK: - Delegates protocols
protocol OneDelegate {
func oneDelegate(one: One)
}
protocol TwoDelegate {
func twoDelegate(two: Two)
}
protocol CombinedDelegate: OneDelegate, TwoDelegate {
func combinedDelegate(combined: Combined)
}
// MARK: - Model protocols
protocol One: class {
var oneDelegate: OneDelegate? { get }
}
protocol Two: class {
var twoDelegate: TwoDelegate? { get }
}
protocol Combined: One, Two {
var delegate: CombinedDelegate? { get }
}
extension Combined {
var oneDelegate: OneDelegate? {
return delegate
}
var twoDelegate: TwoDelegate? {
return delegate
}
}
// MARK: - Implementations
class Delegate: CombinedDelegate {
func oneDelegate(one: One) {
print("oneDelegate")
}
func twoDelegate(two: Two) {
print("twoDelegate")
}
func combinedDelegate(combined: Combined) {
print("combinedDelegate")
}
}
class CombinedImpl: Combined {
var delegate: CombinedDelegate?
func one() {
delegate?.oneDelegate(self)
}
func two() {
delegate?.twoDelegate(self)
}
func combined() {
delegate?.combinedDelegate(self)
}
}
// MARK: - Usage example
let delegate = Delegate()
let protocolImpl = CombinedImpl()
protocolImpl.delegate = delegate
protocolImpl.one()
protocolImpl.two()
protocolImpl.combined()

Call methods on Protocol Type

I've searched quite a bit around and played within a playground but I had no success, so I ask here:
Any way to have variable containing a non-#objc protocol metatype and then call class/static methods from it?
e.g.:
protocol MyProtocol {
class func myFunc() -> Int
}
enum MyEnum: Int, MyProtocol {
case A
case B
static func myFunc() -> Int { return A.rawValue }
}
let foo: MyProtocol.Type = MyEnum.self
let bar = foo.myFunc()
p.s. it says the last call is unimplemented, so should I expect it in a future Swift release?
UPDATED for Swift Version 2.0 and above
Swift 2.0+ allows methods to be declared as static in the protocol definition. These must be satisfied with static/class methods in objects that implement the protocol.
You cannot satisfy a protocol definition for a instance method with a static method or vice-versa, which makes this an incomplete answer for the question above.
If you want to try this just use the keyword "static" in your protocol definition for methods you will implement as static or class methods in your conforming objects:
protocol InstanceVsStatic {
func someInstanceFunc()
static func someStaticFunc()
}
enum MyConformingEnum: InstanceVsStatic {
case someCase
static func someStaticFunc() {
// code
}
func someInstanceFunc() {
// code
}
}
class MyConformingClass: InstanceVsStatic {
class func someStaticFunc() {
// code
}
func someInstanceFunc() {
// code
}
}
struct MyConformingStruct: InstanceVsStatic {
static func someStaticFunc() {
// code
}
func someInstanceFunc() {
// code
}
}
You can have an instance method call a static/class method:
This allows you to execute static code when you need to conform to a protocol that requires an instance method.
struct MyConformingStruct: InstanceVsStatic {
static func doStuffStatically(){
// code
}
static func someStaticFunc() {
// code
}
func someInstanceFunc() {
MyConformingStruct.doStuffStatically()
}
}
Swift 1.2
Other than indirectly as above, there is no way to use static (class) methods to conform to a protocol in pure swift version 1.2 and below. It is a known bug / unimplemented feature: https://openradar.appspot.com/20119848
Class methods are only allowable in classes and protocols; an enum is neither.
Class methods are one type of Swift "type methods"; the other type is a static method, which is what you've declared in your enum. Class and static methods are different.
See the Swift methods docs for a few different patterns and examples.
p.s. it says the last call is unimplemented, so should I expect it in a future Swift release?
Yes.

Swift - class method which must be overridden by subclass

Is there a standard way to make a "pure virtual function" in Swift, ie. one that must be overridden by every subclass, and which, if it is not, causes a compile time error?
You have two options:
1. Use a Protocol
Define the superclass as a Protocol instead of a Class
Pro: Compile time check for if each "subclass" (not an actual subclass) implements the required method(s)
Con: The "superclass" (protocol) cannot implement methods or properties
2. Assert in the super version of the method
Example:
class SuperClass {
func someFunc() {
fatalError("Must Override")
}
}
class Subclass : SuperClass {
override func someFunc() {
}
}
Pro: Can implement methods and properties in superclass
Con: No compile time check
The following allows to inherit from a class and also to have the protocol's compile time check :)
protocol ViewControllerProtocol {
func setupViews()
func setupConstraints()
}
typealias ViewController = ViewControllerClass & ViewControllerProtocol
class ViewControllerClass : UIViewController {
override func viewDidLoad() {
self.setup()
}
func setup() {
guard let controller = self as? ViewController else {
return
}
controller.setupViews()
controller.setupConstraints()
}
//.... and implement methods related to UIViewController at will
}
class SubClass : ViewController {
//-- in case these aren't here... an error will be presented
func setupViews() { ... }
func setupConstraints() { ... }
}
There isn't any support for abstract class/ virtual functions, but you could probably use a protocol for most cases:
protocol SomeProtocol {
func someMethod()
}
class SomeClass: SomeProtocol {
func someMethod() {}
}
If SomeClass doesn't implement someMethod, you'll get this compile time error:
error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
Another workaround, if you don't have too many "virtual" methods, is to have the subclass pass the "implementations" into the base class constructor as function objects:
class MyVirtual {
// 'Implementation' provided by subclass
let fooImpl: (() -> String)
// Delegates to 'implementation' provided by subclass
func foo() -> String {
return fooImpl()
}
init(fooImpl: (() -> String)) {
self.fooImpl = fooImpl
}
}
class MyImpl: MyVirtual {
// 'Implementation' for super.foo()
func myFoo() -> String {
return "I am foo"
}
init() {
// pass the 'implementation' to the superclass
super.init(myFoo)
}
}
You can use protocol vs assertion as suggested in answer here by drewag.
However, example for the protocol is missing. I am covering here,
Protocol
protocol SomeProtocol {
func someMethod()
}
class SomeClass: SomeProtocol {
func someMethod() {}
}
Now every subclasses are required to implement the protocol which is checked in compile time. If SomeClass doesn't implement someMethod, you'll get this compile time error:
error: type 'SomeClass' does not conform to protocol 'SomeProtocol'
Note: this only works for the topmost class that implements the protocol. Any subclasses can blithely ignore the protocol requirements. – as commented by memmons
Assertion
class SuperClass {
func someFunc() {
fatalError("Must Override")
}
}
class Subclass : SuperClass {
override func someFunc() {
}
}
However, assertion will work only in runtime.
This is what I usually do, to causes the compile-time error :
class SuperClass {}
protocol SuperClassProtocol {
func someFunc()
}
typealias SuperClassType = SuperClass & SuperClassProtocol
class Subclass: SuperClassType {
func someFunc() {
// ...
}
}
You can achieve it by passing function into initializer.
For example
open class SuperClass {
private let abstractFunction: () -> Void
public init(abstractFunction: #escaping () -> Void) {
self.abstractFunction = abstractFunction
}
public func foo() {
// ...
abstractFunction()
}
}
public class SubClass: SuperClass {
public init() {
super.init(
abstractFunction: {
print("my implementation")
}
)
}
}
You can extend it by passing self as the parameter:
open class SuperClass {
private let abstractFunction: (SuperClass) -> Void
public init(abstractFunction: #escaping (SuperClass) -> Void) {
self.abstractFunction = abstractFunction
}
public func foo() {
// ...
abstractFunction(self)
}
}
public class SubClass: SuperClass {
public init() {
super.init(
abstractFunction: {
(_self: SuperClass) in
let _self: SubClass = _self as! SubClass
print("my implementation")
}
)
}
}
Pro:
Compile time check for if each subclassimplements the required method(s)
Can implement methods and properties in superclass
Note that you can't pass self to the function so you won't get memory leak.
Con:
It's not the prettiest code
You can't use it for the classes with required init
Being new to iOS development, I'm not entirely sure when this was implemented, but one way to get the best of both worlds is to implement an extension for a protocol:
protocol ThingsToDo {
func doThingOne()
}
extension ThingsToDo {
func doThingTwo() { /* Define code here */}
}
class Person: ThingsToDo {
func doThingOne() {
// Already defined in extension
doThingTwo()
// Rest of code
}
}
The extension is what allows you to have the default value for a function while the function in the regular protocol still provides a compile time error if not defined