Clear way to update some nested struct from a bigger struct in place - swift

Say we have some complex struct with multiple nested levels(for simplicity, in the example will be only one level, but there could be more).
Example. We have a data structure:
struct Company {
var employee: [Int: Employee]
}
struct Employee {
var name: String
var age: Int
}
var company = Company(employee: [
1: Employee(name: "Makr", age: 25),
2: Employee(name: "Lysa", age: 30),
3: Employee(name: "John", age: 28)
])
Now we want to create a function which updates some Employee of the company in place. We could write it using an inout param:
func setAge(_ age: Int, forEmployee employee: inout Employee) {
employee.age = age
}
setAge(26, forEmployee: &company.employees[1]!)
This works, but as you can see we need to unwrap expression 'company.employees[1]' before passing it by ref. This forced unwrap can produce runtime error if there is no such employee for the provided key.
So we need to check if the employee exists:
if company.employees[1] != nil {
setAge(26, forEmployee: &company.employees[1]!)
}
This also works, but this code is kind of ugly because we need to repeat the expression 'company.employees[1]' two times.
So the question is: Is there some way to get rid of this repetition?
I tried to use optional inout param in the modifying function but could not get it working.

Based on your comments, like
What I wanted in the first place is just to have a reference to a substructure of a bigger structure so the part of code that is dealing with the substructure could know nothing about where is this substructure located in the bigger structure.
and
It would be ideal if I just could create a local inout var. Like if var employ: inout Employee? = company.employee[1] { // make whatever I want with that employee }.
I think that what you want is a generic update function. In the community this is part of the family of utility functions referred as with (https://forums.swift.org/t/circling-back-to-with/2766)
The version that you need in this case is one that basically guards on nil, so I suggest something like
func performUpdateIfSome <T> (_ value: inout T?, update: (inout T) throws -> Void) rethrows {
guard var _value = value else { return }
try update(&_value)
value = _value
}
with this utility then what you wanted to do would be done with
performUpdateIfSome(&company.employees[1], update: { $0.age = 26 })
Note
If you want to abstract away how to access the employee but not the company, then keypaths are an option as well :)

You need to hide the implementation and let the struct handle the logic with specific error handling strategy, like throwing an error or simply return true/false depending on success or simply ignore any problems. I don't know what the Int key stands for but here I guess it's an ID of some sort, so add this to Company struct
mutating func setAge(_ age: Int, forId id: Int) -> Bool {
if employee.keys.contains(id) {
employee[id]?.age = age
return true
}
return false
}

I would simply add extension to Employee which set employee's age
extension Employee {
mutating func setAge(_ age: Int) {
self.age = age
}
}
Then you can use optional chaining for calling. So if value for key 1 doesn't exist, nothing happens and code goes on
company.employee[1]?.setAge(26)
Edit:
If your goal is just to change some property and then return object, simply create method which takes optional parameter and returns optional value
func setAge(_ age: Int, forEmployee employee: inout Employee?) -> Employee? {
employee?.age = age
return employee
}
if let employee = setAge(26, forEmployee: &company.employees[1]) { ... }

Related

Inserting a unique value in the set based on value's property

I've created a struct with an "id" property:
struct SomeStruct: Hashable {
let id: String; // should be unique
let date: String;
let comment: String;
static func ==(lhs: SomeStruct, rhs: SomeStruct) -> Bool {
return lhs.id == rhs.id
}
}
I need to insert these structs in a set, but the set itself should decide whether the new member is unique or not based on its id, so:
someSet.insert(SomeStruct(id: "1", date: "22.09.2022", comment: "nothing here")) //inserted: true
someSet.insert(SomeStruct(id: "1", date: "05.12.1978", comment: "something here!")) //inserted: false, not unique id
That is why I implement the equality operator func in the struct and sometimes it works... but sometimes it doesn't and here I am, humbly asking for your help.
My implementation gives me weird results. The structs with the same id's can both be inserted or not, for example if I build my playground file once and new value gets inserted, next time I build it and the same value returns false.
I may have missed something, maybe I should implement custom hashValue property..?
Thanks in advance.
P.S. My job is to create a collection of these structs, where each struct has unique id. If you did it before and/or you think you know how to do it better, please let me know, I will be very glad to hear your ideas.
A solution was given in comments, which was to hash only on id instead of relying on the compiler generated implementation, which hashes on all the properties. That solution is a perfectly valid one for the stated problem, and may be ideal for your use case.
It does have one potential drawback: It means SomeStruct is only ever hashably-distinct based on id. That is the case that's presented, but we don't see the wider code base (nor should we). Is SomeStruct only ever used so that having its hash value based solely on its id is the right thing? It definitely could be, and probably is, since most things with a unique ID work that way, but it doesn't have to be, and I don't like making that assumption about code I can't see.
A more code-base agnostic and reusable approach is rather than tweaking the data's implementation to make a given collection behave the way you want, you can make a collection with that desired behavior for the data you have. There's more code involved in this approach, but it also gives more flexibility in how you can use the data.
The described behavior is that of a Dictionary keyed on the id, but the API of Set. There are some options. Here's one.
First make SomeStruct conform to Identifiable which is a standard Swift protocol. All that means is that it needs a Hashable id property, which you already have so adding that conformance is just:
extension SomeStruct: Identifiable { }
Though you could add Identifiable to the definition of SomeStruct instead. Either way works.
Then create a data structure that is keyed on id. This doesn't have to be complicated. Just leverage the existing thing that does most of what you need to implement it. In this case, that's Dictionary. So here's a minimal, but generic, IDSet that does that:
struct IDSet<T: Identifiable>
{
public typealias Element = T
internal typealias Storage = [Element.ID: Element]
private var storage = Storage()
public mutating func insert(_ value: Element) -> Bool
{
guard !storage.keys.contains(value.id) else { return false }
storage[value.id] = value
return true
}
}
Because IDSet is generic, you can make one for any kind of Identifiable thing. It also doesn't need for the whole element to be Hashable. It only needs its id to be Hashable. That gives you more flexibility in the kinds of elements you store in it.
Presumably you'd want to iterate over the elements, so you'd need to make it conform to Sequence. Again there's no need to get complicated in making an Iterator, because you can leverage Dictionary's Iterator:
extension IDSet: Sequence
{
public struct Iterator: IteratorProtocol
{
internal var iter: Storage.Iterator
public mutating func next() -> Element?
{
guard let (_, value) = iter.next() else { return nil }
return value
}
}
public func makeIterator() -> Iterator {
Iterator(iter: storage.makeIterator())
}
}
You probably want IDSet to conform to some other protocols too, but I think it's likely you can see how that would work.
To use it, it's much like Set, although insert returns a non-discardable Bool indicating whether the element was inserted. Let's say you want to test this behavior in XCTest using your example data:
var someSet = IDSet<SomeStruct>()
XCTAssertTrue(someSet.insert(SomeStruct(id: "1", date: "22.09.2022", comment: "nothing here")))
XCTAssertFalse(someSet.insert(SomeStruct(id: "1", date: "05.12.1978", comment: "something here!")))
And of course, because it conforms to Sequence you can iterate over it:
for s in someSet {
print("id: \(s.id), date: \(s.date), comment: \"\(s.comment)\"")
}
Or use various other Sequence methods:
let comments = someSet.map { $0.comment }
Set elements are Hashable
Hashing a value means feeding its essential components into a hash function, represented by the Hasher type. Essential components are those that contribute to the type’s implementation of Equatable. Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.
I suggest adding:
struct SomeStruct: Hashable {
let id: String;
let date: String;
let comment: String;
static func ==(lhs: SomeStruct, rhs: SomeStruct) -> Bool {
return lhs.id == rhs.id
}
public func hash(into hasher: inout Hasher) {
// feed the only property that contribute to Equatable implementation
hasher.combine(id)
}
}

How do I make the properties of a class iterable is swift using Sequence and IteratorProtocol?

I would like to make my class in Swift iterable.
My goal is to be able to create a class called Contact that holds properties such as the givenName, familyName, and middleName, like iOS CNContact. I would like to be able to have a class function that compares two instances of the class Contact, and finds which property the two contact objects have that match, so that say if both contacts have the same value for the givenName property, then the class function returns the result.
Here is a sample code:
class Contact {
static func compare(left: Contact, right: Contact) {
for property in left.properties {
if property == right.property {
// match is found
}
}
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
private var properties = [givenName, familyName, middleName]
}
let left = Contact()
let right = Contact()
Contact.compare(left: left, right: right)
I found posts that used mirroring and reflection, but I want to use Sequence and IteratorProtocol. I suspect there is already the ability to do exactly what I want to do. It seems to be a logical need that would arise.
What is the way to do this that has a balance between simplicity and the ability to address common needs to iterate through the instance properties of a class. An enumeration can be declared with given has values. Is there a way to make that work for this purpose? Is there a protocol that a class can use that assigns a hash value or other identifiable value that would allow for a sequential order to iterate through the properties of a class?
I was able to find posts and documentation that allowed me to get as far as the following code in playground that generated the following in debug window:
struct Name: Sequence {
typealias Iterator = NameIterator
typealias Element = Name
typealias Name = String
var name = "<name>"
func makeIterator() -> NameIterator {
return NameIterator()
}
}
struct NameIterator: IteratorProtocol {
typealias Iterator = String
typealias Element = Name
typealias Name = String
mutating func next() -> Name? {
let nextName = Name()
return nextName
}
}
let nameStrings = ["Debbie", "Harper", "Indivan", "Juniella"]
for nameString in nameStrings {
print(nameString)
}
Debbie
Harper
Indivan
Juniella
If you really don't want to use mirror, a straightforward way is to cycle through a list of key paths. This is particularly easy in your case because the properties are all strings:
class Contact {
static let properties = [\Contact.givenName, \Contact.familyName, \Contact.middleName]
static func compare(left: Contact, right: Contact) {
for property in properties {
if left[keyPath: property] == right[keyPath: property] {
print("got a match"); return
}
}
print("no match")
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
}
I think there's some confusion going on here.
The Sequence protocol and friends (IteratorProtocol, Collection, etc.) exist for you to be able to define custom sequences/collections that can leverage the existing collection algorithms (e.g. if you conform to Sequence, your type gets map "for free"). It has absolutely nothing to do with accessing object properties. If you want to do that, the only official reflection API in Swift is Mirror.
It's possible to...
...just Mirror, to create a standard collection (e.g. Array) of properties of an object
...just Sequence/Collection, to create a custom collection object that lists the property values of an object from hard-coded keypaths
...or you can use both, together, to create a custom collection object that uses Mirror to automatically list the properties of an object and their values

Is there any way to make the method return a mutable value?

as shown in the code below:
struct Person {
var name: String
}
struct Group {
var person: Person
func callAsFunction() -> Person {
// Person is immutable value
person
}
}
var james = Person(name: "James")
var group = Group(person: james)
group().name = "Wong" //ERROR: Cannot assign to property: function call returns immutable value
group() return an immutable value, that can't be changed! So Is there any way to make the callAsFunction() method return a mutable value?
Thanks ;)
Updated:
My idea is to transfer all the calls and visits of the Group to the Person object in the Group, just like using Person directly.
I can't use dynamicMemberLookup because I don't know what method or property there will be in Person. For example, there may be 100 methods and properties in Person (not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods with dynamicMemberLookup.
My needs are a bit like proxy objects in the Ruby language. Accessing an object (Group) actually accesses another object (Person) inside it, as if the Group does not exist.
ruby proxy patterns:
https://refactoring.guru/design-patterns/proxy/ruby/example
CallAsFunction is the closest implementation so far, but requires that Person cannot be a Struct, otherwise it cannot be assigned to its properties.
Maybe it's not possible to implement this feature in Swift yet?
You're using the wrong dynamic method. What you want is dynamicMemberLookup. Watch closely. First, the preparation:
struct Person {
var name: String
}
#dynamicMemberLookup
struct Group {
var person: Person
subscript(dynamicMember kp:WritableKeyPath<Person,String>) -> String {
get { self.person[keyPath:kp] }
set { self.person[keyPath:kp] = newValue }
}
}
Now look at what that allows you to say:
var group = Group(person: Person(name: "James"))
group.name = "Wong"
print(group.person) // Person(name: "Wong")
Do you see? We set the name of the Group even though it has no name property, and the result was that we set the name of the Group's person which does have a name property.
The callAsFunction simply returns (a copy of the) Person, which is a value type. You cannot then mutate the property of it like that. It is equivalent to the following:
struct Person {
var name: String
}
Person(name: "Foo").name = "Bar"
That returns the same error:
If Person was a reference type, it would have worked, but not for a value type. And even if you took your value type, and first assigned it to a variable before mutating it, you would only be mutating your copy, not the original.
If you want the behavior you want, you would use a #dynamicMemberLookup as suggested by matt (+1) and outlined in SE-0195.
You said:
I can't use dynamicMemberLookup because I don't know what method or property there will be in Person. For example, there may be 100 methods and properties in Person (not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods with dynamicMemberLookup.
You do not need “100 subscript methods.” It is the motivating idea behind #dynamicMemberLookup, namely that the properties will be determined dynamically. E.g., here is Person with two properties, but Group only has the one #dynamicMemberLookup.
struct Person {
var name: String
var city: String
}
#dynamicMemberLookup
struct Group {
var person: Person
subscript(dynamicMember keyPath: WritableKeyPath<Person, String>) -> String {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}
var group = Group(person: Person(name: "James", city: "New York"))
group.name = "Wong"
group.city = "Los Angeles"
print(group.person) // Person(name: "Wong", city: "Los Angeles")
If you want to handle different types, make it generic:
struct Person {
var name: String
var city: String
var age: Int
}
#dynamicMemberLookup
struct Group {
var person: Person
subscript<T>(dynamicMember keyPath: WritableKeyPath<Person, T>) -> T {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}
And
var group = Group(person: Person(name: "James", city: "New York", age: 41))
group.name = "Wong"
group.city = "Los Angeles"
group.age = 42
print(group.person) // Person(name: "Wong", city: "Los Angeles", age: 42)

Swift function overloading - invoking func with specific parameter (in place of Any parameter) on passing Any object

I have 3 structs:
struct Address: Codable {
var addressLine1: String
}
struct Person: Codable {
var name: String
}
struct Order: Codable {
var person: Person?
var address: Address?
}
In my ViewController class I am using Mirror to access each of the properties within Order:
let address = Address(addressLine1: "Some unknown address")
let person = Person(name: "Some unknown name")
let order = Order(person: person, address: address)
let newOrderMirror = Mirror(reflecting: order)
newOrderMirror.children.forEach {
display(child: $0.value)
}
In ViewController I have implemented 3 display functions:
func display(child: Any) {
// this should never get called
print(child)
}
func display(child: Person) {
print(child)
}
func display(child: Address) {
print(child)
}
In above case it is always invoking func display(child: Any), however I want it to invoke the functions with specific parameter. Is there any way to achieve this without type casting?:
Off-course this will work:
newOrderMirror.children.forEach {
if let bs = $0.value as? Person {
display(child: bs)
} else if let addr = $0.value as? Address {
display(child: addr)
}
display(child: $0.value)
}
Is there any other elegant way to achieve the desired behavior without using if-let + typecasting?
Update 1:
Found something related over here - How to call the more specific method of overloading
Update 2:
I can achieve a more concise solution by following these steps:
Declare Displayable protocol with func display()
Let each Modelimplement this protocol
Within forEach on each children I can just bind and typecast to the protocol and invoke display method on it
However this looks like an ugly solution to me as Model is now serving 2 responsibilities - i. Handle/ Store data, ii. Handle how the data is displayed. The 2nd responsibility looks to me like more reasonable for a controller or presenter class to implement rather than the model class, hence I want to avoid that path
The children property points to a collection of Mirror.Child elements, which is actually a tuple made of a String and an Any. Thus every child of a mirror has the compile type set to Any (this is needed as the mirror can virtually be applied to any type).
Due to the above, and given the fact that overloading is a compile-time feature, results that the compiler can't pick other overload than the one with the Any parameter.
One workaround you could make to this is to change the body of the first display() overload:
func display(child: Any) {
switch child {
case let person as Person: display(person)
case let address as Address: display(address)
default: print(child)
}
}
func display(child: Person) {
print(child)
}
func display(child: Address) {
print(child)
}
Basically you still need to help the compiler pick the right function to call.

How to implement read-only var with mutating get

I have a var
var soketTasksList:Set<SocketTask> {
get { return socketManager.tasksList }
}
I don't need to set, only get,
but I need do something like this
soketTasksList.remove(task)
but compiler says
Cannot use mutating member on immutable value is a get-only property
I tried to add the keyword 'mutating' to the get, but this isn't working. I also
tried to add 'mutating' to the var, but this isn't working either.
UPD
i dont undestand why do I need set?
if i do
func getSoketTasksList() -> Set<CXSocketTask> {
return socketManager.tasksList
}
i can
getSoketTasksList().remove(task)
why not with var?
i don't need to set, only get
Yes, you do need to set.
i need do something like this soketTasksList.remove(task)
That is a mutation. Mutating a value type like Set requires the ability to set. But you have cut off that possibility by making this a read-only computed variable.
UPD i dont undestand why do I need set? if i do
func getSoketTasksList() -> Set<CXSocketTask> {
return socketManager.tasksList
}
i can
getSoketTasksList().remove(task)
No you can't. Try it. Here's a playground test:
class CXSocketTask:NSObject{}
class SocketManager {
var tasksList = Set<CXSocketTask>()
}
let task = CXSocketTask()
let socketManager = SocketManager()
socketManager.tasksList.insert(task)
func getSoketTasksList() -> Set<CXSocketTask> {
return socketManager.tasksList
}
getSoketTasksList().remove(task)
The last line generates an error: "cannot use mutating member on immutable value: 'getSoketTasksList' returns immutable value".
You can use temporary variable for this purpose:
var list = soketTasksList
list.remove(task)
Note that underlying list (socketManager.tasksList in this case) remains untouched.
The only situation in which you can do this is if the actual mutating is done through something which is settable.
For example:
struct Person {
var age: Int = 1
mutating func setAge(a: Int) -> Int {
age = a
return a
}
var computedAge: Int {
mutating get {
setAge(a: 4)
return age
}
}
}
var person = Person()
print(person.computedAge) //Prints 4
This is by design