Closed crenelle closed 4 years ago
Hi Michael, there hasn’t been any special work done for this. I’m not sure how ForEach
exactly works since I’m not using SwiftUI myself. There is a slight chance that it could work out of the box since OrderedDictionary
is an iterable collection. You can give it a try and see for yourself. Creating a test project and adding this library via SPM should quickly answer your question.
The error message I get that seems to get right to the point is:
Generic struct 'ForEach' requires that 'OrderedDictionary<String, Any>' conform to 'RandomAccessCollection'
Thanks for the tests. OrderedDictionary
only conforms to BidirectionalCollection
and not RandomAccessCollection
.
I’ve looked into why this is and I couldn’t find any reason not to conform to RandomAccessCollection
. In fact, when doing my quick tests, it worked out of the box when changing the conformance from BidirectionalCollection
to RandomAccessCollection
. The only change necessary would be a straightforward implementation of formIndex(after:)
and formIndex(before:)
methods. However, I’d need to do more testing on this before publishing such change and since the WWDC is around the corner, I won’t have time for this soon.
As a temporary workaround, you should be able to convert your OrderedDictionary
instance to an array of key-value pairs using Array(orderedDictionary)
and iterate over that in the list. I hope this helps.
I've experimented with using your OrderedDictionary (and indeed Dictionary) with ForEach, and decided the results that are specific to working with SwiftUI called for a different approach, and did that. I do have an array but had to make a slightly different arrangement to accommodate the ForEach.
I would for sure recommend holding off on any changes until after WWDC; I expect Apple to offer one, perhaps two minor adjustments to the SwiftUI pantheon that could affect development outcomes, like the existing SwiftUI code in my project. Thanks!
OrderedDictionary can be used with ForEach
, List
etc.. via several ways.
ForEach (0..<orderedDictionary.count) { index in dictionary.elementAt(index)}
ForEach ([CustomObject] (orderedDictionary.orderedValues)) { value in }
(using orderedKeys is the same)Note that conforming Identifiable
and proper implementation is important, otherwise rendered views may be duplicated or wrongly ordered.
@crenelle: I’ve looked into this issue and although I most likely will be able to make OrderedDictionary
conform to RandomAccessCollection
in the upcoming version, it won’t be of any help with its use in SwiftUI’s ForEach
. The reason is that ForEach
requires the elements of Data
to conform to Identifiable
. Since the elements of OrderedDictionary
are tuples holding both the key and value, it’s not possible to make them conform to any protocol and thus they cannot be used directly. However, there are other ways, but it highly depends on what you’re trying to achieve.
The simplest way is to base it on indices as follows:
ForEach(orderedDictionary.indices) { index in
let (key, value) = orderedDictionary[index]
// Use index, key, value
}
If you want SwiftUI to keep track of elements’ identities, you can make the keys conform to Identifiable
and leverage the orderedKeys
property while loading values for them as follows:
ForEach(orderedDictionary.orderedKeys) { key in
let value = orderedDictionary[key]
// Use key, value
}
I hope this helps. If you have any follow-up questions, let me know.
Edit: You might need a conversion to Array
in the second case which would then read like this ForEach(Array(orderedDictionary.orderedKeys))
. In version 4.0, I’m currently working on, this won’t be necessary anymore.
I'm curious to learn whether this OrderedDictionary can work with SwiftUI ForEach.