I am trying to provide a default value for a variable in a Protocol. I am getting an error:
Type ViewController does not conform to protocol Test
Code:
protocol Test {
var aValue: CGFloat { get set }
}
extension Test {
var aValue: CGFloat {
return 0.3
}
}
class ViewController: UIViewController, Test {
override func viewDidLoad() {
super.viewDidLoad()
print("value \(aValue)")
}
}
How can I provide a default value so the ViewController can use the default value (in the protocol extension) without to declaring it?
protocol Test {
var aValue: CGFloat { get set }
}
extension Test {
var aValue: CGFloat {
get {
return 0.3
}
set {
print("the new value is \(newValue)")
}
}
}
class Default: Test {
init() {
print("value \(aValue)")
}
}
class ViewController: Test {
var aValue: CGFloat {
get {
return 0.4
}
set {
print("i am overriding the setter")
}
}
init() {
print("value \(aValue)")
}
}
var d = Default() // value 0.3
d.aValue = 1 // the new value is 1.0
var vc = ViewController() // value 0.4
vc.aValue = 1 // i am overriding the setter
Since you have a protocol extension, you don't have to implement neither the getter nor the setter if you don't want to.
https://docs.swift.org/swift-book/LanguageGuide/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259
In addition to stored properties, classes, structures, and
enumerations can define computed properties, which do not actually
store a value. Instead, they provide a getter and an optional setter
to retrieve and set other properties and values indirectly.
You can't set the value of the same variable in the setter itself.
protocol Test {
var aValue: CGFloat {get set}
}
extension Test {
// var aValue: CGFloat {
// return 0.3
// }
var aValue: CGFloat {
get {
return 0.3
}
set {
debugPrint("new value is \(aValue)")
}
}
}
struct TestClass: Test {
func printData() {
debugPrint(aValue)
}
}
let aTestClass = TestClass()
aTestClass.printData()
Related
I want to initialize the struct by receiving the "struct type"(?) by a specific logic as below.
As I abstracted the return value of the struct into MyProtocol had a declaration of init(), which seems a little awkward.
I'm not sure I can do this.
I'd like to get an undecided struct type returned, what should I do?
Is this for the best?
For your information, Opaque Type is not available because it needs to support iOS 13 or earlier.
protocol MyProtocol {
init() // Is this for the best?
}
struct AAA: MyProtocol {
var numberAAA: Int // Sample variable.
init() {
print("init AAA")
numberAAA = 100
}
}
struct BBB: MyProtocol {
var numberBBB: Int // Sample variable.
init() {
print("init BBB")
numberBBB = 200
}
}
class MyClass {
func mainLogic() {
let myStruct = randomStruct()
myStruct.init() // This is the reason init () declared in the MyProtocol.
}
func randomStruct() -> MyProtocol.Type {
if Bool.random() {
return AAA.self
} else {
return BBB.self
}
}
}
init() as a protocol requirement seems odd. No one is stopping you to do this and compiler should allow this, however I would consider making protocol based on some other requirement rather than just init().
Here's an attempt to do so -
protocol NumberOperation {
var number: Int { get set }
mutating func perform()
}
struct Incrementer: NumberOperation {
var number: Int
mutating func perform() {
number += 1
}
}
struct Decrementer: NumberOperation {
var number: Int
mutating func perform() {
number -= 1
}
}
struct Record<O: NumberOperation> {
public var operation: O
mutating func perform() {
operation.perform()
}
}
class MyClass {
func mainLogic() {
var record = getRecord(type: Incrementer.self)
record.perform()
}
func getRecord<O: NumberOperation>(type: O.Type) -> Record<O> {
if type == Incrementer.self {
return Record(operation: Incrementer(number: 1) as! O)
}
return Record(operation: Decrementer(number: 10) as! O)
}
}
This introduces a container type Record that holds/wraps our type based on the same protocol conformation. This does the same what you were doing, probably is easier to read/understand.
From within a property wrapper in Swift, can you someone refer back to the instance of the class or struck that owns the property being wrapped? Using self doesn't obviously work, nor does super.
I tried to pass in self to the property wrapper's init() but that doesn't work either because self on Configuration is not yet defined when #propertywrapper is evaluated.
My use case is in a class for managing a large number of settings or configurations. If any property is changed, I just want to notify interested parties that something changed. They don't really need to know which value just, so use something like KVO or a Publisher for each property isn't really necessary.
A property wrapper looks ideal, but I can't figure out how to pass in some sort of reference to the owning instance that the wrapper can call back to.
References:
SE-0258
enum PropertyIdentifier {
case backgroundColor
case textColor
}
#propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
#Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
#Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...). At least this way you can annotate your types and only add a single function call in init().
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
#propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: #escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue to assign self to.
You could then use that to trigger some action after setting the wrappedValue.
As an example:
import Foundation
#propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
#Wrapper("a") var a : Int
#Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
#propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
#Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
The answer is yes! See this answer
Example code for calling ObservableObject publisher with a UserDefaults wrapper:
import Combine
import Foundation
class LocalSettings: ObservableObject {
static var shared = LocalSettings()
#Setting(key: "TabSelection")
var tabSelection: Int = 0
}
#propertyWrapper
struct Setting<T> {
private let key: String
private let defaultValue: T
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Setting<T>>
) -> T {
get {
return object[keyPath: storageKeyPath].wrappedValue
}
set {
(object.objectWillChange as? ObservableObjectPublisher)?.send()
UserDefaults.standard.set(newValue, forKey: object[keyPath: storageKeyPath].key)
}
}
}
I am new to Swift and am trying to follow the Stanford Uni course CS193P.
I did originally type the code in but got multiple errors, so as I could not find a soluion on the forums, I copied the code in from the Democode provided.
Now I get errors, but not the same as before.
Here is the file that has the errors:
//
//ViewController.swift
// FaceIt
//
// Created by CS193p Instructor.
// Copyright © 2017 Stanford University. All rights reserved.
//
import UIKit
class ViewController: UIViewController
{
var expression = FacialExpression(eyes: .closed, mouth: .frown) {
didSet {
updateUI()
}
}
private func updateUI()
{
switch expression.eyes {
case .open:
FaceView?.eyesOpen = true
case .closed:
FaceView?.eyesOpen = false
case .squinting:
FaceView?.eyesOpen = false
}
FaceView?.mouthCurvature = mouthCurvatures[expression.mouth] ?? 0.0
}
private let mouthCurvatures =
[FacialExpression.Mouth.grin:0.5,.frown:-1.0,.smile:1.0,.neutral:0.0,.smirk:-0.5]
}
I can't understand why it says "Type 'FaceView?'has no member 'eyesOpen'
as in the file FaceView it contains the code:
class FaceView: UIView
{
// Public API
// 1.0 is full smile and -1.0 is full frown
#IBInspectable
var mouthCurvature: Double = 0.5 { didSet { setNeedsDisplay() } }
#IBInspectable
var eyesOpen: Bool = true { didSet { setNeedsDisplay() } }
#IBInspectable
var scale: CGFloat = 0.9 { didSet { setNeedsDisplay() } }
#IBInspectable
var lineWidth: CGFloat = 5.0 { didSet { setNeedsDisplay() } }
#IBInspectable
var color: UIColor = UIColor.blue { didSet { setNeedsDisplay() } }
func changeScale(byReactingTo pinchRecognizer: UIPinchGestureRecognizer)
{
I also get the same type of error for 'mouthCurvature'.
The only doubt I have is the type of file that I set up FacialExpression, as in the lecture the lecturer dragged it inform somewhere. I set it up as a Cocoa Touch file. Is this why I get the error?
Otherwise can anybody explain why I am getting this error?
Your eyesOpen property is an instance property—it exists on an instance of the FaceView type, not on the FaceView type itself. You should probably declare a property of type FaceView?, and then refer to that instead of the class name (the time to use the class name is when you are accessing a member that is declared class var/let/func or static var/let/func, and thus belongs to the type itself rather than to a specific instance of the type). So, do something like:
class ViewController: UIViewController {
var faceView: FaceView?
// add some code somewhere, maybe in viewDidLoad, to assign something to faceView
private func updateUI() {
switch self.expression.eyes {
case .open:
self.faceView?.eyesOpen = true
// ... etc ...
}
}
protocol FooType {
var num:Int { get set }
}
class Foo: FooType {
var num: Int = 0 {
didSet {
print("Did set num")
}
}
}
class Bar {
var foo: FooType = Foo() {
didSet {
print("Did set Foo")
}
}
func changeNum(num:Int) {
foo.num = num
}
}
let bar = Bar()
bar.changeNum(5)
Did set num
Did set Foo
In this example, setting a property on foo causes Bar's didSet for foo to get called.
I would expect only Foo's num didSet to get called.
If I remove the protocol constraint of Bar's foo property to FooType, it behaves as I expected.
protocol FooType {
var num:Int { get set }
}
class Foo: FooType {
var num: Int = 0 {
didSet {
print("Did set num")
}
}
}
class Bar {
var foo = Foo() {
didSet {
print("Did set Foo")
}
}
func changeNum(num:Int) {
foo.num = num
}
}
let bar = Bar()
bar.changeNum(5)
Did set num
If I keep the conformance to FooType, but add a class constraint (FooType: class), it also behaves as expected.
protocol FooType: class {
var num:Int { get set }
}
class Foo: FooType {
var num: Int = 0 {
didSet {
print("Did set num")
}
}
}
class Bar {
var foo: FooType = Foo() {
didSet {
print("Did set Foo")
}
}
func changeNum(num:Int) {
foo.num = num
}
}
let bar = Bar()
bar.changeNum(5)
Did set num
If I remove the protocol completely and make Foo a struct rather than a class, we're back to both setters being called.
struct Foo {
var num: Int = 0 {
didSet {
print("Did set num")
}
}
}
class Bar {
var foo = Foo() {
didSet {
print("Did set Foo")
}
}
func changeNum(num:Int) {
foo.num = num
}
}
let bar = Bar()
bar.changeNum(5)
Did set num
Did set Foo
In the case changing Foo to a struct, I can see why it's happening... it mentions it in the swift documentation because a struct is a value type, it makes a copy, etc. (although I didn't expect it at first).
But the other cases I don't get...
you can solve this by conformance FooType to AnyObject
protocol FooType: AnyObject {
var num:Int { get set }
}
I can not figure out why Xcode playground is crashing, here is my basic setup
protocol Shootable {
func shoot()
}
class BaseMissile: Shootable {
var damage = 0
func shoot() {
println("Pew pew for \(damage) damage")
}
}
class Missile1: BaseMissile {
override init() {
super.init()
damage = 1
}
}
protocol Targetable {
var life: Int {get set}
}
class BaseSpaceship: Targetable {
var life = 0
var currentMissile: BaseMissile!
func printHealth() {
println("My current life: \(life)")
}
}
class Spaceship1: BaseSpaceship {
override init() {
super.init()
life = 1
currentMissile = Missile1()
}
}
var spaceship = Spaceship1()
spaceship.printHealth()
spaceship.currentMissile.shoot()
specifically the last line spaceship.currentMissile.shot() crashes the playground. If I move var currentMissile: BaseMissile! from BaseSpaceship to Spaceship1 it works, but is that an acceptable way to handle this?
I guess this happens because BaseSpaceship class has no initializers and var currentMissile: BaseMissile! cant be nil. So you either should do it optional by replacing ! with ? or give it a default value.