Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI iterating through dictionary with ForEach

Is there a way to iterate through a Dictionary in a ForEach loop? Xcode says

Generic struct 'ForEach' requires that '[String : Int]' conform to 'RandomAccessCollection'

so is there a way to make Swift Dictionaries conform to RandomAccessCollection, or is that not possible because Dictionaries are unordered?

One thing I've tried is iterating the dictionary's keys:

let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3] ... ForEach(dict.keys) {...} 

But keys is not an array of Strings, it's type is Dictionary<String, Int>.Keys (not sure when that was changed). I know I could write a helper function that takes in a dictionary and returns an array of the keys, and then I could iterate that array, but is there not a built-in way to do it, or a way that's more elegant? Could I extend Dictionary and make it conform to RandomAccessCollection or something?

like image 947
RPatel99 Avatar asked Jun 19 '19 20:06

RPatel99


People also ask

How do you use ForEach in a dictionary?

With this, it's possible to iterate through a dictionary in a KeyValuePair agnostic way: var dictionary = new Dictionary<int, string>(); // ... foreach (var (key, value) in dictionary) { // ... } Save this answer.

How does ForEach work in SwiftUI?

ForEach in SwiftUI is a view struct in its own right, which means you can return it directly from your view body if you want. You provide it an array of items, and you may also need to tell SwiftUI how it can identify each of your items uniquely so it knows how to update them when values change.

Is Swift dictionary ordered?

There is no order. Dictionaries in Swift are an unordered collection type. The order in which the values will be returned cannot be determined. If you need an ordered collection of values, I recommend using an array.

What is dictionary SwiftUI?

Swift dictionary is an unordered collection of items. It stores elements in key/value pairs. Here, keys are unique identifiers that are associated with each value.


2 Answers

You can sort your dictionary to get (key, value) tuple array and then use it.

struct ContentView: View {     let dict = ["key1": "value1", "key2": "value2"]          var body: some View {         List {             ForEach(dict.sorted(by: >), id: \.key) { key, value in                 Section(header: Text(key)) {                     Text(value)                 }             }         }     } } 
like image 149
ozmpai Avatar answered Sep 22 '22 17:09

ozmpai


Simple answer: no.

As you correctly pointed out, a dictionary is unordered. The ForEach watches its collection for changes. These changes includes inserts, deletions, moves and update. If any of those changes occurs, an update will be triggered. Reference: https://developer.apple.com/videos/play/wwdc2019/204/ at 46:10:

A ForEach automatically watches for changes in his collection

I recommend you watch the talk :)

You can not use a ForEach because:

  1. It watches a collection and monitors movements. Impossible with an unorered dictionary.
  2. When reusing views (like a UITableView reuses cells when cells can be recycled, a List is backed by UITableViewCells, and I think a ForEach is doing the same thing), it needs to compute what cell to show. It does that by querying an index path from the data source. Logically speaking, an index path is useless if the data source is unordered.
like image 26
J. Doe Avatar answered Sep 20 '22 17:09

J. Doe