Фоновый поток SwiftUI - PullRequest
0 голосов
/ 18 июня 2020

в моем проекте. Я пытаюсь загрузить некоторые данные аэропорта через вызов API и отобразить их в списке,

пока это происходит в моем представлении SwiftUI, я хочу отобразить простую анимацию загрузки Circle.

Для обеда загрузки я использую кнопку, которая запускает действие и активирует анимацию с помощью @state (isSearching = false или true)

вот мое мнение:

import SwiftUI

struct AirportSearch: View {
    @ObservedObject var dm : DataManager
    @State var ICAOSearsh = ""
    @State var isSearching = false
    var body: some View {

        ZStack {
            backgroundGradiet.edgesIgnoringSafeArea(.all)
            if self.isSearching == false{
            LottieView(filename: "planeAnimation")
            }
            VStack{


                CustomTextField(placeholder: Text(" Search ICAO").foregroundColor(.white), text: $ICAOSearsh)
                    .padding(.horizontal , 10)
                    .frame(height: 40.0)
                    .border(Color.black.opacity(5.0), width: 1)
        \\ Button HERE________________________________________________________________        
                Button(action: {
                    self.isSearching = true
                    self.dm.searchAirportOnline(icaoCode: self.ICAOSearsh)
                    {
                        debugPrint("finito search")
                        self.isSearching = false
                    }
                }) {
                    HStack{
                    Image(systemName: "magnifyingglass").foregroundColor(Color.white)
                    Text("Find your airport").foregroundColor(.white)
                    }
                }
                \\ button above HERE________________________________________________________   
                if isSearching == false{
                List{
                    ForEach(self.dm.airportVector) { aeroporto in

                        VStack{
                            HStack{
                                Image(systemName: "airplane").foregroundColor(.red)
                                Text(aeroporto.aptShortName).bold()
                                Spacer()
                                Text(aeroporto.aptICAO)
                                Text("/")
                                Text(aeroporto.aptIATA)
                            }

                            HStack{
                                Text(aeroporto.countryCode)
                                Spacer()
                                Text(aeroporto.continent)
                            }.font(.system(size: 13))
                        }

                        .foregroundColor(.white)
                    }.padding(.horizontal)
                }.onAppear {
                        UITableView.appearance().backgroundColor = UIColor.clear
                        UITableViewCell.appearance().backgroundColor = .clear
                }
                } else {
                    Spacer()
                    CircleLoading()
                    Spacer()
                }

            }
        }           
    }

    var backgroundGradiet: LinearGradient {
        let gradient = LinearGradient(gradient: Gradient(colors: [Color.blue,Color.black]), startPoint: .topTrailing, endPoint: .bottomLeading)

        return gradient
    }

}

к сожалению когда я нажимаю кнопку, анимация останавливается.

для решения этой проблемы я пытаюсь использовать DispatchQueue.global (). asyn c {} и DispatchQueue.main.asyn c {} на мой класс DataManager, но я получаю предупреждение:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates```

здесь, ниже моего класса:

import UIKit
import Combine
import SwiftUI
import CoreLocation


class DataManager: ObservableObject {
    let objectWillChange = PassthroughSubject<Void,Never>()

    static let shared = DataManager()

    @Published var airportVector : [AirportModel] = []
        {
        didSet {
            objectWillChange.send()
        }
    }

    init() {

    }

    // MARK: - Search Online ICAO apt
    func searchAirportOnline(icaoCode: String, closure : @escaping () ->()) {
     DispatchQueue.global().async {
        self.airportVector = [] // empty the vector to
        // set the request apiKey
        let headers : HTTPHeaders = [
            "x-rapidapi-host": "aerodatabox.p.rapidapi.com",
            "x-rapidapi-key": "18df152ef8msh5e365520639e47ep1fb24bjsn8e65c311e1f7"
        ]
        let linkRequest =  "https://aerodatabox.p.rapidapi.com/airports/icao/\(icaoCode)?withTime=true&withRunways=false"
        // make request via AlamoFire
        AF.request(linkRequest, method: .get, headers: headers)
            .responseData { (responseData) in
                switch responseData.result {
                case .success(let value ) :

                    let json = JSON(value)
                    var airport = AirportModel(aptICAO: "", aptIATA: "", aptShortName: "", aptFullName: "", aptMunicipalName: "", locationLat: "", locationLon: "", countryName: "", countryCode: "", continent: "", timeZone: "", linkLiveATC: "", linkLinkFlightRadar: "", airportWebsite: "", currentTimeUTC: "", currentTimeLocal: "", runway: [RunwayModel(name1: RunwayModel.Runway(lengthM: "", lengthF: "", hasLighting: false, widthM: "", widthF: "", surface: "", isClosed: false, name: "", trueHdg: "", displacedThresholdM: "", displacedThresholdF: "", locationLat:0.0, locationLong: 0.0), name2: RunwayModel.Runway(lengthM: "", lengthF: "", hasLighting: false, widthM: "", widthF: "", surface: "", isClosed: false, name: "", trueHdg: "", displacedThresholdM: "", displacedThresholdF: "", locationLat:0.0, locationLong: 0.0))])
                    airport.aptICAO = json["icao"].stringValue
                    airport.aptIATA = json["iata"].stringValue
                    airport.aptShortName = json["shortName"].stringValue
                    airport.aptFullName = json["fullName"].stringValue
                    airport.aptMunicipalName = json["municipalityName"].stringValue
                    airport.locationLat = json["location"]["lat"].stringValue
                    airport.locationLon = json["location"]["lon"].stringValue
                    airport.countryName = json["country"]["name"].stringValue
                    airport.countryCode = json["country"]["code"].stringValue
                    airport.continent = json["continent"]["name"].stringValue
                    airport.timeZone = json["timeZone"].stringValue
                    airport.linkLiveATC = json["urls"]["liveAtc"].stringValue
                    airport.linkLinkFlightRadar = json["urls"]["flightRadar"].stringValue
                    airport.airportWebsite = json["urls"]["webSite"].stringValue
                    airport.currentTimeUTC = json["currentTime"]["utcTime"].stringValue
                    airport.currentTimeLocal = json["currentTime"]["localTime"].stringValue
                    self.runwayRequest(airportICAO: icaoCode) { (vettoreRunwayModel) in
                        airport.runway = vettoreRunwayModel
                    }

                 DispatchQueue.main.async { // ERROR LOOKS HERE.. 
                    self.airportVector.append(airport) // aggiungo ad airport vector l'aeroporto trovato

                    closure()
                                        }
                case.failure(let error) :
                    debugPrint(error)
                    debugPrint("cazzoo")

                }
        }
         }
    }
}

ошибка смотрит на self.airportVector.append, потому что это @Published var и я не знаю, как решить проблему ..

спасибо за помощь,

1 Ответ

0 голосов
/ 18 июня 2020

Попробуйте это

func searchAirportOnline(icaoCode: String, closure : @escaping () ->()) {
    self.airportVector = [] // empty the vector to
    // set the request apiKey
    let headers : HTTPHeaders = [
        "x-rapidapi-host": "aerodatabox.p.rapidapi.com",
        "x-rapidapi-key": "18df152ef8msh5e365520639e47ep1fb24bjsn8e65c311e1f7"
    ]
    let linkRequest =  "https://aerodatabox.p.rapidapi.com/airports/icao/\(icaoCode)?withTime=true&withRunways=false"
    // make request via AlamoFire
    AF.request(linkRequest, method: .get, headers: headers)
        .responseData { (responseData) in
            switch responseData.result {
            case .success(let value ) :

                let json = JSON(value)
                var airport = AirportModel(aptICAO: "", aptIATA: "", aptShortName: "", aptFullName: "", aptMunicipalName: "", locationLat: "", locationLon: "", countryName: "", countryCode: "", continent: "", timeZone: "", linkLiveATC: "", linkLinkFlightRadar: "", airportWebsite: "", currentTimeUTC: "", currentTimeLocal: "", runway: [RunwayModel(name1: RunwayModel.Runway(lengthM: "", lengthF: "", hasLighting: false, widthM: "", widthF: "", surface: "", isClosed: false, name: "", trueHdg: "", displacedThresholdM: "", displacedThresholdF: "", locationLat:0.0, locationLong: 0.0), name2: RunwayModel.Runway(lengthM: "", lengthF: "", hasLighting: false, widthM: "", widthF: "", surface: "", isClosed: false, name: "", trueHdg: "", displacedThresholdM: "", displacedThresholdF: "", locationLat:0.0, locationLong: 0.0))])
                airport.aptICAO = json["icao"].stringValue
                airport.aptIATA = json["iata"].stringValue
                airport.aptShortName = json["shortName"].stringValue
                airport.aptFullName = json["fullName"].stringValue
                airport.aptMunicipalName = json["municipalityName"].stringValue
                airport.locationLat = json["location"]["lat"].stringValue
                airport.locationLon = json["location"]["lon"].stringValue
                airport.countryName = json["country"]["name"].stringValue
                airport.countryCode = json["country"]["code"].stringValue
                airport.continent = json["continent"]["name"].stringValue
                airport.timeZone = json["timeZone"].stringValue
                airport.linkLiveATC = json["urls"]["liveAtc"].stringValue
                airport.linkLinkFlightRadar = json["urls"]["flightRadar"].stringValue
                airport.airportWebsite = json["urls"]["webSite"].stringValue
                airport.currentTimeUTC = json["currentTime"]["utcTime"].stringValue
                airport.currentTimeLocal = json["currentTime"]["localTime"].stringValue
                self.runwayRequest(airportICAO: icaoCode) { (vettoreRunwayModel) in
                    DispatchQueue.main.async {
                        airport.runway = vettoreRunwayModel
                    }
                }

                DispatchQueue.main.async {
                    self.airportVector.append(airport) // aggiungo ad airport vector l'aeroporto trovato
                    closure()
                }
            case.failure(let error) :
                debugPrint(error)
                debugPrint("cazzoo")

            }
    }
}
...