How Do People Deal with Iterating a Swift Struct Value-Type Property

how do people deal with iterating a Swift struct value-type property?

As of Swift 4, a compromise is to iterate over the indices of a mutable collection instead of the elements themselves, so that

for elem in mutableCollection {
// `elem` is immutable ...
}

or

for var elem in mutableCollection {
// `elem` is mutable, but a _copy_ of the collection element ...
}

becomes

for idx in mutableCollection.indices {
// mutate `mutableCollection[idx]` ...
}

In your example:

for idx in f.columns.indices {
f.columns[idx].cards.append(Card())
}

As @Hamish pointed out in the comments, a future version of Swift may implement a mutating iteration, making

for inout elem in mutableCollection {
// mutate `elem` ...
}

possible.

Loop through Struct to check a value

You can enumerate each key value pair in a dictionary like so:

for (i, alrt) in alertStack

Where "i" would be your int value

But: it may be better to try to find a more Swifty way of expressing your problem (what are you trying to do?) rather than trying to translate from PHP. For example, perhaps like:

let alerts: [Alert] = [
(60, 55, "green", "60 Seconds"),
(30, 25, "yellow", "30 Seconds!")
]
.map(Alert.init(begin:end:color:message:))

let color: String
let alertText: String

if let foundAlert = alerts.last(where: { $0.begin < secondsLeft }) {
color = foundAlert.color
alertText = foundAlert.message
}
else {
color = "default"
alertText = ""
}

(Maybe there is a reason but I don't know why you would want to have them in a dictionary keyed by their begin numbers)

If it helps I would imagine your problem may be expressed something like this:

struct Alert: Codable {
let color: String
let message: String

static let defaultAlert = Alert(color: "default", message: "")

static let thresholds: [ClosedRange<Double>: Alert] = [
55...60: Alert(color: "green", message: "60 Seconds"),
25...30: Alert(color: "yellow", message: "30 Seconds!")
]
}

func currentAlert(value: Double) -> Alert {
// Return the alert where the range of seconds contains
// the provided remaining seconds value
guard let found = Alert.thresholds.first(where: {
$0.key.contains(value)
}) else {
return .defaultAlert
}

return found.value
}


print(currentAlert(value: 57)) // 60 Seconds
print(currentAlert(value: 42)) // default
print(currentAlert(value: 26)) // 30 Seconds!

How to get all property names in nested Structs

So the issue with the “some” stems from your values being optional, optional values are an enumeration so your reflection is picking that up. This is why you cannot perform the guard in private func values(obj: Any, count: Int, limit: Int) -> [Any] as you are not unwrapping to a concrete type.

As all your subtypes confirm to Loopable we can refactor your code to check if the type conforms to Loopable. It is also more efficient to check if something is empty than if its count is equal to zero, so you should use that property where you can.

We’re now using the prop value as a prefix so that we can get the name that you are looking for, however, because your values are optional they are wrapped in an enum so you have to strip the .some from the string. The type of val is Any, this means we cannot unwrap it if it is an optional without knowing what its concrete type is, hence we need to do the above dance to remove the .some from the prefix.


import Foundation


protocol Loopable {
func allProperties() -> [String]
}

extension Loopable {
func allProperties() -> [String] {
return props(obj: self)
}

private func props(obj: Any, prefix: String = "") -> [String] {
let mirror = Mirror(reflecting: obj)
var result: [String] = []
for (prop, val) in mirror.children {
guard var prop = prop else { continue }

// handle the prefix
if !prefix.isEmpty {
prop = prefix + prop
prop = prop.replacingOccurrences(of: ".some", with: "")
}

if let _ = val as? Loopable {
let subResult = props(obj: val, prefix: "\(prop).")
subResult.isEmpty ? result.append(prop) : result.append(contentsOf: subResult)
} else {
result.append(prop)
}
}
return result
}
}


Here is a simple struct that we can test the above code with.

struct User: Loopable {
let name: String
let age: Age?

struct Age: Loopable {
let value: Int?
let day: Day?

struct Day: Loopable {
let weekday: Int?
}
}
}

let user = User(name: "mike", age: .init(value: 20, day: .init(weekday: 5)))

print(user.allProperties())

This will print out the following

["name", "age.value", "age.day.weekday"]

Swift and mutating struct

The mutability attribute is marked on a storage (constant or variable), not a type. You can think struct has two modes: mutable and immutable. If you assign a struct value to an immutable storage (we call it let or constant in Swift) the value becomes immutable mode, and you cannot change any state in the value. (including calling any mutating method)

If the value is assigned to a mutable storage (we call it var or variable in Swift), you're free to modify the state of them, and calling of mutating method is allowed.

In addition, classes don't have this immutable/mutable mode. IMO, this is because classes are usually used to represent reference-able entity. And reference-able entity is usually mutable because it's very hard to make and manage reference graphs of entities in immutable manner with proper performance. They may add this feature later, but not now at least.

For Objective-C programmers, mutable/immutable concepts are very familiar. In Objective-C we had two separated classes for each concept, but in Swift, you can do this with one struct. Half work.

For C/C++ programmers, this is also very familiar concept. This is exactly what const keyword do in C/C++.

Also, immutable value can be very nicely optimised. In theory, Swift compiler (or LLVM) can perform copy-elision on values passed by let, just like in C++. If you use immutable struct wisely, it will outperform refcounted classes.

Update

As @Joseph claimed this doesn't provide why, I am adding a little more.

Structs have two kind of methods. plain and mutating methods. Plain method implies immutable (or non-mutating). This separation exists only to support immutable semantics. An object in immutable mode shouldn't change its state at all.

Then, immutable methods must guarantee this semantic immutability. Which means it shouldn't change any internal value. So compiler disallows any state changes of itself in a immutable method. In contrast, mutating methods are free to modify states.

And then, you may have a question of why immutable is the default? That's because it's very hard to predict the future state of mutating values, and that usually becomes the main source of headaches and bugs. Many people agreed that the solution is avoiding mutable stuffs, and then immutable by default was on top of wish list for decades in C/C++ family languages and its derivations.

See purely functional style for more details. Anyway, we still need mutable stuffs because immutable stuffs have some weaknesses, and discussing about them seems to be out of topic.

I hope this helps.

How to loop over struct properties in Swift?

Although old question, with Swift evolving this question has new answer. I think that you approach is way better for the described situation, however original question was how to iterate over struct properties, so here is my answer(works both for classes and structs)

You can use Mirror Structure Reference. The point is that after calling reflect to some object you get it's "mirror" which is pretty sparingly however still useful reflection.

So we could easily declare following protocol, where key is the name of the property and value is the actual value:

protocol PropertyLoopable
{
func allProperties() throws -> [String: Any]
}

Of course we should make use of new protocol extensions to provide default implementation for this protocol:

extension PropertyLoopable
{
func allProperties() throws -> [String: Any] {

var result: [String: Any] = [:]

let mirror = Mirror(reflecting: self)

guard let style = mirror.displayStyle where style == .Struct || style == .Class else {
//throw some error
throw NSError(domain: "hris.to", code: 777, userInfo: nil)
}

for (labelMaybe, valueMaybe) in mirror.children {
guard let label = labelMaybe else {
continue
}

result[label] = valueMaybe
}

return result
}
}

So now we can loop over the properties of any class or struct with this method. We just have to mark the class as PropertyLoopable.

In order to keep things static(as in the example) I will add also a singleton:

struct ReuseID: PropertyLoopable {
static let instance: ReuseID = ReuseID()
}

Whether singleton used or not, we could finally loop over the properties like follows:

do {
print(try ReuseID.instance.allProperties())
} catch _ {

}

And that's it with looping struct properties. Enjoy swift ;)

Get struct properties from string - swift

What you are looking for is reflection:

struct MyStruct {
var informationA = "test"
var informationB = "test...test"
var informationC = "test...test"
var informationD = "test...test..."
}

func getInformation() {
let my_struct = MyStruct()
let keys = ["informationA", "informationB", "informationC"]
let m = Mirror(reflecting: my_struct)
let properties = Array(m.children)

for k in keys {
if let prop = properties.first(where: { $0.label == k }) {
print(prop.value)
}
}
}

What are some good ways to avoid repeating array index accessing operation on same Array of struct element?

There are a lot of good answers here, and you should not think of value types as "a limitation." The behavior of value types is very intentional and is an important feature. Generally, I'd recommend inout for this problem, like matt suggests.

But it is also certainly possible to get the syntax you're describing. You just need a computed variable (which can be a local variable).

let index = 0 // Just to show it can be externally configurable

var referenceToCardStruct: Card {
get { cards[index] }
set { cards[index] = newValue }
}
referenceToCardStruct.name0 = "good"
referenceToCardStruct.name1 = "bye"

print(cards[0].name0 + " " + cards[0].name1)

Modifying struct data in a ForEach in SwiftUI

This is because a ForEach returns you a read-only copy of the original example to operate on and you can't modify it. And even if you could it wouldn't be the same struct (structs are value types).

You need to access the original item instead:

var body: some View {
ForEach(examples.examples.indices) { index in
VStack {
Button("Complete") {
examples.examples[index].completed = true
}
Text(examples.examples[index].title)
}
}
}


Related Topics



Leave a reply



Submit