智用指南
霓虹主题四 · 更硬核的阅读氛围

MVVM中Model层该怎么写?别再搞混了

发布时间:2025-12-09 04:10:21 阅读:323 次

开发一个天气应用时,界面要显示城市名、温度、湿度,还有一周预报。这些数据从哪来?怎么组织?很多人一上来就急着写界面,结果代码越堆越多,改个接口字段全得重来。其实关键在于Model层的写法。

Model不是简单的数据容器

很多人以为Model就是定义几个属性,比如 cityName、temperature,然后在ViewModel里直接调用API返回的数据塞进去。这种做法看似简单,实则埋下隐患。一旦接口字段变了,或者需要对数据做处理,就得到处改。

真正的Model应该封装数据来源和逻辑。它不关心界面长什么样,只负责把干净、结构化的数据交给ViewModel。

用类来组织,别用字典凑合

比如天气数据,应该定义一个 WeatherModel 类:

class WeatherModel {
    let cityName: String
    let temperature: Double
    let humidity: Int
    let forecast: [ForecastItem]

    init?(from json: [String: Any]) {
        guard let name = json["city"] as? String,
              let temp = json["temp_c"] as? Double else {
            return nil
        }
        self.cityName = name
        self.temperature = temp
        self.humidity = json["humidity"] as? Int ?? 0
        
        if let list = json["forecast"] as? [[String: Any]] {
            self.forecast = list.compactMap { ForecastItem(from: $0) }
        } else {
            self.forecast = []
        }
    }
}

这样做的好处是,接口字段叫 temp_c 还是 temperature,对上层透明。ViewModel 拿到的就是标准结构,不需要知道原始数据长啥样。

网络请求不该出现在ViewModel

有些开发者喜欢在ViewModel里直接发请求,解析完塞进属性。这会让ViewModel越来越臃肿。正确的做法是,把请求逻辑放在Model层的服务类里。

比如加一个 WeatherService:

class WeatherService {
    func fetchWeather(for city: String, completion: @escaping (WeatherModel?) -> Void) {
        let url = URL(string: "https://api.weather.com/v1/forecast?city=\(city)")!
        URLSession.shared.dataTask(with: url) { data, _, _ in
            guard let data = data,
                  let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                completion(nil)
                return
            }
            let model = WeatherModel(from: json)
            completion(model)
        }.resume()
    }
}

ViewModel只需要调用 service.fetchWeather(...),拿到结果后更新状态就行。职责清晰,测试也方便。

数据转换放Model,别让ViewModel操心

比如温度单位要从摄氏转华氏,有人会在ViewModel里写转换逻辑。其实这属于数据处理,应该由Model提供计算属性:

var temperatureFahrenheit: Double {
    return temperature * 9 / 5 + 32
}

或者日期格式化,也该由Model处理好再输出。ViewModel只管拿数据更新UI,不做加工。

本地缓存也可以归Model管

如果希望离线也能看上次的数据,可以在WeatherService里加一层缓存读取,优先从UserDefaults或数据库取,再异步刷新。这些细节都不需要ViewModel知道。

Model层就像后台服务员,默默把菜做好端出来,前台(ViewModel)只需摆盘上桌,不用关心锅里怎么炒的。