My question is how to deal with inheritance of property methods such as set, get, didSet, willSet, ...?
Let's take an example: I want to override the setter method of a property in a Swift class. Here is what I want to achieve (which is obviously not working):
class A {
var value: Int {get {...} set {...} }
}
class B: A {
var value: Int {
set(newValue) {
// do some fancy stuff...
}
}
}
This is not working, too:
// in class B
override func setValue(newValue: Int) {
// do some fancy stuff...
}
We can do in Swift something like this:
class A {
var _value: Int = 0
var value: Int {
get {
return _value
}
set {
_value = newValue
}
}
}
class B: A {
override var value: Int {
get {
return _value
}
set {
_value = newValue + 1
}
}
}
let a = A()
a.value = 1
print(a.value) // => 1
let b = B()
b.value = 1
print(b.value) // => 2
This approach is not very elegant because I have to implement also the getter methods, which is actually not necessary, because only the setter should be overridden.
You could do:
class A {
var value: Int = 0
}
class B: A {
override var value: Int {
get { return super.value }
set {
// Something fancy...
super.value = newValue
}
}
}
While you still have to implement a getter in B, you at least don't need _value, which helps keeps class A clean.
Unfortunately your only way to do this would be to create a separate get and set method if you really wanted to do this.
class A {
private var value: Int = 0
func getValue() -> Int { return value }
func setValue(newValue : Int) {
value = newValue
}
}
By the nature of using computed properties you are basically doing a "shortcut" in some ways over making two separate methods - and as thus you would have to override both the set and get if you wanted to change things.
Depending on your usage an override of didSet could do the trick (based on your last example):
class B: A {
override var value: Int {
didSet {
_value++
}
}
}
Related
I am trying to create a wrapper for my API return wrapper class for my project.
these are my classes
class Wrapper<T> {
let message = "Hello World"
let wrapped = T.self
public func getData() -> T.Type {
return wrapped
}
}
class Object {
let number = 100
public func getNumber() -> Int {
return number
}
}
class SecondObject {
let name = "Second Object"
public func getName() -> String {
return name
}
}
What I want to achieve is, is there any way I can call the Object function like this
let example = Wrapper<Object>()
example.getData().getNumber() // <<-- This is not working
let secondExample = Wrapper<SecondObject>()
secondExample.getData().getName() // <<-- This is not working
The error in my playground is this
error: instance member 'getNumber' cannot be used on type 'Object'
If you notice the Wrapper class, there is message property which will be used for all my API return object model
So my goal is, I could simply call the Wrapper class together with my object model class and just call the function that is inside the object model class.
I am still learning about generic in swift. What am I missing here?
You don't set wrapped to anything useful. You ned to set it to an instance of T. So you can pass a Tinto the constructor
class Wrapper<T>
{
let wrapped: T
init(wrapped: T)
{
self.wrapped = wrapped
}
}
Or you can have the class construct an instance of T, but if you want to do that, you need to tell it how to construct the instance. For example:
class Wrapper<T>
{
let wrapped: T
init()
{
self.wrapped = T() // << error!
}
}
won't work because the compiler knows nothing about T, not even if it has an init. You can change that with a protocol
protocol Initable
{
init()
}
class Wrapper<T: Initable>
{
let wrapped: T
init()
{
self.wrapped = T()
}
}
And you can apply the protocol to any type you like with an extension. In most cases the extension can be empty because mot types already have an init() method. For example:
class MyClass
{
init() { /* do stuff */ }
}
extension MyClass: Initable {}
class MyOtherClass
{
init(number: Int) { /* do stuff */ }
}
extension MyOtherClass: Initable
{
init() { self.init(number: 0) }
}
Another option is to supply a closure to the wrapper's init.
class Wrapper<T>
{
let wrapped: T
init(factory: ()-> T)
{
self.wrapped = factory()
}
}
let w = Wrapper() { return Array<Int>() }
Normally you'd only do this if you wanted to create multiple instances i.e. you'd keep a reference to the closure and call it each time you needed a new instance.
class Wrapper<T> {
private var wrapped: T // Storing your object of T type
init(value: T) { // init with instance of T
wrapped = value
}
public func getData() -> T { //returning instance of T
return wrapped
}
}
class Object {
let number = 100
public func getNumber() -> Int {
return number
}
}
let o = Object()
let example = Wrapper(value: o) // Here we creating instance of Wrapper with instance of Object
example.getData().getNumber()
How about this , in your example changing the type of wrapped from non-optional to an optional variable type.
class Wrapper {
let message = "Hello World"
var wrapped : T?
public func getData() -> T? {
return wrapped
}
}
class Object {
let number = 100
public func getNumber() -> Int {
return number
}
}
class SecondObject {
let name = "Second Object"
public func getName() -> String {
return name
}
}
and then using it as below
let example = Wrapper()
example.wrapped = Object()
let result1 = example.getData()?.getNumber() // ()
secondExample.wrapped = SecondObject()
let result2 = secondExample.getData()?.getName()
if let val1 = result1 , let val2 = result2 {
print("result1 = \(val1) result2 = \(val2)" )
}
I want to "make" a number of class instances similar to a base class but different in underlying type. (Not quite the same as the typical "Animal" class factory examples seen all over the net!)
The code below is close to working but it requires the user to "upcast" the make result, as in:
var f1 = FOO.make(version: FOO.Ver.f1) as! FOO1_32
I do not want the user to know about the specific class type other than it is a FOO. I have seen other proposals and they all indicate that the solution is to define the make with a generic type that conforms to the protocol as in:
make<T: FOOProtocol>(version: Ver = .f1) -> T
However this gives me the error "generic parameter 'T' could not be inferred" on the call to FOO.make(version: FOO.Ver.f1)
Anyone know how to do this? My Playground code follows.
protocol FOOProtocol
{
associatedtype FOOtype
var value: FOOtype {get set}
}
class FOO
{
enum Ver
{
case f1
case f2
}
class func make(version: Ver = .f1) -> FOO
{
print("FOO make")
switch version
{
case .f1:
return FOO1_32()
case .f2:
return FOO2_64()
}
}
}
class FOO1_32: FOO, FOOProtocol
{
typealias FOOtype = UInt32
private var fooVal: UInt32 = 0
var value: UInt32
{
get { return self.fooVal }
set { self.fooVal = newValue }
}
override init()
{
print("FOO1_32 init")
self.fooVal = 132
}
}
class FOO2_64: FOO, FOOProtocol
{
typealias FOOtype = UInt64
private var fooVal: UInt64 = 0
var value: UInt64
{
get { return self.fooVal }
set { self.fooVal = newValue }
}
override init()
{
print("FOO2_64 init")
self.fooVal = 264
}
}
var f1 = FOO.make(version: FOO.Ver.f1) // requires: as! FOO1_32
let f1v = f1.value
print("\(f1v)")
var f2 = FOO.make(version: FOO.Ver.f2) // requires: as! FOO2_64
let f2v = f2.value
print("\(f2v)")
I've created an abstract base class-like structure in Swift, using protocol extensions, as per this answer. This is a simplified example:
protocol AbstractBase {
var _constant: Int { get }
func _operation(_ val: Int) -> Int
}
public class ConcreteSub: AbstractBase {
let _constant: Int = 42
func _operation(_ val: Int) -> Int {
return val + 2
}
}
extension AbstractBase {
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
So basically, ConcreteSub provides the implementation details needed by AbstractBase, namely _constant and _operation.
I would like to hide those details from clients, and only expose mainOperation. However, Swift does not allow me to make the members fileprivate on the protocol -- if I do the following
protocol AbstractBase {
fileprivate var _constant: Int { get }
// etc
I get "error: 'fileprivate' modifier cannot be used in protocols".
Nor can I apply the modifier on the subclass -- when I try
public class ConcreteSub: AbstractBase {
fileprivate let _constant: Int = 42
// etc
I get "error: property '_constant' must be declared internal because it matches a requirement in internal protocol 'AbstractBase'".
Lastly, when I make the whole protocol fileprivate, I get no compile errors, but I consistently run into Linking errors, which I guess is because the protocol is private, but the subclass is public.
Is there another way I'm missing?
When I need an abstract base with some properties/functions hidden I use class with some additional fatalErrors and asserts to crash whenever someone is trying to use Base instead of implementation.
public class AbstractBase {
init() {
assert(type(of: self) != AbstractBase.self, "Abstract class")
}
fileprivate var _constant: Int {
fatalError("Abstract class")
}
fileprivate func _operation(_ val: Int) -> Int {
fatalError("Abstract class")
}
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
public class ConcreteSub: AbstractBase {
fileprivate override var _constant: Int {
return 42
}
fileprivate override func _operation(_ val: Int) -> Int {
return val + 2
}
}
I actually just ran into this issue. As of Swift 5.1, you can do this instead:
protocol MyProtocol {
var someVisibleVar: String { get }
func someVisibleFunc()
}
fileprivate extension MyProtocol {
var someFilePrivateVar: String {
"whatever"
}
func someFilePrivateFunc() {
print("someFilePrivateFunc() was called with \(someVisibleVar)")
}
}
class SomeClass: MyProtocol {
var someVisibleVar: String { "whatever" }
func someVisibleFunc() {
if someFilePrivateVar == someVisibleVar {
someFilePrivateFunc()
}
}
}
class SomeOtherClass: MyProtocol {
var someVisibleVar: String { "something else" }
func someVisibleFunc() {
if someFilePrivateVar == someVisibleVar {
someFilePrivateFunc()
}
}
}
I want to write the following in Swift:
class A
{
class let x = 0
func print_class_property()
{
print(type(of: self).x)
}
}
class B:A
{
overriding class let x = 1
}
class C:A
{
overriding class let x = 5
}
A().print_class_property() // 0
B().print_class_property() // 1
C().print_class_property() // 5
But of course, this doesn’t compile.
Instead, I can demote the class property to a variable instance property that’s overwritten in the subclass initializers, but this allocates storage for x in every instance of A, B, or C. In addition you lose the guarantee that x never changes across the lifetime of the object.
How do I store constants at the class level, that can be shared by all instances of a subclass?
Unfortunately Swift (as for now) cannot have class stored properties that can be overriden. But can have class computed properties, which are overridable.
You can write something like this:
class A
{
class var x: Int {
return 0
}
func print_class_property()
{
print(type(of: self).x)
}
}
class B:A
{
override class var x: Int {
return 1
}
}
class C:A
{
override class var x: Int {
return 5
}
}
A().print_class_property() //->0
B().print_class_property() //->1
C().print_class_property() //->5
ADDITION
If you do not desire such re-evaluation as noted in comments, you may need to have some another static property.
For example:
class A
{
class var x: SomeLargeObject {
struct My {
static let obj = SomeLargeObject("abc", 0)
}
return My.obj
}
func print_class_property()
{
print(type(of: self).x)
}
}
class B:A
{
override class var x: SomeLargeObject {
struct My {
static let obj = SomeLargeObject("def", 1)
}
return My.obj
}
}
class C:A
{
override class var x: SomeLargeObject {
struct My {
static let obj = SomeLargeObject("ghi", 5)
}
return My.obj
}
}
I want to implement a function like this one:
protocol Base {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
objects.filter({ $0 is type }).forEach { // <<< ERROR IS HERE
var object = $0
object.value = value
}
}
updatePropertyForType(ObjectTypeB.self, value: 10)
But there is an error:
'type' is not a type
Please, help me fix it.
See this answer:
protocol Base: AnyObject {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
objects.filter({let item = $0; return type === item.dynamicType }).forEach {
$0.value = value
}
}
call it with:
updatePropertyForType(ObjectTypeA.self, value: 3)
As the other answers have eluded to, you cannot use meta-types with is. However, a nicer solution would be to simply use generics. This will allow Swift to infer the type you pass into the function, allowing you to write it as:
protocol Base : class {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updateElements<T:Base>(ofType type: T.Type, withValue value: Int) {
objects.filter{ $0 is T }.forEach{ $0.value = value }
}
updateElements(ofType: ObjectTypeB.self, withValue: 10)
You'll also want to make your Base protocol class bound (: class) in order to allow Swift to treat Base typed instances as reference types (allowing you to do $0.value = value).
Your previous code of:
var object = $0
object.value = value
would have worked for reference types, but not for value types – as object here is simply a copy of $0, so any mutations won't be reflected in the array. Therefore you should annotate your protocol to make it clear that your protocol isn't designed to be used with value types.
If you do want to be able to handle value types, you may want to consider using map instead:
func updateElements<T:Base>(ofType type: T.Type, withValue value: Int) {
objects = objects.map {
var object = $0
if object is T {
object.value = value
}
return object
}
}
But this code works just fine.
protocol Base {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
objects.filter({ object in
let result = object.dynamicType == type
return result
}).forEach {
var object = $0
object.value = value
}
}
updatePropertyForType(ObjectTypeB.self, value: 10)
print(objects)