可选协议要求的 SwiftUI 结构体属性不可访问

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

几天来我一直在努力通过此链接扩展 Matteo Manferdinis 网络架构https://matteomanferdini.com/swift-rest-api/#send-data-post-body

由于指南仅显示了如何实现 GET 方法,因此我还需要 POST 数据。所以我向 ApiResource 协议引入了可选的 body 属性:


protocol APIResource {
  associatedtype ModelType: Codable
  var baseUrl:      String            {get}
  var apiVersion:   String            {get}
  var path:         String            {get}
  var method:       HTTPMethod        {get}
  //  var headers:    [String: String]? {get}
  //  var parameters: [String: Any]?    {get}
  var filterStatus: String            {get}
  var body:         ModelType?        {get}
}

enum HTTPMethod: String {
  case get    = "GET"
  case post   = "POST"
}

extension APIResource {
  var filterStatus: String      {get {return ""}}
  var apiVersion:   String      {get {return ""}}
  var body:         ModelType?  {return nil}
  
  var url: URL {
    var components = URLComponents(string: baseUrl)!
    components.path = "\(apiVersion)\(path)"
    
    if filterStatus != "" {
      components.queryItems = [URLQueryItem(name: "status", value: filterStatus)]
    }
    return components.url!
  }
}

然后我构建一个适用于 APIResource 协议的 PostItemStruct 并初始化 body 属性。


struct PostItemResource: APIResource {
  typealias ModelType = ItemType
  var baseUrl      = AWSAPI.baseUrl
  var apiVersion   = AWSAPI.version
  var path         = "/items/"
  var method       = HTTPMethod.post
  var body         : ItemType
  
  init(item: ItemType) {
    self.body  = item
    self.path  = "/items/\(item.id)"
  }
}

我的viewModel使用updateItem()函数来声明资源和请求并调用request.execute()函数。

 func updateItem() async {
    guard let item = item else { return }
    
    let resource = PostItemResource(item: item)
    let request = APIRequest(resource: resource)
    
    errorMessage = nil
    
    do {
      try await request.execute()
    } catch {
      print((error as NSError).localizedFailureReason ?? "unknownError")
    }
  }

request.execute() 函数将检查它是否是 get / 或 post 方法,在这种情况下,它将调用 NetworkRequest 协议中的 upload(resource) 函数。

  func execute() async throws -> (urlResponse: URLResponse, data: ModelType?) {
    switch resource.method {
    case .get:
      try await load(resource.url)
    case .post:
      try await upload(resource)
    }
  }

这给我们带来了一个痛点,我无法再理解发生了什么:

func upload(_ postData: any APIResource) async throws -> (urlResponse: URLResponse, data: ModelType?) {
    var request = URLRequest(url: postData.url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
    print("\n\nFull postData:")
    dump(postData)
    
    print("\n\npostData.body:")
    dump(postData.body)
    
    guard let body = postData.body else {throw NetworkError.invalidInput}
    request.httpBody = try encode(body as! Self.ModelType)
    
    let (data, response) = try await URLSession.shared.data(for: request)
    
    return try (response, decode(data))
  }
}

在这里,我希望 postData.body 包含我要上传的数据。但是转储(postData.body)给了我一个零,并且警卫将我从函数中抛出。 但是转储(postData)给了我一个输出,表明主体不为零。

下面您可以看到控制台,其中包含我的上传功能的两个转储。谁能解释一下为什么 postData.body 在直接寻址时会转换为 nil ?

Full postData:
▿ App.PostItemResource
  - baseUrl: "xxxx"
  - apiVersion: "/beta"
  - path: "/item/498AEF35-844E-467E-A782-E1FFF11D4CCA"
  - method: App.HTTPMethod.post
  ▿ body: App.ItemType
    - id: "498AEF35-844E-467E-A782-E1FFF11D4CCA"
    ▿ version: Optional(0)
      - some: 0
    - type: App.MyType.einTyp
    - status: App.MyStatus.stock


postData.body:
- nil

在架构的多个级别上尝试转储数据,并且在 URLSession 之前它在所有地方都工作正常。这里 postData.body 因不明原因变为 nil。

swift xcode swiftui option-type
1个回答
0
投票

APIResource
需要这个:

var body: ModelType? { get }

PostItemResource
中,
ModelType
ItemType
,所以应该有一个

var body: ItemType? { get }

但是你写道:

var body: ItemType

不满足协议要求。相反,您在

extension
中声明的默认实现可以满足协议要求,它只返回
nil

所以只需将

body
声明为可选,一切都应该没问题。

var body: ItemType?

如果不想让人误将

body
设置为
nil
,可以改为
let
,或者有专门的设置方法:

private(set) var body: ItemType?

func setBody(_ item: ItemType) {
    body = item
}
© www.soinside.com 2019 - 2024. All rights reserved.