Swift conform to protocol subclass - ios

Within my app, I have multiple UIView subclasses that depend on a model. Each of the classes adopting 'Restorable' protocol which holds the superclass of the model. Each sub-model describes the specific UIView not-common properties.
// Super-model
public protocol StoryItem {
var id: Int64? { get }
}
// Parent protocol
public protocol Restorable: AnyObject {
var storyItem: StoryItem? { get set }
}
// Specific protocol
public struct TextItem: StoryItem {
public var id: Int64?
public var text: String?
}
// Not complling
class ResizableLabel: UILabel, Restorable {
var storyItem: TextItem?
}
I'm getting the following compiler error:
*Type 'ResizableLabel' does not conform to protocol 'Restorable'*
The only way I can make it compile is by changing ResizableLabel to
// Works
class ResizableLabel: UILabel, Restorable {
var storyItem: StoryItem?
}
Is there any way to conform to protocol subclass? it'll make the Init process much cleaner. Thank you for your help!

Change
public protocol Restorable: AnyObject {
var storyItem: StoryItem? { get set } // adopter must declare as StoryItem
}
to
public protocol Restorable: AnyObject {
associatedtype T : StoryItem
var storyItem: T? { get set } // adopter must declare as StoryItem adopter
}
Now your code compiles. Full example:
public protocol StoryItem {
var id: Int64? { get }
}
public protocol Restorable: AnyObject {
associatedtype T : StoryItem
var storyItem: T? { get set }
}
public struct TextItem: StoryItem {
public var id: Int64?
public var text: String?
}
class ResizableLabel: UILabel, Restorable {
var storyItem: TextItem? // ok because TextItem is a StoryItem adopter
}

Related

Extending structs with protocols in Swift

I have the following piece of code. The protocols and extensions I've added don't seem to be doing quite what I expect. What am I missing?
struct foo {
var getFoo: String
var getBar: bar
struct bar {
var getBar: String
}
}
protocol FooProtocol {
var getFoo: String { get }
var getBar: BarProtocol { get }
}
extension foo: FooProtocol {} // Type 'foo' does not conform to protocol 'FooProtocol'
protocol BarProtocol {
var getBar: String { get }
}
extension foo.bar: BarProtocol {}
The problem in this code is the line extension foo: FooProtocol {}, where we're trying to say that, in effect foo.getBar.getBar should be a valid property. However, Xcode throws up a compilation error saying "Type 'foo' does not conform to protocol 'FooProtocol'.
I can fix this by changing the following:
protocol FooProtocol {
var getFoo: String { get }
var getBar: foo.bar { get } // instead of var getBar: BarProtocol { get }
}
However, it seems the line extension foo.bar: BarProtocol {} should mean FooProtocol doesn't care if we give it foo.bar or BarProtocol. What am I missing here?
#Drobs answer was correct. Here's a bonus question.
BONUS
Now suppose I need to implement another struct that conforms to FooProtocol. Neither of these approaches work. What's the fix?
struct FooImplementation: FooProtocol { // Type 'FooImplementation' does not conform to protocol 'FooProtocol'
var getFoo: String
var getBar: BarProtocol
}
Do I need to use some sort of typealias to achieve this?
You want to use an associatedtype in the protocol. In that case it essentially says give me a type that implements the protocol BarProtocol.
protocol FooProtocol {
associatedtype BarType: BarProtocol
var getFoo: String { get }
var getBar: BarType { get }
}
protocol BarProtocol {
var getBar: String { get }
}
struct Foo: FooProtocol {
var getFoo: String
var getBar: Bar
struct Bar: BarProtocol {
var getBar: String
}
}

Swift different default implementations for protocol property

I know that you can give a default value with a protocol extension like this
protocol SomeProtocol {
var prop: String { get }
}
extension SomeProtocol {
var prop: String {
return "defaultValue"
}
}
struct SomeA: SomeProtocol {}
struct SomeB: SomeProtocol {}
let a = SomeA()
let b = SomeB()
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints defaultValue
but is there a way to have different default value for different implementations of the protocol like this without implementing the property for every class or struct that conforms to this protocol?
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints differentDefaultValue
or some similar pattern for doing something like this?
Protocol inheritance.
protocol 😺: SomeProtocol { }
extension 😺 {
var prop: String { "😺" }
}
struct SomeA: SomeProtocol { }
struct SomeB: 😺 { }
struct SomeC: 😺 { }
SomeA().prop // "defaultValue"
SomeB().prop // "😺"
SomeC().prop // "😺"

How to set a readwrite internal, readonly external property in protocol

I have some classes have same behaviors, they all have
properties > savedPath: String, items: [String], currentItem: [String]
functions > archive(with items: [String]), unarchive()
So I create a protocol.swift and let those classes conform this protocol to implement these common behavior.But, in my case, i want:
items is a readonly external, readwrite internal
archive/unarchive are private func
i tried to use private(set) before items, private before archive/unarchive and some errors showed up.
Is there any flexible solutions to fix that?
before without protocol
class SampleClass {
private(set) var items: [SampleModel] {
set {
archive(with: newValue)
} get {
return unarchive()
}
}
func archive(with addresses: [SampleModel]) { ... }
func unarchive() -> [SampleModel] { ... }
}
after try to use protocol to satisfy
protocol SampleProtocol {
associatedtype Item: Switchable
var savedPath: String { get }
var items: [Item] { get }
var currentItem: Item? { get }
func archive(with items: [Item])
func unarchive() -> [Item]
}
You must remove any private function from protocol (Since it's meaningless). Nothing is private inside a protocol( setter or function or etc. )
protocol SampleProtocol {
associatedtype Item: Switchable
var savedPath: String { get }
var items: [Item] { get }
var currentItem: Item? { get }
}
And you should then implement the class access controls like this:
class SampleClass {
private(set) var items: [SampleModel] {
set {
archive(with: newValue)
} get {
return unarchive()
}
}
private func archive(with addresses: [SampleModel]) { /* ... */ }
private func unarchive() -> [SampleModel] { /* ... */ }
}

Override swift protocol property to optional

I am using this library, https://github.com/gcharita/XMLMapper.
It contains protocol,
public protocol XMLBaseMappable {
var nodeName: String! { get set }
}
I would like to make this nodeName optional for my struct/classes implementing it, like
public protocol CustomXMLBaseMappable {
var nodeName: String? { return "default" }
//I don't want struct/classes using it, implements `nodeName`.
}
Any suggestions will be helpful.
The existence of this property in XMLBaseMappable protocol is crucial in order to function correctly the whole library.
Having said that, you can't omit the implementation of this property in your structs and classes but you can "hide" it in a super class. Using this:
class BasicXMLMappable: XMLMappable {
var nodeName: String!
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
}
}
You can have XMLMappable objects that extends BasicXMLMappable and they don't have to implement nodeName property:
class TestBasicXMLMappable: BasicXMLMappable {
// Your custom properties
required init(map: XMLMap) {
super.init(map: map)
}
override func mapping(map: XMLMap) {
// Map your custom properties
}
}
Edit:
As of version 1.5.1 you can use XMLStaticMappable for implementing XMLMapper in an extension. For example:
class CustomClass {
var property: String?
}
extension CustomClass: XMLStaticMappable {
var nodeName: String! {
get {
return "default"
}
set(newValue) {
}
}
static func objectForMapping(map: XMLMap) -> XMLBaseMappable? {
// Initialize CustomClass somehow
return CustomClass()
}
func mapping(map: XMLMap) {
property <- map["property"]
}
}
Hope this helps.
You can't override the original protocol, but you could manually change it like that:
#objc protocol XMLBaseMappable {
#objc optional var nodeName: String! { get set }
}
or leave it as it is, and create your custom protocol
#objc protocol CustomXMLBaseMappable {
#objc optional var nodeName: String! { get set }
}

Weak property requirement in Swift protocol with associated type

I want to write a protocol with weak property requirement. Class that conforms it must be able to specify any type for this property. Also I don't want to specify an actual type, so it should be a type specified with some protocol. This code shows my idea for non-weak property:
protocol ObjectProtocol: class {
typealias PropertyType
var property: PropertyType {get set}
}
protocol FirstPropertyProtocol: class {}
protocol SecondPropertyProtocol: class {}
class FirstObjectImpl: ObjectProtocol {
var property: FirstPropertyProtocol?
}
class SecondObjectImpl: ObjectProtocol {
var property: SecondPropertyProtocol?
}
It works as expected.
I tried to do the same for weak property:
protocol ObjectProtocol: class {
typealias WeakPropertyType: AnyObject //must be a class type
weak var weakProperty: WeakPropertyType? {get set}
}
protocol WeakPropertyProtocol: class {}
class ObjectImpl: ObjectProtocol {
weak var weakProperty: WeakPropertyProtocol?
}
And I got a compiler error:
Type 'ObjectImpl' does not conform to protocol 'ObjectProtocol'
Is there any way I can make this work?
I made it work with #objc attribute for WeakPropertyProtocol:
protocol ObjectProtocol: class {
typealias WeakPropertyType: AnyObject //must be a class type
weak var weakProperty: WeakPropertyType? {get set}
}
#objc protocol WeakPropertyProtocol {}
class SomeObjectImpl: ObjectProtocol {
weak var weakProperty: WeakPropertyProtocol?
}
It's not a best solution because I concern about this note from apple doc
Note also that #objc protocols can be adopted only by classes that
inherit from Objective-C classes or other #objc classes.
I can live with this restriction but I will appreciate any better solution.
I don't believe a protocol can enforce weak-ness. For example:
protocol ObjectProtocol: class {
weak var weakProperty: AnyObject? {get set}
}
class ObjectImpl1: ObjectProtocol {
weak var weakProperty: AnyObject?
}
class ObjectImpl2: ObjectProtocol {
var weakProperty: AnyObject?
}
These both compile ok, even though the protocol has weak but ObjectImpl2 does not implement it.
EDIT: Is this what you're after?...
protocol ObjectProtocol: class {
typealias WeakPropertyType: Any //must be a class type
var weakProperty: WeakPropertyType? {get set}
}
protocol WeakPropertyProtocol: class {}
class ObjectImpl: ObjectProtocol {
typealias WeakPropertyType = WeakPropertyProtocol
weak var weakProperty: WeakPropertyProtocol?
}
This implementation requires use of Any rather than AnyObject, since WeakPropertyProtocol is a protocol rather than a class.
Or this?...
protocol WeakPropertyProtocol: class {}
protocol ObjectProtocol: class {
typealias WeakPropertyType: AnyObject //must be a class type
var weakProperty: WeakPropertyType? {get set}
}
class MyWeakClass: WeakPropertyProtocol {
}
class ObjectImpl: ObjectProtocol {
typealias WeakPropertyType = MyWeakClass
weak var weakProperty: MyWeakClass?
}
Either way, I think the key is in defining which class/protocol to use for WeakPropertyType.
As Swift 5.7, bundled with Xcode 14.0, the compiler emits the error: "'weak' cannot be applied to a property declaration in a protocol"
A possible protocol limitation workaround uses a computed variable and a supplementary 'weak' variable wrapped by the computed variable.
Workaround 1: This workaround doesn't force types that apply the protocol to set the property to weak
protocol SharedInformation {
var name: UIViewController? { get set }
}
class Country: SharedInformation {
private weak var _name: UIViewController?
var name: UIViewController? {
get {
_name
}
set {
_name = newValue
}
}
}
Workaround 2: This one forces a type that applies the protocol to have the property wrapped with weak storage.
protocol SharedInformation {
var name: Weak<UIViewController> { get set }
}
class Country: SharedInformation {
var name: Weak<UIViewController> = .init()
func usage() {
// set
name.wrappedValue = UIViewController()
// get
let value = name.wrappedValue
}
}
public struct Weak<Wrapped: AnyObject> {
public weak var wrappedValue: Wrapped?
public init(_ value: Wrapped? = nil) {
self.wrappedValue = value
}
}
Swift 4 version.
I needed my view models to conform to a protocol. They mustn't retain a coordinator object:
protocol ViewModelType {
associatedtype CoordinatorType: AnyObject
weak var coordinator: CoordinatorType? { get }
}

Resources