In swift is it possible to declare a method property not as a type but as a protocol?
like:
protocol myProtocol {
var data1: String {get set}
var node: Int {get set}
}
class myData: myProtocol {
var data1: String = "Boo"
var node: int = 10
}
class myClass {
func myFunc(data: myProtocol) {
data.data1 = "Hello"
}
}
Basically I want to say to the method look I don't care about the type. As long as the object conforms to the protocol its ok
Yes this is fine, but to modify data you will have to declare a class only protocol.
protocol MyProtocol: AnyObject {
var data1: String {get set}
var node: Int {get set}
}
class MyData: MyProtocol {
var data1: String = "Boo"
var node: Int = 10
}
class MyClass {
func myFunc(data: MyProtocol) {
data.data1 = "Hello"
}
}
I've also fixed the capitalisation of your classes.
You can use associatedtype.
protocol MyProtocol {
associatedtype CustomData
var data1: String { get set }
var node: Int { get set }
func myFunc(data: CustomData)
}
class MyData: MyProtocol {
func myFunc(data: String) {
print(data)
}
var data1: String = "Boo"
var node: Int = 10
}
Also, you should use PascalCase for both protocols and classes, and Int is the integer type for swift.
EDIT:
I misunderstood your question. You can also specify a function parameter by an abstract protocol, not just a class or a struct!
protocol MyProtocol {
var data1: String { get set }
var node: Int { get set }
}
class MyData: MyProtocol {
var data1: String = "Boo"
var node: Int = 10
}
class MyClass {
func myFunc(data: MyProtocol) {
print(data.data1)
}
}
let data = MyData()
let instance = MyClass()
instance.myFunc(data: data) // Boo
Related
have been fighting the code for some time now, and can't resolve the issue of: Type 'American' does not conform to protocol 'Food'
protocol Food {
var type: String { get }
var ingredient1: String { get }
var price: Int { get set}
func showHistory()
mutating func transfer()
init(type: String)
init(ingredient1: String)
}
struct American: Food {
let type: String
let ingredient1: String
var price: Int = 125
init(type: String, ingredient1: String) {
self.type = type
self.ingredient1 = ingredient1
}
func showHistory() {
print("American history")
}
mutating func transfer() {
print("transfering burgers")
}
}
I doubt you intended to separate your inits into two separate calls which is causing your error. You could solve this by implementing two separate inits as well but then you'd have to initialize both properties in separate inits which will give you an error
protocol Food {
var type: String { get }
var ingredient1: String { get }
var price: Int { get set}
func showHistory()
mutating func transfer()
init(type: String, ingredient1: String)
}
struct American: Food {
let type: String
let ingredient1: String
var price: Int = 125
init(type: String, ingredient1: String) {
self.type = type
self.ingredient1 = ingredient1
}
func showHistory() {
print("American history")
}
mutating func transfer() {
print("transfering burgers")
}
}
the following code gives me a compile error
'WeakReference' requires that 'ServiceDelegate' be a class type
protocol ServiceDelegate: AnyObject {
func doIt()
}
class SomeClass() {
// compile error occurs in this line
private var observers = [WeakReference<ServiceDelegate>]()
}
WeakReference code:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
How can I fix this error? Delegate should be declared correctly as per this site.
What I have tried so far:
Changing the delegate protocol conformance from AnyObject to
class does not solve the problem.
Try the above code in a clean playground.
You can't have a WeakReference<ServiceDelegate>. ServiceDelegate itself is not an AnyObject, it just requires that anything that conforms to it be an AnyObject.
You would need to make SomeClass generic and use the generic type as the type for the WeakReference:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
If the generic on SomeClass is too constricting and you want to be able to have instances of multiple unrelated classes as observers then I would do it by abandoning the generic parameter on WeakReference:
final class WeakServiceDelegate {
private(set) weak var value: ServiceDelegate?
init(value: ServiceDelegate?) {
self.value = value
}
}
class SomeClass {
private var observers = [WeakServiceDelegate]()
}
Alternatively you could make WeakReference conditionally conform to ServiceDelegate:
extension WeakReference: ServiceDelegate where T: ServiceDelegate {
func doIt() {
value?.doIt()
}
}
And then use an array of ServiceDelegate in SomeClass:
class SomeClass {
private var observers = [ServiceDelegate]()
func addObserver<T: ServiceDelegate>(_ observer: T) {
observers.append(WeakReference(value: observer))
}
}
As you see, ServiceDelegate is a protocol, not a class type.
Even if all types which can conform to ServiceDelegate are class types, ServiceDelegate itself is not a class type. It is the fact of the pure Swift protocols currently.
Try #obc, Objective-C protocols are a bit different:
#objc protocol ServiceDelegate {
func doIt()
}
You may want to exclude Objective-C something and to make some pure Swift classes conform to ServiceDelegate, but I cannot find other ways around.
The problem is that WeakReference<ServiceDelegate> is wrong at line
private var observers = [WeakReference<ServiceDelegate>]()
You have to use a concrete class instead of protocol inside <>
You have two possible solutions:
Create a concrete class and use it:
class ServiceClass: ServiceDelegate {
//...
}
private var observers = [WeakReference<ServiceClass>]()
Or use a protocol. I mean this:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
protocol SomeContainer: AnyObject { }
extension WeakReference: SomeContainer { }
and use this way:
private var observers = [SomeContainer]()
Note
Using this way:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
You just move the problem to another part of the code.
I had similar problem and ended up keeping generic WeakReference, but removing type constraint:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map { $0 as AnyObject }
}
}
init(value: T?) {
self.value = value
}
}
This works for classes, Objective-C protocols and Swift protocols:
protocol P: class {}
#objc protocol Q {}
class Z: P, Q {}
var z = Z()
var rz = WeakReference<Z>(value: z)
var rp = WeakReference<P>(value: z)
var rq = WeakReference<Q>(value: z)
assert(rz.value === z)
assert(rp.value === z)
assert(rq.value === z)
z = Z()
assert(rz.value === nil)
assert(rp.value === nil)
assert(rq.value === nil)
Unfortunately it compiles for other things too:
protocol R {}
struct S: R {}
var rr = WeakReference<R>(value: S())
print("rr =", rr.value as Any) // nil
var rs = WeakReference<S>(value: S())
print("rs =", rs.value as Any) // nil
In Swift anything can be casted to AnyObject, but for value types that means boxing - new instance is allocated and immediately lost, so it always produces nil.
This can be used to implement an assertion that casting to AnyObject preserves identity:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map {
let asObject = $0 as AnyObject
assert(asObject === $0 as AnyObject)
return asObject
}
}
}
init(value: T?) {
self.value = value
}
}
Alternative approach would be to use https://github.com/wickwirew/Runtime to validate kind of T.self.
create plain protocol
public protocol AnyWeakValue {
var anyValue: Any? { get }
}
inherit associatedtype protocol from AnyWeakValue
public protocol WeakValue: AnyWeakValue {
associatedtype ValueType
var value: ValueType? { get }
}
extension WeakValue {
public var anyValue: Any? { return value }
}
create class Weak inherit WeakValue
open class Weak<Value: AnyObject>: WeakValue {
public init(value: Value?) { self.value = value }
open private(set) weak var value: Value?
}
using example
private var _delegates: [AnyWeakValue] = []
public var delegates: [SomeProtocol] {
return _delegates.compactMap({$0.anyValue as? SomeProtocol})
}
public func register<Delegate>(_ delegate: Delegate) where Delegate: SomeProtocol {
let weak: Weak<Delegate> = Weak.init(value: delegate)
_delegates.append(weak)
}
I am trying to implement generic for MVVM Swift. I have two base class, an protocol for generic class. The special in here is inheritance. I tried for three hour but I can't fix it :(.
protocol ObjectProtocol {
var id: Int { get set }
var name: String { get set }
}
class BaseViewModel<T: ObjectProtocol> {
var objects: [T] = []
init(){
}
}
protocol ListViewControllerType {
associatedtype T: ObjectProtocol
associatedtype ViewModelType: BaseViewModel<T>
var viewModel: ViewModelType! { get set }
func showError(error: String)
}
extension ListViewControllerType {
func showError(error: String) {
print(error)
}
}
class Consult: ObjectProtocol {
var id: Int = 1
var name: String = "Consult"
}
class ConsultViewModel<T: Consult>: BaseViewModel<Consult> {
}
class ConsultViewController: ListViewControllerType {
var viewModel: ConsultViewModel<Consult>!
}
But I get error in var viewModel: ConsultViewModel<Consult>!
This is error: Type 'ConsultViewController' does not conform to protocol 'ListViewControllerType'
Someone have experience with generic and inheritance can help me please.
Thank you so much.
I usually do like this:
class BaseViewModel {
}
class TemplateViewModel<T: ObjectProtocol>: BaseViewModel {
var objects: [T] = []
init(){
}
}
protocol AProtocol: BProtocol {
/// content to be shown on disclaimer Label of cell
var disclaimer: String {get set}
var cellDisclaimerAttributed: NSAttributedString {get}
var showSelection: Bool {get set}
var isReadMore: Bool {get}
}
I want to make variables optional so that I need not implement all variables every time after conforming protocol. Like in Objective-C we did for methods:
protocol AProtocol: BProtocol {
/// content to be shown on disclaimer Label of cell
optional var disclaimer: String {get set}
optional var cellDisclaimerAttributed: NSAttributedString {get}
optional var showSelection: Bool {get set}
optional var isReadMore: Bool {get}
}
Is it possible?
protocol TestProtocol {
var name : String {set get}
var age : Int {set get}
}
Provide a default extension for the protocol. Provide the default implementation for all the variables set and get which u want them to be optional.
In below protocol, name and age are optional.
extension TestProtocol {
var name: String {
get { return "Any default Name" } set {}
}
var age : Int { get{ return 23 } set{} }
}
Now if I am conforming above protocol to any other class, like
class TestViewController: UIViewController, TestProtocol{
var itemName: String = ""
**I can implement the name only, and my objective is achieved here, that the controller will not give a warning that "TestViewController does not conform to protocol TestProtocol"**
var name: String {
get {
return itemName ?? ""
} set {}
}
}
If you want to conform to Swift's documentation, you'd have to implement it like this :
#objc protocol Named {
// variables
var name: String { get }
#objc optional var age: Int { get }
// methods
func addTen(to number: Int) -> Int
#objc optional func addTwenty(to number: Int) -> Int
}
class Person: Named {
var name: String
init(name: String) {
self.name = name
}
func addTen(to number: Int) -> Int {
return number + 10
}
}
I have a protocol that looks like this:
protocol MyProtocol {
associatedtype SpeedType
var name: String {get set}
func forward(_: SpeedType)
}
I made 2 simple classes that conform to this protocol:
class A: MyProtocol {
typealias SpeedType = Double
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Double) {
print("Moving \(s) km/h")
}
}
class B: MyProtocol {
typealias SpeedType = Int
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Int) {
print("Moving \(s) km/h")
}
}
What I want to achieve is to be able to declare a variable of type MyProtocol, and initialize it later like so:
let x: Bool = true
var person: MyProtocol
if x {
person = A(name: "Robot")
} else {
person = B(name: "Human")
}
Before I made forward() method "generic" I was able to do this, however now I am getting the next error:
Protocol "MyProtocol" can only be used as generic constraint because it has Self or associated type requirement.
So my goal is to have a method forward() that can take as an argument parameter of a type that I specify, and also be able to declare a variable of a type that conforms to my protocol.
Swift doesn't allow this.
Here's why: you don't know anything about the type of argument person.forward(_:) takes. There is no way to call it. MyProtocol essentially defines an open-ended set of independent types.
If you don't want to be able to call person.forward(_:), and you just want to be able to access the non-generic person.name property, then split your protocol into a base, non-generic protocol defining name, and a sub-protocol that adds the generic forward(_:) method.
protocol NamedThing {
var name: String {get set}
}
protocol MovableNamedThing: NamedThing {
associatedtype SpeedType
func forward(_: SpeedType)
}
class A: MovableNamedThing {
typealias SpeedType = Double
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Double) {
print("Moving \(s) km/h")
}
}
class B: MovableNamedThing {
typealias SpeedType = Int
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Int) {
print("Moving \(s) km/h")
}
}
let x: Bool = true
var person: NamedThing
if x {
person = A(name: "Robot")
} else {
person = B(name: "Human")
}