Computed property not updating with mutating function values - ios

I seem to have a very perplexing Swift problem.
I have a struct called "Graph" which has a number of mutating functions that change its stored properties. The struct's initializer reads from a text file and saves the data in various stored properties.
struct Graph {
var dists: [[Int]]
.
.
.
func tourValue() -> Int {
return dists.count
}
mutating func swapHeuristic() {
dists = [[0], [1]]
}
mutating func twoOpt() {
dists = [[1]]
}
.
.
.
}
I also have a function makeFile() which creates a text file for the graph to read from.
The main issues lie with my ContentView.swift file, where I create and use instances of Graph.
ContentView.swift
import SwiftUI
struct ContentView: View {
var nodes: Int
var graph: Graph { // Graph object is initialized and stored perfectly
set {
makeFile(nodes: nodes, selection: .Euclidean) // this works perfectly
}
get {
Graph(flag: -1, filename: "\(nodes)nodesEUCLIDEAN.txt") // this works perfectly
}
}
var originalTour: Double {
graph.tourValue() // saves the original tour value perfectly
}
var swapValue: Double {
set {
graph.swapHeuristic() // does not change Graph's dists property like it should
}
get {
return graph.tourValue() // as a result of dists not changing, the tourValue is also unchanged
}
}
var body: some View {
Text("Hello, World!")
}
}
Thus, the initialized graph instance never has its property changed, and I can't derive values from it.
How do I solve this?

Your graph variable is computed, so unless its mutated form is reflected somewhere else (other objects/properties, a file, etc) the mutation is inevitably lost.
When you set swapValue = 0, first the getter for var graph is called, creating a new Graph instance. Then that instance executes swapHeuristic(). Since that function mutates the value, it triggers the setter for var graph. The serious red flag here is that your setter ignores newValue. newValue in the setter is at that point the only piece of your code which has the mutated form of your graph. But it does nothing with that value, so when the setter finishes it's simply deallocated.

The question is already answered, but in attempt to be helpful, let me come at the same point from a slightly different direction.
The line
var graph: Graph { // Graph object is initialized and stored perfectly
is a form of self delusion. There is no Graph object that is initialized and stored. This is, as you've been told, a computed property. So consider it in full:
var graph: Graph { // Graph object is initialized and stored perfectly
set {
makeFile(nodes: nodes, selection: .Euclidean) // this works perfectly
}
get {
Graph(flag: -1, filename: "\(nodes)nodesEUCLIDEAN.txt") // this works perfectly
}
}
What we see in the get part (the getter) is not the retrieval of a stored Graph. It is the creation of a new graph. Every single time we ask for graph, a completely new and different Graph is created at that moment and passed out to the caller.
Thus there is no existing object to mutate. The line graph.swapHeuristic() causes a completely new Graph to come into existence, calls the method, and throws that Graph away.

Related

Updating a #State var in SwiftUI from an async method doesn't work

I have the following View:
struct ContentView: View {
#State var data = [SomeClass]()
var body: some View {
List(data, id: \.self) { item in
Text(item.someText)
}
}
func fetchDataSync() {
Task.detached {
await fetchData()
}
}
#MainActor
func fetchData() async {
let data = await SomeService.getAll()
self.data = data
print(data.first?.someProperty)
// > Optional(115)
print(self.data.first?.someProperty)
// > Optional(101)
}
}
now the method fetchDataSync is a delegate that gets called in a sync context whenever there is new data. I've noticed that the views don't change so I've added the printouts. You can see the printed values, which differ. How is this possible? I'm in a MainActor, and I even tried detaching the task. Didn't help. Is this a bug?
It should be mentioned that the objects returned by getAll are created inside that method and not given to any other part of the code. Since they are class objects, the value might be changed from elsewhere, but if so both references should still be the same and not produce different output.
My theory is that for some reason the state just stays unchanged. Am I doing something wrong?
Okay, wow, luckily I ran into the Duplicate keys of type SomeClass were found in a Dictionary crash. That lead me to realize that SwiftUI is doing some fancy diffing stuff, and using the == operator of my class.
The operator wasn't used for actual equality in my code, but rather for just comparing a single field that I used in a NavigationStack. Lesson learned. Don't ever implement == if it doesn't signify true equality or you might run into really odd bugs later.

What is the reason behind objc_sync_enter doesn't work well with struct, but works well with class?

I have the following demo code.
struct IdGenerator {
private var lastId: Int64
private var set: Set<Int64> = []
init() {
self.lastId = 0
}
mutating func nextId() -> Int64 {
objc_sync_enter(self)
defer {
objc_sync_exit(self)
}
repeat {
lastId = lastId + 1
} while set.contains(lastId)
precondition(lastId > 0)
let (inserted, _) = set.insert(lastId)
precondition(inserted)
return lastId
}
}
var idGenerator = IdGenerator()
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func click(_ sender: Any) {
DispatchQueue.global(qos: .userInitiated).async {
for i in 1...10000 {
let id = idGenerator.nextId()
print("i : \(id)")
}
}
DispatchQueue.global(qos: .userInitiated).async {
for j in 1...10000 {
let id = idGenerator.nextId()
print("j : \(id)")
}
}
}
}
Whenever I execute click, I would get the following crash
Thread 5 Queue : com.apple.root.user-initiated-qos (concurrent)
#0 0x000000018f58b434 in _NativeSet.insertNew(_:at:isUnique:) ()
#1 0x000000018f598d10 in Set._Variant.insert(_:) ()
#2 0x00000001001fe31c in IdGenerator.nextId() at /Users/yccheok/Desktop/xxxx/xxxx/ViewController.swift:30
#3 0x00000001001fed8c in closure #2 in ViewController.click(_:) at /Users/yccheok/Desktop/xxxx/xxxx/ViewController.swift:56
It isn't clear why the crash happen. My raw guess is, under struct, objc_sync_enter(self) doesn't work as expected. 2 threads accessing Set simultaneously will cause such an issue.
If I change the struct to class, everything just work fine.
class IdGenerator {
private var lastId: Int64
private var set: Set<Int64> = []
init() {
self.lastId = 0
}
func nextId() -> Int64 {
objc_sync_enter(self)
defer {
objc_sync_exit(self)
}
repeat {
lastId = lastId + 1
} while set.contains(lastId)
precondition(lastId > 0)
let (inserted, _) = set.insert(lastId)
precondition(inserted)
return lastId
}
}
May I know what is the reason behind? Why the above objc_sync_enter works well in class, but not in struct?
The objc_sync_enter/objc_sync_exit functions take an object instance and use its identity (i.e., address in memory) in order to allocate and associate a lock in memory — and use that lock to protect the code between the enter and exit calls.
However, structs are not objects, and don't have reference semantics which would allow them to be used in this way — they're not even guaranteed to be allocated in a stable location in memory. However, to support interoperation with Objective-C, structs must have a consistent object-like representation when used from Objective-C, or else calling Objective-C code with, say, a struct inside of an Any instance could trigger undefined behavior.
When a struct is passed to Objective-C in the guise of an object (e.g., inside of Any, or AnyObject), it is wrapped up in a temporary object of a private class type called __SwiftValue. This allows it to look like an object to Objective-C, and in some cases, be used like an object, but critically, it is not a long-lived, stable object.
You can see this with the following code:
struct Foo {}
let f = Foo()
print(f) // => Foo()
print(f as AnyObject) // => __SwiftValue
print(ObjectIdentifier(f as AnyObject)) // => ObjectIdentifier(0x0000600002595900)
print(ObjectIdentifier(f as AnyObject)) // => ObjectIdentifier(0x0000600002595c60)
The pointers will change run to run, but you can see that every time f is accessed as an AnyObject, it will have a new address.
This means that when you call objc_sync_enter on a struct, a new __SwiftValue object will be created to wrap your struct, and that object is passed in to objc_sync_enter. objc_sync_enter will then associate a new lock with the temporary object value which was automatically created for you... and then that object is immediately deallocated. This means two major things:
When you call objc_sync_exit, a new object will be created and passed in, but the runtime has no lock associated with that new object instance! It may crash at this point.
Every time you call objc_sync_enter, you're creating a new, separate lock... which means that there's effectively no synchronization at all: every thread is getting a new lock altogether.
This new pointer instance isn't guaranteed — depending on optimization, the object may live long enough to be reused across objc_sync_* calls, or a new object could be allocated exactly in the same place as an old one... or a new object could be allocated where a different struct used to be, and you accidentally unlock a different thread...
All of this means that you should definitely avoid using objc_sync_enter/objc_sync_exit as a locking mechanism from Swift, and switch over to something like NSLock, an allocated os_unfair_lock, or even a DispatchQueue, which are well-supported from Swift. (Really, the objc_sync_* functions are primitives for use largely by the Obj-C runtime only, and probably should be un-exposed to Swift.)

Binding model and view: how to observe object properties

I have a view structured like a form that creates a model object. I am trying to bind the form elements (UIControl) to the model properties, so that the views auto-update when their corresponding model property is changed, and the model update when the controls are changed (two way binding). The model can change without the view knowing because multiple views can be linked to one same model property.
Approach 1: Plain Swift
My problem is the following: to observe changes to the model properties, I tried to use KVO in Swift, and specifically the observe(_:changeHandler:) method.
class Binding<View: NSObject, Object: NSObject, ValueType> {
weak var object: Object?
weak var view: View?
var objectToViewObservation: NSKeyValueObservation?
var viewToObjectObservation: NSKeyValueObservation?
private var objectKeyPath: WritableKeyPath<Object, ValueType>
private var viewKeyPath: WritableKeyPath<View, ValueType>
init(betweenObject objectKeyPath: WritableKeyPath<Object, ValueType>,
andView viewKeyPath: WritableKeyPath<View, ValueType>) {
self.objectKeyPath = objectKeyPath
self.viewKeyPath = viewKeyPath
}
override func bind(_ object: Object, with view: View) {
super.bind(object, with: view)
self.object = object
self.view = view
// initial value from object to view
self.view![keyPath: viewKeyPath] = self.object![keyPath: objectKeyPath]
// object --> view
objectToViewObservation = object.observe(objectKeyPath) { _, change in
guard var view = self.view else {
// view doesn't exist anymore
self.objectToViewObservation = nil
return
}
guard let value = change.newValue else { return }
view[keyPath: self.viewKeyPath] = value
}
// view --> object
viewToObjectObservation = view.observe(viewKeyPath) { _, change in
guard var object = self.object else {
// object doesn't exist anymore
self.viewToObjectObservation = nil
return
}
guard let value = change.newValue else { return }
object[keyPath: self.objectKeyPath] = value
}
}
}
However some of the properties of my model have types CustomEnum, CustomClass, Bool?, and ClosedRange<Int>, and to use observe I had to mark them as #objc dynamic, which yielded the error:
Property cannot be marked #objc because its type cannot be represented in Objective-C
Approach 2: Using RxSwift rx.observe
I turned to RxSwift and the rx.observe method thinking I could work around this problem, but the same thing happened (at runtime this time).
// In some generic bridge class between the view and the model
func bind(to object: SomeObjectType) {
object.rx
.observe(SomeType.self, "someProperty")
.flatMap { Observable.from(optional: $0) }
.bind(to: self.controlProperty)
.disposed(by: disposeBag)
}
Approach 3: Using RxSwift BehaviorRelays?
This is my first experience with RxSwift, and I know I should be using BehaviorRelay for my model, however I don't want to change all my model properties as my model object is working with other framework. I could try to implement a bridge then, to transform model properties into BehaviorRelay, but I would come across the same problem: how to listen for model changes.
In this question, there were no answer as to how to listen for property changes without refactoring all model properties to RxSwift's Variable (currently deprecated).
Approach 4: Using didSet Swift property observer?
The didSet and willSet property observers in plain Swift would allow listening for changes, however this would require to mark all the properties in the model with these observers, which I find quite inconvenient, since my model object has a lot of properties. If there is a way to add these observers at runtime, this would solve my problem.
I believe that what I am trying to achieve is quite common, having a set of views that modify a model object, however I can't find a way to properly link the model to the view, so that both auto-update when needed.
Basically, I'm looking for an answer to one of the following questions:
Is there something I overlooked, is there a better way to achieve what I want?
or How to overcome the "Property cannot be marked #objc" problem?
or How to bridge my model object to BehaviorRelay without changing my model?
or How to add didSet observers at runtime?
You said:
I believe that what I am trying to achieve is quite common, having a set of views that modify a model object, however I can't find a way to properly link the model to the view, so that both auto-update when needed.
Actually it's not at all common. One idea you don't mention is to wrap your entire model into a Behavior Relay. Then the set of views can modify your model object.
Each of your views, in turn, can observe the model in the behavior relay and update accordingly. This is the basis of, for example, the Redux pattern.
You could also use your approach #3 and use property wrappers to make the code a bit cleaner:
#propertyWrapper
struct RxPublished<Value> {
private let relay: BehaviorRelay<Value>
public init(wrappedValue: Value) {
self.relay = BehaviorRelay(value: wrappedValue)
}
var wrappedValue: Value {
get { relay.value }
set { relay.accept(newValue) }
}
var projectedValue: Observable<Value> {
relay.asObservable()
}
}
But understand that the whole reason you are having this problem is not due to Rx itself, but rather due to the fact that you are trying to mix paradigms. You are increasing the complexity of your code. Hopefully, it's just a temporary increase during a refactoring.
Old Answer
You said you want to make it "so that the views auto-update when their corresponding model property is changed, and the model update when the controls are changed (two way binding)."
IMO, that way of thinking about the problem is incorrect. Better would be to examine each output independently of all other outputs and deal with it directly. In order to explain what I mean, I will use the example of converting °F to °C and back...
This sounds like a great reason to use 2-way binding but let's see?
// the chain of observables represents a view model
celsiusTextField.rx.text // • this is the input view
.orEmpty // • these next two convert
.compactMap { Double($0) } // the view into an input model
.map { $0 * 9 / 5 + 32 } // • this is the model
.map { "\($0)" } // • this converts the model into a view
.bind(to: fahrenheitTextField) // • this is the output view
.disposed(by: disposeBag)
fahrenheitTextField.rx.text
.orEmpty
.compactMap { Double($0) }
.map { ($0 - 32) * 5 / 9 }
.map { "\($0)" }
.bind(to: celsiusTextField.rx.text)
.disposed(by: disposeBag)
The above code handles the two-way communication between the text fields without two-way binding. It does this by using two separate view models (The view model is the code between the text Observable and the text Observer as described in the comments.)
We can see a lot of duplication. We can DRY it up a bit:
extension ControlProperty where PropertyType == String? {
func viewModel(model: #escaping (Double) -> Double) -> Observable<String> {
orEmpty
.compactMap { Double($0) }
.map(model)
.map { "\($0)" }
}
}
You may prefer a different error handling strategy than what I used above. I was striving for simplicity since this is an example.
The key though is that each observable chain should be centered on a particular effect. It should combine all the causes that contribute to that effect, implement some sort of logic on the inputs, and then emit the needed output for that effect. If you do this to each output individually you will find that you don't need two-way binding at all.

Accessing singleton object in swift

This is my first time implementing a singleton to share an instance of an object in swift. Everything seems to be working totally fine, except for when I try and add an element to an array (accessing from another class) that lives within my singleton object. It indeed does not append any objects to the array at all. I'm thinking that it's appending onto an array, but not the same instance of the class that I would like it to be (as I only want one and only one instance). However, If I append elements onto the array from the init() of the class, everything works out just fine. Here's some code (I've simplified all the classes to make things more obvious):
File 1:
class Brew: NSObject {
var method = Method()
//Singleton variable
private static var currentBrew: Brew?
//Method to get the current (and only) brew object
static func getCurrentBrew() -> Brew {
if currentBrew == nil {
currentBrew = Brew()
}
return currentBrew!
}
}
struct Method {
var chemex = Device()
init() {
//If I append here - everything works fine
//chemex.instructions.append = (Instruction(title: "Prepare", direction: "Prewet & Heat", time: 3, water: 0))
}
}
struct Device {
var instructions = [Instruction]()
init() {
instructions.append(Instruction(title: "None", direction: "None", time: 1, water: 0, index: 0))
}
File 2: (where I would like to append to the array of instructions)
let brew = Brew.getCurrentBrew() //How i'm accessing the object
//I'm calling this method from viewDidLoad to set up the array
func setupBrewDevices() {
//This is the line that does not actually append to the singleton instance
brew.method.chemex.instructions.append(Instruction(title: "Extraction", direction: "Match water.", time: 8 , water: 25))
Just a side note, I also tried to make a method that would append an instruction onto the array that lives inside of the same class, but that had the same result. Hopefully this is clear enough - I appreciate any help!
Thanks,
Cole
There is a better way to create a singleton instance in Swift.
class Brew: NSObject {
static let currentBrew = Brew()
var method = Method()
}
This is thread-safe and avoids using an optional.
That said, when I tried your code the instructions array ended up with two elements like I would expect ("None") and ("Extraction"). The problem may lie elsewhere in your code.

What is the difference between a property and a variable in Swift?

From a few initial tutorials, I see that properties belong to a Class and are essentially 'global variables' as used in the C++ world (coded in this years ago). I also see variables as more of a 'local' entities only used / storing information within a method.
Then I came across this Quora thread: https://www.quora.com/Apple-Swift-programming-language/What-is-the-difference-between-a-property-and-a-variable
Now I see properties being able to execute code associated with their invocation. This is very cool, but also opened up a whole bunch of other questions for me.
Are there other simple and clear ways to remember the distinction between a property and a variable?
Properties belong to an object, whereas variables do not. A variable can be declared without having to be associated with a particular class, or other object. A property must be associated with a particular object (i.e.: a class, enum, or struct)
Local variables are just things that you work with. You have full control over these, and if you change a variable in a function, nothing outside of your function is ever gonna know. If I write a framework and you use it, and I decide to change something about a function's local variables, your app that uses my framework will keep working just as if nothing changed.
Classes, on the other hand, describe a contract. When you use a class, you have access to everything they publicly advertise. This means that if I write a framework and you use it, if I ever change or remove a public member on a class, your code will break if you were previously using that member.
For this reason, in many languages, it's bad practice to mark instance variables as public. Instance variables having no logic attached, if I want at some point to trigger something when a field is changed or if I want to remove the field entirely (and instead report a value in a sub-object or something), then I'm stuck with changing the public contract (turning the field in a pair of get/set methods, for instance), and possibly breaking your code.
Swift makes properties an indirection for this reason. Swift properties can be treated as dumb values for the most part, but if you ever need to change from a stored value to a computed value or something, you can do it without changing your class's interface. That way, you don't break existing code that relies on the property.
Swift variable, constant, Property
[Swift types]
variable - named storage of address. Every variable has a type which defines a memory size, attributes and behaviours
Swift variable and constants
constant is a variable but can not be modified after definition.
//definition
var <name> = <initial_value>
//type annotation
var <name>: <Swift_type> [= <initial_value>] // [] is optional
//var - variable
var myVariable1 = 11
var myVariable2: Int
myVariable2 = 12
//let - constant
let myConstant1 = 21
let myConstant2: Int
myConstant2 = 22
Global and local variable
Global variable is a variable which is defined out of function, class.
Local variable is: variable inside a type context(class, struct, enum)[About], inside a function, function parameter
Property
property - associate value with a type context. It is a variable + bounded getter/setter. It has field syntax but uses methods(getter/setter) under the hood.
Stored properties and computed properties
They can belong to instance(instance property) or type(type property):
Stored property (class, structure)
Computed property (class, structure, enum)
Stored property - is a local variable -> variable inside a type context. Swift stored property does not support instance variable like Objective-C.
variable stored properties - var
constant stored properties - let
It supports property observers (willSet, didSet)
Computed property - provide getter and optional setter to calculate a value every time
public class Person {
var firstName = "John"
var lastName = "Wick"
var fullNameComputedProperty: String {
get {
return "\(firstName) \(lastName)"
}
//optional
set {
let arr = newValue.split(separator: " ")
firstName = String(arr[0])
lastName = String(arr[1])
}
}
var addressStoredProperty: String {
//Property Observers
willSet {
print("old address:\(addressStoredProperty)")
print("new address:\(newValue)")
//addressStoredProperty is not updated yet
}
didSet {
print("old address:\(oldValue)")
print("new address:\(addressStoredProperty)")
}
}
}
Lazy Stored property
Property is calculate during first access to it(on demand)
only var lazy because let must have a value during initialization
Init/customize stored property by closure
Official doc
You are able to init/setup/customise a stored property with a help of closure
() at the end executes the closure immediately and assign a value to stored property(calculate and return a value).
in initializing case it is not possible to access to any instance variable or function because it has not initialized yet
in initializing case it will be executed only once for every object or if you use static - once for the class[Example]
Examples
func testStoredPropertyWithClosure() {
class ClassA { }
class ClassB {
static let staticStoredProperty: ClassA = {
//is called only when you access to it like ClassB.staticStoredProperty
print("init staticStoredProperty")
return ClassA()
}()
var storedProperty: ClassA = {
print("init storedProperty")
//self.foo() //Error: Class declaration cannot close over value 'self' defined in outer scope
return ClassA()
}()
func foo () {
storedProperty = {
print("customize storedProperty")
return ClassA()
}()
}
}
let b = ClassB()
b.foo()
ClassB.staticStoredProperty
}
closure stored property vs Computed property
closure stored property is called once and can be changed after initialization(if it is var)
Computed property is calculated every time when it is called
[Java variable, property...]

Resources