调用 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的主要内容,如果未能解决你的问题,请参考以下文章

PowerBI调用API处理地址(一)

用java如何通过api数据接口调用数据

C#调用金数据API

java中怎么调用api数据接口

如何使用服务器端 API 调用返回数据?

Sqlserver如何调用OA api数据结果?