Ios 如何用多个链接URL请求和请求依赖项填充单个模型对象?

Ios 如何用多个链接URL请求和请求依赖项填充单个模型对象?,ios,swift,swiftui,combine,urlsession,Ios,Swift,Swiftui,Combine,Urlsession,我有一个数据模型对象,我必须填充多个网络请求 我的模型看起来像这样 struct CO2Data: Identifiable, Codable { let id = UUID() let totalCO2: Double let dailyAverage: Int let calendar = Calendar.current let dateString: String let emissionStats: EmissionStats let

我有一个数据模型对象,我必须填充多个网络请求

我的模型看起来像这样

struct CO2Data: Identifiable, Codable {
    let id = UUID()
    let totalCO2: Double
    let dailyAverage: Int
    let calendar = Calendar.current
    let dateString: String
    let emissionStats: EmissionStats

let driveLog: String 
    // It is an ID to get the trip details

    let trip : Trip
    // This is what needs to be filled from the next URL request using driveLog


    enum CodingKeys: String, CodingKey {
        case id
        case totalCO2 = "totCO2"
        case dateString = "stDt"
        case dailyAverage = "avg"
        case emissionStats = "emisStats"
        case drLogRefId = "drLogRefId"
        //Not having case trip here breaks the conformance to codable
        // and results in error
    }
}
// for CO2Data
[
  {
    "stDt": "2019-11-17T16:00:00.000+0000",
    "totCO2": 50,
    "avg": 0,
    "emisStats": {
      "wea": 10,
      "aggDr": 10,
      "tfc": 10,
      "tirePress": 0,
      "ac": 10,
      "seatHeater": 10
    },
    "drLogRefId": "5dc20204199f752c7726a8f0"
  }
]

//for Trip
[
  {
    "id": "5dc20204199f752c7726a8f0",
    "startTime": "...",
    "startLat: "...",
    "startLong: "...",
    "endLat: "...",
    "endLong": "...",
  }
]
这是网络请求和响应的流程

  • 请求获取CO2Data并使用JSONDecoder()对其进行解码

  • 使用获取的CO2数据中的driveLog属性发出另一个网络请求以获取Trip对象

  • 第二次网络请求的结果应添加到trip属性中的CO2Data对象中

  • 我该怎么做呢。甚至可以将行程存储在CO2数据中吗

    我正在使用Combine&URLSession.shared.dataTaskPublisher(for:URLRequest)进行此操作,但我完全可以使用普通的简单数据任务。 有没有像这样处理请求依赖关系的已知方法? 我不使用任何第三方库,希望坚持使用URLSession

    JSON对象如下所示

    struct CO2Data: Identifiable, Codable {
        let id = UUID()
        let totalCO2: Double
        let dailyAverage: Int
        let calendar = Calendar.current
        let dateString: String
        let emissionStats: EmissionStats
    
    let driveLog: String 
        // It is an ID to get the trip details
    
        let trip : Trip
        // This is what needs to be filled from the next URL request using driveLog
    
    
        enum CodingKeys: String, CodingKey {
            case id
            case totalCO2 = "totCO2"
            case dateString = "stDt"
            case dailyAverage = "avg"
            case emissionStats = "emisStats"
            case drLogRefId = "drLogRefId"
            //Not having case trip here breaks the conformance to codable
            // and results in error
        }
    }
    
    // for CO2Data
    [
      {
        "stDt": "2019-11-17T16:00:00.000+0000",
        "totCO2": 50,
        "avg": 0,
        "emisStats": {
          "wea": 10,
          "aggDr": 10,
          "tfc": 10,
          "tirePress": 0,
          "ac": 10,
          "seatHeater": 10
        },
        "drLogRefId": "5dc20204199f752c7726a8f0"
      }
    ]
    
    //for Trip
    [
      {
        "id": "5dc20204199f752c7726a8f0",
        "startTime": "...",
        "startLat: "...",
        "startLong: "...",
        "endLat: "...",
        "endLong": "...",
      }
    ]
    

    根据周围数据的复杂性,查看

    • 操作:您可以指定此操作所依赖的其他操作
    • DispatchGroup:使用
      DispatchGroup.enter()
      DispatchGroup.leave()
      和最后的
      DispatchGroup.notify()
      还可以对所执行的已调度任务的依赖关系建模

    祝你好运

    以下是实现目标的方法。在第一次网络呼叫期间,您需要将
    Trip
    设置为可选,因为您没有要在那里设置的数据。确保在json解码期间正确处理此问题

    然后使用
    flatMap
    进行第二次网络调用。 使用
    zip
    等待两个请求完成,并将结果数据传递给扩展
    CO2Data.init

    完整代码,其中fetchCO2TripData将返回CO2DataTrip

    struct CO2Data: Identifiable, Codable {
        let id = UUID()
        /// TODO: add other properties
        let driveLog: String
        let trip : Trip?
    }
    
    struct Trip: Codable {
        let startTime: String
        let endTime: String
        let startLat: String
        let startLong: String
        let endLat: String
        let endLong: String
    }
    
    func fetchCO2Data() -> AnyPublisher<CO2Data, Error> {
        /// TODO: Replace with actual network request
        Empty().eraseToAnyPublisher()
    }
    
    func fetchTripData(by driveLog: String) -> AnyPublisher<Trip, Error> {
        /// TODO: Replace with actual network request
        Empty().eraseToAnyPublisher()
    }
    
    func fetchCO2TripData() -> AnyPublisher<CO2Data, Error> {
        let co2Publisher = fetchCO2Data().share()
        let tripPublisher = co2Publisher.flatMap { fetchTripData(by: $0.driveLog) }
    
        return co2Publisher
            .zip(tripPublisher)
            .map { CO2Data(co2data: $0, trip: $1) }
            .eraseToAnyPublisher()
    }
    
    extension CO2Data {
        init(co2data: CO2Data, trip: Trip) {
            self.driveLog = co2data.driveLog
            self.trip = trip
        }
    }