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 ;)
Loop through Swift struct to get keys and values
First of all let's use CamelCase for the struct name
struct MyStruct {
var a = "11215"
var b = "21212"
var c = "39932"
}
Next we need to create a value of type MyStruct
let elm = MyStruct()
Now we can build a Mirror
value based on the elm
value.
let mirror = Mirror(reflecting: elm)
The Mirror
value does allow us to access all the properties of elm
, here's how
for child in mirror.children {
print("key: \(child.label), value: \(child.value)")
}
Result:
key: Optional("a"), value: 11215
key: Optional("b"), value: 21212
key: Optional("c"), value: 39932
Iterate over properties of a struct and check if they are equal to the properties of other value of the same type
In recent versions of swift, if all the properties of a struct are equatable, you can make the struct Equatable
without having to implement the ==
function.
In your case, all of your properties are Equatable
, so you can just declare the struct as Equatable
and you're done.
If the vast majority of your properties are Equatable
it might be easier to add "equatability" to those properties.
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 loop through keys in struct for SwiftUI?
Here is a demo of possible approach (for this simple case) - you can reflect properties and map them to shown values (for custom/complex types it will require more efforts by idea should be clear).
var body: some View {
let columnValues = Mirror(reflecting: pdt)
.children.map { "\($0.value)" }
return HStack {
ForEach(columnValues, id: \.self) { Text($0) }
}
}
Looping over the properties of Views (struct) in an Array is returning wrong values (init values)
Views are structs in SwiftUI. So passing them into a function will create a copy of those Views. And you are itterating over the original Views that have not been modified.
I think a ViewModel/Model would be best here.
struct Model: Identifiable{
var selected: Bool
let text: String
let id = UUID()
}
class Viewmodel: ObservableObject{
@Published var models: [Model] = [] //Create the datamodel here
func select(_ selectedModel: Model){
var selectedModel = selectedModel
selectedModel.selected.toggle() //Create a new copy and toggle selection
// get index of selected item
guard let index = models.firstIndex(where: {$0.id == selectedModel.id}) else{
return
}
// assign item. This will toggle changes in the view
models[index] = selectedModel
}
func post(){
//Create combined string
let combinedString = models.filter{$0.selected}.reduce("") { partialResult, model in
partialResult + model.text
}
//call post
postTextFunc(combinedString)
}
func postTextFunc(_ string: String){
}
}
struct InnerView: View{
@EnvironmentObject private var viewmodel: Viewmodel
var selected: Model // provide the individual data here
var body: some View{
Text(selected.text)
.onTapGesture {
viewmodel.select(selected)
}
}
}
struct TestView: View{
@StateObject private var viewmodel = Viewmodel()
var body: some View {
ScrollView {
ForEach(viewmodel.models, id: \.id) { model in
InnerView(selected: model)
.environmentObject(viewmodel) // pass viewmodel in environment
}
}
Button {
viewmodel.post()
} label: {
Text("Submit")
}
}
}
Related Topics
How to Enable Swipe to Delete Cell in a Tableview
How to Programmatically Check Support of 'Face Id' and 'Touch Id'
Detect Shake Gesture iOS Swift
Custom Font in iOS Not Working
Ios: Can't Save File to 'Application Support' Folder, But Can to 'Documents'
How to Extract a Url from a Sentence That Is in a Nsstring
Facebook Logout Is Not Working in New Sdk V.4.1.0 in iOS
Sectionindextitles for a Uicollectionview
Capture Metal Mtkview as Movie in Realtime
How to Integrate Linphone into an Existing Project (Sip in iOS)
Icloud + Coredata - How to Avoid Pre-Filled Data Duplication
Get List of All Installed Application in iOS 8
Decompilation Possibilities in iOS and How to Prevent Them
Xcode: "Scene Is Unreachable Due to Lack of Entry Points" But Can't Find It
Generics in Swift - "Generic Parameter 'T' Could Not Be Inferred