I have a project where I want there are factories with orders (an array of Ints) that can be mutated.
I want all the code mutating, adding, removing, validating, etc of orders in another class (ie: almost like a proxy pattern) and when ready update the factory with the new orders.
I follow a delegate pattern to kick the new orders back to the factory for updating, however the factory orders never update.
Note: I know this is because the factory is a struct and that it is a value type
I am wondering if its possible to update the struct using a delegate pattern; or must I change it to a reference type (a class) in order to resolve the issue.
In the following code I've stripped out all the validation, push, pop and other features and am keeping it simple for this query by force changing the order array and then using a delegate to kick back the changed orders.
// Swift playground code
protocol OrderUpdatedDelegate {
mutating func ordersDidUpdate(_ orders: [Int])
}
// This class will handle all the validation to do with
// orders array, but for now; lets just force
// change the orders to test the delegate pattern
class OrderBook {
var delegate: OrderUpdatedDelegate?
var orders: [Int] = [Int]()
init(orders: [Int]) {
self.orders = orders
}
func changeOrders() {
self.orders = [7,8,1]
print ("updated orders to -> \(orders)")
delegate?.ordersDidUpdate(orders)
}
}
struct Factory {
var orders: [Int] = [Int]()
init(orders: [Int]) {
self.orders = orders
}
}
extension Factory: OrderUpdatedDelegate {
mutating func ordersDidUpdate(_ orders: [Int]) {
print ("recieved: \(orders)")
self.orders = orders
}
}
var shop = Factory(orders: [1,2,3])
let book = OrderBook.init(orders: shop.orders)
book.changeOrders()
print ("\nBook.orders = \(book.orders)")
print ("Shop.orders = \(shop.orders)")
Output:
Book.orders = [7, 8, 1]
Shop.orders = [1, 2, 3]
Again, I know the reason is because I've declared factory to be a struct; but I'm wondering if its possible to use a delegate pattern to mutate the orders array within the struct?
If not, I'll change it to a class; but I appreciate any feedback on this.
With thanks
There are 2 problems with your code, both of which needs fixing for it to work:
using a value type
not setting the delegate
Once you set the delegate, you'll see ordersDidUpdate actually getting called, but shop.orders will still have its original value. That is because as soon as you mutate your Factory, the delegate set on OrderBook will be a different object from the mutated Factory, which was updated in the delegate call ordersDidUpdate.
Using a reference type fixes this issue.
Couple of things to keep in mind when you switch to a class delegate. Make your OrderUpdatedDelegate be a class-bound protocol, then remove mutating from the function declaration. And most importantly, always declare class-bound delegates as weak to avoid strong reference cycles.
protocol OrderUpdatedDelegate: class {
func ordersDidUpdate(_ orders: [Int])
}
// This class will handle all the validation to do with
// orders array, but for now; lets just force
// change the orders to test the delegate pattern
class OrderBook {
weak var delegate: OrderUpdatedDelegate?
var orders: [Int] = []
init(orders: [Int]) {
self.orders = orders
}
func changeOrders() {
self.orders = [7,8,1]
print ("updated orders to -> \(orders)")
delegate?.ordersDidUpdate(orders)
}
}
class Factory {
var orders: [Int] = []
init(orders: [Int]) {
self.orders = orders
}
}
extension Factory: OrderUpdatedDelegate {
func ordersDidUpdate(_ orders: [Int]) {
print("receieved: \(orders)")
self.orders = orders
print("updated order: \(self.orders)")
}
}
var shop = Factory(orders: [1,2,3])
let book = OrderBook(orders: shop.orders)
book.delegate = shop
book.changeOrders()
print ("Book.orders = \(book.orders)")
print ("Shop.orders = \(shop.orders)")
As you said since Factory is a struct, when setting OrderBook delegate its already copied there so the delegate is actually a copy of your original factory instance.
A class is the appropriate solution for this.
Related
I am able to write mutating functions in structure but not in class.
struct Stack {
public private(set) var items = [Int]() // Empty items array
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int? {
if !items.isEmpty {
return items.removeLast()
}
return nil
}
}
In swift, classes are reference type whereas structures and enumerations are value types. The properties of value types cannot be modified within its instance methods by default. In order to modify the properties of a value type, you have to use the mutating keyword in the instance method. With this keyword, your method can then have the ability to mutate the values of the properties and write it back to the original structure when the method implementation ends.
If you change the struct to a class, just delete the keyword mutating wherever it appears.
That's because classes are reference types, and structures are value types.
struct TestValue {
var a : Int = 42
mutating func change() { a = 1975 }
}
let val = TestValue()
val.a = 1710 // Forbidden because `val` is a `let` of a value type, so you can't mutate it
val.change() // Also forbidden for the same reason
class TestRef {
var a : Int = 42
func change() { a = 1975 }
}
let ref = TestRef()
ref.a = 1710 // Allowed because `ref` is a reference type, even if it's a `let`
ref.change() // Also allowed for the same reason
So on classes, you don't need to specify if a function is mutating or not, because, even when defined with let variables, you can modify instance...
That's why the mutating key word has no meaning on classes.
I created this singleton to access a shared array throughout my app:
class TranslationItems {
var delegate: TranslationItemsDelegate?
static let shared = TranslationItems()
var array = [Translation]() {
didSet {
delegate?.newItemAdded()
}
}
}
The problem is that this allows for duplication (the array may contain multiple items with the same hashValue). If I check for duplication inside the didSet setter and then change the array there (for example by doing array = Array(Set(array))) that leads to an infinite loop.
How do I remove duplicates in my class?
If you want to avoid duplicates why don't you use a Set anyway (Translation must conform to Hashable)?
var set = Set<Translation>()
However if you want to keep the array a more efficient way is to add an add method which filters the duplicates, Translation must conform to Equatable
func add(object: Translation) {
if !array.contains(object) {
array.append(object)
delegate?.newItemAdded()
}
}
Making a Set from the Array and then convert it back to Array is unnecessarily expensive.
You can do it exactly how you suggested. This doesn't lead to an infinite loop
didSet {
array = Array(Set(array))
...
}
Just add one instance method
class TranslationItems {
var delegate: TranslationItemsDelegate?
static let shared = TranslationItems()
private(set) var array = [Translation]() {
didSet {
delegate?.newItemAdded()
}
}
func set(array:[Translation]) {
self.array = Array(Set(array))
}
}
I have my model implemented as structs in Swift 3.0. Several of these structs have delegates that should be able to modify the model depending on the user's actions.
However, when I pass the struct to the delegate method, it gets copied.
How do you solve this? Can you force the compiler to pass this struct as a reference, or the only option is to use a class?
structs are always passed by value. The whole point of using a struct is to have it behave as a value type. If you need delegation (which usually implies mutable state), you should be using a class.
If you really really need to, you could force pass-by-reference by using an inout parameter, but that is not recommended in general. You could also use a box type to simulate passing by reference. But, in general, you should just use a class if you need reference behavior.
The whole point of using struct in the first place is that this is desirable behavior. It preserves the immutability of the data. inout can achieve this, but it's not recommended in the general case.
protocol Delegate {
func callback(_ oldValue: Int) -> Int
}
struct IncrementerDelegate: Delegate {
let step: Int
func callback(_ oldValue: Int) -> Int {
return oldValue + step
}
}
struct Model {
var i = 0
}
class Controller {
var model = Model()
var delegate: Delegate
init(delegate: Delegate) {
self.delegate = delegate
}
// To be called externally, such as by a button
func doSomething() {
// Delegate determains new value, but it's still the
// controller's job to perform the mutation.
model.i = delegate.callback(model.i)
}
}
let delegate = IncrementerDelegate(step: 5)
let controller = Controller(delegate: delegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i)
protocol CrappyDelegate {
func callback(_ model: inout Model)
}
struct CrappyIncrementerDelegate: CrappyDelegate {
let step: Int
func callback(_ model: inout Model) {
model.i = 9999999
// Just hijacked the models value,
// and the controller can't do anything about it.
}
}
class VulnerableController {
var model = Model()
var delegate: CrappyDelegate
init(delegate: CrappyDelegate) {
self.delegate = delegate
}
// To be called externally, such as by a button
func doSomething() {
// Controller leaks entire model, and has no control over what happens to it
delegate.callback(&model)
}
}
let crappyDelegate = CrappyIncrementerDelegate(step: 5)
let vulnerableController = VulnerableController(delegate: crappyDelegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i) // model hijacked
If you want to pass by reference, you should generally use a class not a struct.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html states:
You can use both classes and structures to define custom data types to
use as the building blocks of your program’s code.
However, structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.
I'm trying to come up with a simple observable object in Swift and thought to use RxSwift. I couldn't find a simple example to do something like this:
protocol PropertyObservable {
typealias PropertyType
var propertyChanged: Event<(PropertyType, Any)> { get }
}
class Car: PropertyObservable {
typealias PropertyType = CarProperty
let propertyChanged = Event<(CarProperty, Any)>()
dynamic var miles: Int = 0 {
didSet {
propertyChanged.raise(.Miles, oldValue as Any)
}
}
dynamic var name: String = "Turbo" {
didSet {
propertyChanged.raise(.Name, oldValue as Any)
}
}
}
The above is pure Swift solution for observables from this blog post; I really like how it's a protocol-based solution and not invasive. In my case, I have an object in my project where each property is set asynchronously under the hood (bluetooth device). So I need to observe/subscribe to the changes instead of getting/setting the properties in real-time.
I keep hearing RxSwift will do just that and more. However, I can't find a simple example to match above and beginning to think RxSwift is overkill for my need? Thanks for any help.
Easiest way to quickly make this observable with RxSwift would probably be to use the RxSwift class Variable (all code here is untested off the top of my head):
import RxSwift
class Car {
var miles = Variable<Int>(0)
var name = Variable<String>("Turbo")
}
This enables you to observe the values by subscribing to them:
let disposeBag = DisposeBag()
let car = Car
car.name.asObservable()
.subscribeNext { name in print("Car name changed to \(name)") }
.addToDisposeBag(disposeBag) // Make sure the subscription disappears at some point.
Now you've lost the old value in each event. There are of course numerous ways to solve this, the RxSwifty way would probably be to add a scan operation to your element sequence, which works a lot like reduce does on a normal Array:
car.name.asObservable()
.scan(seed: ("", car.name.value)) { (lastEvent, newElement) in
let (_, oldElement) = lastEvent
return (oldElement, newElement)
}
.subscribeNext { (old, new) in print("Car name changed from \(old) to \(new)") }
.addToDisposeBag(disposeBag)
I have this class named Meal
class Meal {
var name : String = ""
var cnt : Int = 0
var price : String = ""
var img : String = ""
var id : String = ""
init(name:String , cnt : Int, price : String, img : String, id : String) {
self.name = name
self.cnt = cnt
self.price = price
self.img = img
self.id = id
}
}
and I have an array of Meal :
var ordered = [Meal]()
I want to duplicate that array and then do some changes to the Meal instances in one of them without changing the Meal instances in the second one, how would I make a deep copy of it?
This search result didn't help me
How do I make a exact duplicate copy of an array?
Since ordered is a swift array, the statement
var orderedCopy = ordered
will effectively make a copy of the original array.
However, since Meal is a class, the new array will contain references
to the same meals referred in the original one.
If you want to copy the meals content too, so that changing a meal in one array will not change a meal in the other array, then you must define Meal as a struct, not as a class:
struct Meal {
...
From the Apple book:
Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.
To improve on #Kametrixom answer check this:
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
You either have to, as #MarioZannone mentioned, make it a struct, because structs get copied automatically, or you may not want a struct and need a class. For this you have to define how to copy your class. There is the NSCopying protocol which unifies that on the ObjC world, but that makes your Swift code "unpure" in that you have to inherit from NSObject. I suggest however to define your own copying protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
which you can implement like this:
class Test : Copying {
var x : Int
init() {
x = 0
}
// required initializer for the Copying protocol
required init(original: Test) {
x = original.x
}
}
Within the initializer you have to copy all the state from the passed original Test on to self. Now that you implemented the protocol correctly, you can do something like this:
let original = Test()
let stillOriginal = original
let copyOriginal = original.copy()
original.x = 10
original.x // 10
stillOriginal.x // 10
copyOriginal.x // 0
This is basically the same as NSCopying just without ObjC
EDIT: Sadly this yet so beautiful protocol works very poorly with subclassing...
A simple and quick way is to map the original array into the new copy:
let copyOfPersons: [Person] = allPersons.map({(originalPerson) -> Person in
let newPerson = Person(name: originalPerson.name, age: originalPerson.age)
return newPerson
})
The new Persons will have different pointers but same values.
Based on previous answer here
If you have nested objects, i.e. subclasses to a class then what you want is True Deep Copy.
//Example
var dogsForAdoption: Array<Dog>
class Dog{
var breed: String
var owner: Person
}
So this means implementing NSCopying in every class(Dog, Person etc).
Would you do that for say 20 of your classes? what about 30..50..100? You get it right? We need native "it just works!" way. But nope we don't have one. Yet.
As of now, Feb 2021, there is no proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your class conforms to codable
class Dog: Codable{
var breed : String = "JustAnyDog"
var owner: Person
}
Create this helper class
class DeepCopier {
//Used to expose generic
static func Copy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need true deep copy of your object, like this:
//Now suppose
let dog = Dog()
guard let clonedDog = DeepCopier.Copy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
clonedDog.breed = "rottweiler"
//Also clonedDog.owner != dog.owner, as both the owner : Person have dfferent memory allocations
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our object. Just make sure all your Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.