【问题标题】:SwiftUI MVVM data binding decode JSON with different formats [closed]SwiftUI MVVM 数据绑定解码不同格式的 JSON [关闭]
【发布时间】:2021-06-29 04:12:30
【问题描述】:

我的 API 返回具有 2 种可能的数据集格式的 JSON 数据,我该如何解码?提前非常感谢您

结果.json

format 1:
{
    product_name: "coffee"
    product_code: "100010"
    variant: [
        flavour: [
            {
                flavour_name: "chocolate",
                flavour_code: "C001",
            },
            {
                flavour_name: "vanilla",
                flavour_code: "C002",
            }
        ],
        size: [ { ... }, { ... } ]
    ]
}

format 2:
{
    product_name: "bread",
    product_code: "B123",
    variant: [
        portion: ["quarter", "half", "full"],
        type: ["plain", "wheat", "oat"],
    ]
}

MenuViewModel.swift

class MenuDetailViewModel: ObservableObject, MenuDetailService {
    var apiSession: APIService
    @Published var detaildata: MenuDetailData?
    
    var cancellables = Set<AnyCancellable>()
    
    init(apiSession: APIService = APISession()) {
        self.apiSession = apiSession
    }
    
    func getMenuDetail() {
        let cancellable = self.getMenuDetail(menuCode: menuCode)
            .sink(receiveCompletion: { result in
                switch result {
                case .failure(let error):
                    print("Handle error: \(error)")
                case .finished:
                    break
                }
                
            }) { (detail) in
                self.detaildata = detail.data
        }
        cancellables.insert(cancellable)
    }
    
}

MenuDetailService.swift

protocol MenuDetailService {
    var apiSession: APIService {get}
    
    func getMenuDetail(menuCode: String) -> AnyPublisher<MenuDetailAPIResponse, APIError>
}

extension MenuDetailService {
    
    func getMenuDetail(menuCode: String) -> AnyPublisher<MenuDetailAPIResponse, APIError> {
        return apiSession.request(with: APIEndpoint.menuDetail(menuCode: menuCode))
            .eraseToAnyPublisher()
    }
}

APIService.swift

protocol APIService {
    func request<T: Decodable>(with builder: RequestBuilder) -> AnyPublisher<T, APIError>
}

RequestBuilder.swift

protocol RequestBuilder {
    var urlRequest: URLRequest {get}
}

APISession.swift

struct APISession: APIService {
    func request<T>(with builder: RequestBuilder) -> AnyPublisher<T, APIError> where T: Decodable {
        
        // 1
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        
        // 2
        return URLSession.shared
            .dataTaskPublisher(for: builder.urlRequest)
            // 3
            .receive(on: DispatchQueue.main)
            // 4
            .mapError { _ in .unknown }
            // 5
            .flatMap { data, response -> AnyPublisher<T, APIError> in
                if let response = response as? HTTPURLResponse {
//                    let dataString = String(data: data, encoding: .utf8) {
//                        print("Response data string:\n \(dataString)")
//                    }
                    
                    if (200...299).contains(response.statusCode) {
                        print(String(data: data, encoding: .utf8) ?? "")
                        // 6
                        return Just(data)
                            .decode(type: T.self, decoder: decoder)
                            .mapError {_ in .decodingError}
                            .eraseToAnyPublisher()
                    } else {
                    // 7
                        return Fail(error: APIError.httpError(response.statusCode))
                            .eraseToAnyPublisher()
                    }
                }
                return Fail(error: APIError.unknown)
                        .eraseToAnyPublisher()
            }
            .eraseToAnyPublisher()
    }
}

APIEndpoint.swift

enum APIEndpoint {
    case menuDetail(menuCode: String)
}

extension APIEndpoint: RequestBuilder {
    var urlRequest: URLRequest {
        switch self {
        case .menuDetail(let menuCode):
            guard let url = URL(string: "API_URL/product/detail")
                else {preconditionFailure("Invalid URL format")}
            var request = URLRequest(url: url)
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            request.setValue(Constants.API_TOKEN, forHTTPHeaderField: "Authorization")
            request.httpMethod = "POST"
            
            let body: [String: Any] = ["brand": 1, "city": "", "menu_code": menuCode, "member_phone": ""]
            let rb = try! JSONSerialization.data(withJSONObject: body)
            request.httpBody = rb
            
            return request
        }
    }
}

MenuDetailAPIResponse.swift

struct MenuDetailAPIResponse: Codable {
    let data: MenuDetailData
}

MenuDetailData.swift

struct MenuDetailData: Codable, Identifiable {
    let id = UUID()
    let productName: String
    let productCode: String
    let variant: [MenuVariant]
}

struct MenuVariant: Codable, Identifiable, Hashable {
    let id = UUID()
    let flavour: [MenuFlavour]
    let size: [MenuSize]
}

struct MenuFlavour: Codable, Identifiable, Hashable {
    let id = UUID()
    let flavourName: String
    let flavourCode: String
}

【问题讨论】:

  • 请让您的问题更加集中,这看起来像是一个与 MVVM 或 SwiftUI 无关的 json 解码问题。删除所有与您所询问的内容不直接相关的代码,并更改问题的标题,以便更好地反映您所询问的内容。
  • 如果您有关于如何解码的问题,为什么要将所有这些其他代码包含在各种协议和请求构建器中?此外,您的模型不会映射到第二个变体,并且您没有说明需要如何映射。
  • 尝试使用带有try catch表达式的解码器并为错误“DecodingError.typeMismatch”配置catch块。

标签: ios json swift xcode swiftui


【解决方案1】:

尝试使用可选变量将两个模型合二为一:

struct MenuDetailData: Codable, Identifiable {
    let id = UUID()
    let productName: String
    let productCode: String
    let variant: [MenuVariant]
}

struct MenuVariant: Codable, Identifiable, Hashable {
    let id = UUID()
    let flavour: [MenuFlavour]?
    let size: [MenuSize]?
    let portion: [String]?
    let type: [String]?

}

然后你可以检查收到的数据

if let _ = model.type
if let _ = model.flavour...

等等

【讨论】:

  • 是的,谢谢,感觉我以前试过这个,但没有成功,一定是犯了一些错误。现在可以了,谢谢大佬
猜你喜欢
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多