I am importing data from an API, which means I cannot use an enum or a list for my picker. This took me a bit to sort out and I did not see any good answers for it, so here is my solution.
The .task{} makes a server call that gets us an array of Seasons. As soon as the user chooses a Season from the picker, we use an .onChange() to set our local id to the id of that Season, so that when the user chooses a team, that id will be passed through. The second onChange() was a slight hack. I wanted to set seasons in the .task{}, but it would set seasons before leagueDataModel.league.seasons! was set. This meant seasons was always empty until I left and came back to the page. Nothing else worked for me so if you have a suggestion on the correct way to do this please comment.
import SwiftUI
struct TeamSelectView: View {
@StateObject private var leagueDataModel = LeagueDataModel()
@State private var teamSeason = Season()
@State private var seasons : [Season] = []
@State private var seasonNumber = "1"
var url = "fakeurl.com"
var body: some View {
VStack{
Picker("Season", selection: $teamSeason){
ForEach(seasons, id: \.self) {
Text($0.name!).tag($0.id)
}
}
ForEach(leagueDataModel.league.teamsNoAll!) { each in
TeamCard(team: each, season: seasonNumber)
}
}
.task{
await self.leagueDataModel.getLeagueData(url: url)
}
.onChange(of: self.teamSeason) {
self.seasonNumber = teamSeason.id ?? "1"
}
.onChange(of: self.leagueDataModel.league.seasons) {
seasons = leagueDataModel.league.seasons!
}
}
}
import Foundation
import SwiftUI
@MainActor
class LeagueDataModel : ObservableObject {
@Published var league = League()
func getLeagueData(url: String) async {
Service().getDataFromServer(stringurl: url) { (data : League) in
DispatchQueue.main.async { [self] in
self.league = data
}
}
}
}
// MARK: - League
struct League: Codable {
var seasons: [Season]? = []
}
// MARK: - Season
class Season: Codable, Hashable {
//the seasons all have ids (1,2,3, etc) and names (playoffs 2024, preseason 2024, etc)
let id, name: String?
enum CodingKeys: String, CodingKey {
case id, name
}
//everything must be optional so we can set an empty Season() to start with.
//I also could have set specific value for everything but preferred this route.
init(id: String? = nil, name: String? = nil) {
self.id = id
self.name = name
}
//we need Season to be Hashable in order to use it for the Picker
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Season, rhs: Season) -> Bool {
return lhs.id == rhs.id
}
}
.onChangeis for external actions, you'll have better luck if you learn@Bindingand computedBindingvars for updating dataViewswork well withstruct, try usingstruct Season: Identifiable, Codable, Hashable {....}removing yourfunc hashandstatic func == .... Note also useForEach(seasons) {...}.