I'm new to mobile development, and in the process of learning SwiftUI.
I've been struggling to figure out what's wrong with my picker. I am successfully returning the data from my URLSession, adding it to my model. I can confirm this by adding my @ObservedObject to a List, which returns all of the items. Putting the same @ObservedObject into a picker returns an empty picker for some reason. Any help would be appreciated. Thanks.
Here's my view with the Picker(). When run, the Picker is empty. I can comment out the Picker(), leaving just the ForEach() with Text(), and the text appears.
import Foundation
import Combine
import SwiftUI
struct ContentView: View {
@ObservedObject var countries = CulturesViewModel()
@State private var selectedCountries = 0
var body: some View {
VStack {
//loop through country array and add them to picker
Picker(selection: $selectedCountries, label: Text("Select Your Country")) {
ForEach(0 ..< countries.cultures.count, id: \.self) { post in
Text(self.countries.cultures[post].Culture).tag(post)
}
}.labelsHidden()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here's my ViewModel. It set's the @Published variable to the results of the JSON request in the WebService(). If I hard-code the @Published variable to the value that's begin returned, the Picker works.
import Foundation
import Combine
import SwiftUI
class CulturesViewModel: ObservableObject {
init() {
fetchCultures()
}
@Published var cultures = [Culture](){
didSet {
didChange.send(self)
}
}
private func fetchCultures(){
WebService().GetCultures {
self.cultures = $0
}
}
let didChange = PassthroughSubject<CulturesViewModel, Never>()
}
Here's my WebService(). Unfortunately, I'm unable to share the JSON url, I've added in the json that's returned.
import Foundation
import SwiftUI
import Combine
class WebService {
func GetCultures(completion: @escaping([Culture]) ->()) {
guard let url = URL("")
[
{
"CultureId": 0,
"Culture": "Select Your Country"
},
{
"CultureId": 1078,
"Culture": "English (United States)"
},
{
"CultureId": 6071,
"Culture": "English (Canada)"
}
]
URLSession.shared.dataTask(with: url) { (data,_,_) in
do {
if let data = data {
let culturesList = try JSONDecoder().decode([Culture].self, from: data)
DispatchQueue.main.async {
completion(culturesList)
}
} else {
DispatchQueue.main.async {
completion([])
}
}
} catch {
print(error)
DispatchQueue.main.async {
completion([])
}
}
}.resume()
}
}
Lastly, here's my Model.
import Foundation
struct Culture: Codable, Identifiable {
var id = UUID()
var CultureId: Int
var Culture: String
}
The work around to make the picker refresh is to add a unique id. Refreshing (or) reloading the countries, will create a new UUID for the picker items. This will force the picker to refresh. I've modified your code to include an id.
//loop through country array and add them to picker
Picker(selection: $selectedCountries, label: Text("Select Your Country")) {
ForEach(0 ..< countries.cultures.count, id: \.self) { post in
Text(self.countries.cultures[post].Culture).tag(post)
}
}.labelsHidden()
.id(UUID())
This seems to be a known issue with the picker.