调用 API 数据 Swift/SwiftUI
Posted
技术标签:
【中文标题】调用 API 数据 Swift/SwiftUI【英文标题】:Calling API Data Swift/SwiftUI 【发布时间】:2020-08-05 00:31:10 【问题描述】:我对 Swift 和 SwiftUI 还很陌生,谁能帮我理解为什么当我尝试使用 fecthUserSeries 函数调用特定的电视节目时,什么都没有出现?
这里是端点
import UIKit
struct AppData
static let baseUrl = "https://api.tvmaze.com"
static let search = "/search/shows"
static let show = "/show/"
static let shows = "/shows/"
static let episodes = "/episodes"
static let season = "/seasons"
static let cast = "/cast"
static let crew = "/crew"
static let placeholderUrl = "http://via.placeholder.com/350/ffffff/000000?text=Image+Not+found"
static let dateFormat = "dd MMM yyyy"
static let dateFormatApi = "yyyy-MM-dd"
enum AppError : Error
case invalidFormat
所有功能如下:
import SwiftUI
import Combine
enum APIType
case listSeries
case listseasons
case listEpisodes
case listCast
case listCrew
class APIStore: ObservableObject
@Published var serieses: [Series] = []
@Published var seasons: [Season] = []
@Published var episodes: [Episode] = []
@Published var casts: [CastCrew] = []
@Published var crews: [CastCrew] = []
init()
serieses = []
seasons = []
episodes = []
casts = []
crews = []
func fetchSeries(pageNumber: Int = 1)
let params = [
("page", String(pageNumber))
]
ApiMapper().callAPI(withPath: AppData.show, params: params, andMappingModel: [Series].self) [weak self] (result) in
switch(result)
case .success(let serieses):
DispatchQueue.main.async
self?.serieses = serieses
case .failure(_):
break
func searchSeries(searchString: String)
let params = [
("q", searchString)
]
ApiMapper().callAPI(withPath: AppData.search, params: params, andMappingModel: [SearchResult].self) [weak self] (result) in
switch(result)
case .success(let searchResult):
DispatchQueue.main.async
self?.serieses = searchResult.compactMap($0.series)
case .failure(_):
break
func fetchSeason(with seriesID: Int)
let path = "\(AppData.shows)\(seriesID)\(AppData.season)"
ApiMapper().callAPI(withPath: path, params: [], andMappingModel: [Season].self) [weak self] (result) in
switch(result)
case .success(let seasons):
DispatchQueue.main.async
self?.seasons = seasons
case .failure(_):
break
func fetchEpisodes(with seriesID: Int)
let path = "\(AppData.shows)\(seriesID)\(AppData.episodes)"
ApiMapper().callAPI(withPath: path, params: [], andMappingModel: [Episode].self) [weak self] (result) in
switch(result)
case .success(let episodes):
DispatchQueue.main.async
self?.episodes = episodes
case .failure(_):
break
func fetchCasts(with seriesID: Int)
let path = "\(AppData.shows)\(seriesID)\(AppData.cast)"
ApiMapper().callAPI(withPath: path, params: [], andMappingModel: [CastCrew].self) [weak self] (result) in
switch(result)
case .success(let casts):
DispatchQueue.main.async
self?.casts = casts
case .failure(_):
break
func fetchCrews(with seriesID: Int)
let path = "\(AppData.shows)\(seriesID)\(AppData.crew)"
ApiMapper().callAPI(withPath: path, params: [], andMappingModel: [CastCrew].self) [weak self] (result) in
switch(result)
case .success(let casts):
DispatchQueue.main.async
self?.crews = casts
case .failure(_):
break
func fetchUserSeries(seriesID: Int)
let path = "\(AppData.shows)\(seriesID)"
ApiMapper().callAPI(withPath: path, params: [], andMappingModel: [Series].self) [weak self] (result) in
switch(result)
case .success(let userSeries):
DispatchQueue.main.async
self?.serieses = userSeries
case .failure(_):
print("Error")
break
import UIKit
class ApiMapper
//MARK: Api Calls
func callAPI<T: Codable>(withPath pathString: String, params : [(String, String)], andMappingModel model: T.Type, callback: @escaping (Result<T, Error>) -> Void )
if let url = self.generateURL(withPath: pathString , andParams: params)
let task = URLSession.shared.dataTask(with: url) (data, response, error) in
do
let jsonDecoder = JSONDecoder()
let responseModel = try jsonDecoder.decode(model, from: data!)
callback(Result.success(responseModel))
catch
callback(Result.failure(error))
task.resume()
else
callback(Result.failure(URLError(.badURL)))
//MARK: helper methods
private func generateURL(withPath path: String, andParams params: [(String, String)]) -> URL?
guard var urlComp = URLComponents(string: AppData.baseUrl) else return nil
urlComp.queryItems = [URLQueryItem]()
for param in params
urlComp.queryItems?.append(URLQueryItem(name: param.0, value: param.1))
guard var url = urlComp.url else return nil
url = url.appendingPathComponent(path)
return url
最后,这里是我调用函数的地方。如您所见,我正在调用 index = 1 的节目(fetchSeries 函数是一个单独的函数,它从数据库的每个页面中检索所有节目)
import SDWebImageSwiftUI
struct HomeView: View
@ObservedObject var apiStore = APIStore()
@State private var searchString = ""
@State private var pageNumber = 1
@State var selectedSeason: Int
// var seriesID: [Int] = [178, 31, 555, 1855, 263]
var seriesID: Int = 1
init(selectedSeason: Int)
UITableView.appearance().separatorStyle = .none
_selectedSeason = State(initialValue: selectedSeason)
var body: some View
NavigationView()
VStack
TextField("Search", text: $searchString, onEditingChanged: status in
if !status && self.searchString != ""
self.apiStore.searchSeries(searchString: self.searchString)
self.pageNumber = 1
else if !status && self.searchString == ""
self.apiStore.fetchSeries(pageNumber: self.pageNumber)
)
.padding(.all, 10.0)
.textFieldStyle(RoundedBorderTextFieldStyle())
List
ForEach(self.apiStore.serieses) series in
SeriesCell(series : self.apiStore.serieses[0], selectedSeason: self.$selectedSeason)
.onAppear
// ForEach(self.seriesID.) seriesID in
// if self.seriesID.contains(where: $0 == series.id)
// self.apiStore.fetchUserSeries(with: series.id ?? 0)
//
self.apiStore.fetchUserSeries(with: self.seriesID)
if series.id == self.apiStore.serieses.last?.id
self.pageNumber += 1
self.apiStore.fetchSeries(pageNumber: self.pageNumber)
.navigationBarTitle("Shows", displayMode: .inline)
.onAppear
self.searchString == "" ? self.apiStore.fetchSeries() : self.apiStore.searchSeries(searchString: self.searchString)
// self.apiStore.fetchUserSeries()
// self.searchString == "" ? self.apiStore.fetchSeries() : self.apiStore.searchSeries(searchString: self.searchString)
struct SeriesCell: View
var series: Series
@Binding var selectedSeason: Int
var body: some View
NavigationLink(destination: DetailsView(series: series, selectedSeason: selectedSeason))
ZStack(alignment: .topTrailing)
VStack(alignment: .center)
WebImage(url: URL(string: (series.image?.original ?? "")))
.placeholderImage(systemName: "camera")
.resizable()
.scaledToFit()
.frame(minWidth: UIScreen.main.bounds.width - 60, minHeight:(UIScreen.main.bounds.width - 60))
HStack
Text(String(series.name ?? ""))
.bold()
.multilineTextAlignment(.center)
.font(.title)
.padding(10)
Image(systemName: "plus")
.onTapGesture
ZStack(alignment: .center)
Image(systemName: "star.fill")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(Color.yellow)
.padding()
Text(String.localizedStringWithFormat("%.1f", series.rating?.average ?? 0))
.font(.system(size: 11))
.bold()
.background(Color.white)
.cornerRadius(6)
.shadow(radius: 5)
struct ContentView_Previews: PreviewProvider
static var previews: some View
let content = HomeView(selectedSeason: 1)
content.apiStore.serieses = SampleAPIResult.getDummySeries()
return content
// .previewDevice("iPhone 7")
```
【问题讨论】:
欢迎来到 Stack Overflow。您粘贴了很多代码,大多数用户都不会费心阅读所有代码。我建议您使用 Xcode 上的断点功能来确定正在命中的代码行,并使用 print() 函数来打印变量的值以查看它们是否被设置。缩小错误范围并向我们展示有问题的代码,而不是整个项目。 【参考方案1】:在 APIStore 类中试试这个:
func fetchUserSeries(seriesID: Int)
到
func fetchUserSeries(with seriesID: Int)
添加“with”作为外部参数
【讨论】:
以上是关于调用 API 数据 Swift/SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章