Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwiftUI ForEach? #64

Closed
crenelle opened this issue Jun 12, 2020 · 6 comments
Closed

SwiftUI ForEach? #64

crenelle opened this issue Jun 12, 2020 · 6 comments

Comments

@crenelle
Copy link

I'm curious to learn whether this OrderedDictionary can work with SwiftUI ForEach.

@lukaskubanek
Copy link
Owner

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.

@crenelle
Copy link
Author

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'

@lukaskubanek
Copy link
Owner

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.

@crenelle
Copy link
Author

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!

@denizaydemir
Copy link

denizaydemir commented Jul 14, 2020

OrderedDictionary can be used with ForEach, List etc.. via several ways.

  1. ForEach (0..<orderedDictionary.count) { index in dictionary.elementAt(index)}
  2. 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.

@lukaskubanek lukaskubanek added this to To do in Version 4.0.0 Oct 4, 2020
@lukaskubanek
Copy link
Owner

lukaskubanek commented Oct 4, 2020

@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.

@lukaskubanek lukaskubanek moved this from To do to Done in Version 4.0.0 Oct 4, 2020
lukaskubanek added a commit that referenced this issue Oct 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Version 4.0.0
  
Done
Development

No branches or pull requests

3 participants