需要帮助使 iOS 14 Widget 显示来自 REST api 的内容

Neo*_*xn0 10 widget swift widgetkit swiftui ios14

最初的问题是在 Apple 论坛上,但没有人可以提供帮助。 https://developer.apple.com/forums/thread/654967?answerId=622833022#622833022

我决定问 SO。

问题

我正在开发一个显示来自 REST api 的内容的小部件扩展。它显示更新的以下股票信息。

我的小部件用户界面

Widgets Code-along,第 3 部分:推进时间线对我没有帮助。

调查给我带来了一个虽然“这是 iOS 测试版的错误”:

public func timeline(
    for configuration: ConfigurationIntent,
    with context: Context,
    completion: @escaping (Timeline<Entry>) -> ()
  ) {
    print("Provider.timeline: ")

    var entries: [SimpleEntry] = []

    let currentDate = Date()
    entries.append(SimpleEntry(
      date: Calendar.current.date(byAdding: .second, value: 15, to: currentDate)!,
      configuration: configuration
    ))
    
    let timeline = Timeline(entries: entries, policy: reloadPolicy)

    completion(timeline)
  }
Run Code Online (Sandbox Code Playgroud)

上面的代码Provider.timeline: 在 1 秒内打印14->19 次。


这是我处理网络请求但没有成功的代码:

public func timeline(
    for configuration: ConfigurationIntent,
    with context: Context,
    completion: @escaping (Timeline<Entry>) -> ()
  ) {
    print("Provider.timeline: ")
    
    fetchStocks { (stocks: [Stock], error: Bool) in
      print("fetchStocks: stocks: ", stocks) 
      completion(getTimeLineFromStocks(stocks: stocks, for: configuration, with: context, reloadPolicy: .atEnd))
    }
}

func getTimeLineFromStocks(
    stocks: [Stock],
    for configuration: ConfigurationIntent,
    with context: Context,
    reloadPolicy: TimelineReloadPolicy
  ) -> Timeline<Entry> {
    var entries: [SimpleEntry] = []
    let currentDate = Date()
    entries.append(SimpleEntry(
      date: Calendar.current.date(byAdding: .second, value: 15, to: currentDate)!,
      configuration: configuration,
      stocks: stocks
    ))
    
    let timeline = Timeline(entries: entries, policy: reloadPolicy)
    
    return timeline
  }


  func fetchStocks(completion: @escaping ([Stock], Bool) -> Void) {
    // Fetch stocks info from API    
    myStockService.getSearchResults(searchTerm: "FIT", perPage: 5) { results, errorMessage in
      if !errorMessage.isEmpty {
        print("--> Search error: " + errorMessage)
        completion([], true)
      } else if results == nil {
        print("--> Search result with ERROR: nil results")
        completion([], true)
      } else {
        print("--> searchResults: ", results)
        completion(results!, false)
        // reloadTimeline()
      }
    }
  }



// ------- MyStockService.swift -------
// If I set breakpoint I can see the list of stocks
func getSearchResults(searchTerm: String,  perPage: Int, completion: @escaping QueryResult) {
    // 1
    dataTask?.cancel()
    
    // 2
    if var urlComponents = URLComponents(string: "https://****************/my-stocks") {
      urlComponents.query = "foo=bar"

      // 3
      guard let url = urlComponents.url else {
        return
      }
    
      // 4
      dataTask = defaultSession.dataTask(with: url) { [weak self] data, response, error in
        defer {
          self?.dataTask = nil
        }
        
        // 5
        if let error = error {
          self?.errorMessage += "DataTask error: " + error.localizedDescription + "\n"
        } else if
          let data = data,
          let response = response as? HTTPURLResponse,
          response.statusCode == 200 {
          
            // update the: self?.resultItems, self?.errorMessage
            self?.updateSearchResults(data, perPage: perPage)
          
          // 6
          DispatchQueue.main.async {
            completion(self?.resultItems, self?.errorMessage ?? "")
          }
        }
      }
      
      // 7
      dataTask?.resume()
    }
  }

  func updateSearchResults(....) {
     ... This fn convert data to [Stock] and assign it to resultItems 
  }

Run Code Online (Sandbox Code Playgroud)

我得到了日志:

Provider.timeline: 
Provider.timeline: 
Provider.timeline: 
Provider.timeline: 
Provider.timeline: 
Provider.timeline: 
Provider.timeline: 
--> Search error: DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled

fetchStocks: stocks:  []
--> Search error: DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled

fetchStocks: stocks:  []
2020-07-23 18:06:38.131476+0700 my-widgetExtension[5315:1272323] libMobileGestalt MobileGestaltCache.c:166: Cache loaded with 4563 pre-cached in CacheData and 53 items in CacheExtra.
--> Search error: DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled

fetchStocks: stocks:  []
Provider.timeline: 
Provider.timeline: 
Provider.timeline: 
--> Search error: DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled

fetchStocks: stocks:  []
--> Search error: DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled
DataTask error: cancelled

fetchStocks: stocks:  []
2020-07-23 18:06:39.751035+0700 my-widgetExtension[5315:1272388] [connection] nw_resolver_start_query_timer_block_invoke [C1] Query fired: did not receive all answers in time for api-t19.24hmoney.vn:443
2020-07-23 18:06:51.891582+0700 my-widgetExtension[5315:1272323] [widget] No intent in timeline(for:with:completion:)
Run Code Online (Sandbox Code Playgroud)

上面的代码不会更新或显示 Stock 到小部件 UI,它有时会发生一些奇怪的崩溃。

任何人都可以帮助我使它工作吗?

Tay*_*son 0

我不是快速网络方面的专家,但在我看来,您有一个实例,dataTask并在发出新请求之前取消它。那么,当您的时间线重新加载时,它是否会取消现有的请求,从而导致您没有任何条目导致它再次重新加载?

Provider如果没有看到您的实施以及重新加载政策,很难说出到底发生了什么。