管理视图模型

问题描述 投票:0回答:1

这更多的是一个架构问题。

我正在使用 Rick 和 Morty API 开发一个个人项目,遵循此设计,使用 MVVM 模式使用 Swift 和 SwiftUI 进行构建。

我为每个选项卡视图都有单独的视图模型 - 角色、位置、剧集,负责独立获取角色、位置和剧集。

现在,当您单击某个角色时,它会将您带到角色详细信息视图,该视图还显示该角色曾参与过的剧集,我的困惑是我是否继续为这些创建 ViewModel?或者有更好的方法吗?

工作流程将是这样的:

CharacterView [taps character] —> CharacterDetailView [sees episode characters in, tap an episode] —> EpisodeDetailView
,这样的情况一直持续下去。

解决这个问题更好的方法是什么?或者为每个视图创建一个独立的视图模型就足够了吗?这不会导致项目中的文件集合很大吗?或者做这样的post工作会起作用吗?

我尝试为每个创建一个视图模型。这是获取角色出现的剧集的视图模型。

class CharacterEpisodeViewModel: ObservableObject {
  @Published var charEpisodes: [CharacterEpisode] = []
  @Published var error: Error?

  init(character: Character) {
    loadData(character: character)
  }

  func loadData(character: Character) {
    Task(priority: .low) {
      do {
        try await fetchCharacterEpisode(from: character.episode) { episodes in
          self.charEpisodes = episodes
          print("Received episodes: \(episodes)")
        }
      } catch {
        print("Error fetching episodes: \(error)")
      }
    }
  }

  func fetchCharacterEpisode(from urls: [String], completionHandler: ([CharacterEpisode]) -> Void) async throws {
    var fetchedEpisodes: [CharacterEpisode] = []
    var seenEpisodes: Set<Int> = []

    for url in urls {
      let episodeID = Int(url.components(separatedBy: "/").last ?? "") ?? 0
      if seenEpisodes.contains(episodeID) {
        continue
      }
      seenEpisodes.insert(episodeID)

      // Fetch episode data from API
      let episode = try await fetchEpisodeData(from: url)
      fetchedEpisodes.append(episode)
      print("Received episode: \(episode)")
    }

    completionHandler(fetchedEpisodes)
  }

  func fetchEpisodeData(from url: String) async throws -> CharacterEpisode {
    guard let url = URL(string: url) else {
      throw APIResponseError.invalidURL
    }

    let (data, response) = try await URLSession.shared.data(from: url)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else {
      throw APIResponseError.serverError
    }

    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let episode = try decoder.decode(CharacterEpisode.self, from: data)
      
    print("Decoded episode: \(episode)")
    return episode
  }
}
ios swift swiftui mvvm
1个回答
0
投票

在 SwiftUI 中,View 结构已经是视图模型,因此最好使用它们而不是您自己的对象,否则您将遇到 View 结构已经设计用来解决的一致性问题。

要异步加载视图数据,它是 .task(id:) ,您可以从那里调用异步函数(通常它们在作为环境键的 Controller 结构中定义,因此可以模拟它以进行测试)。

将结果存储在@State中并使用计算变量将其转换为子视图。

© www.soinside.com 2019 - 2024. All rights reserved.