ios 如何等待从API加载数据,然后在CalendarKit库中创建事件

qmelpv7a  于 2022-11-19  发布在  iOS
关注(0)|答案(1)|浏览(99)

从API提取数据时遇到问题。DayViewCalendar正在从API提取事件数据之前创建View

我的主视图在SwiftUI中

struct CalendarScreen: View {
    @StateObject private var viewModel: ViewModel = ViewModel()
    var body: some View {
        NavigationView {
            ZStack(alignment: .trailing) {
                CalendarKitDisplayView(viewModel: viewModel)
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

我有一个ViewModel,它正在从API获取事件数据

import Combine
import Foundation

extension NSNotification.Name {
    static let onEventLoaded = Notification.Name("onEventLoaded")
}
extension CalendarScreen {
    class ViewModel: ObservableObject {
        let calendarService = CalendarService()
        @Published var calendarEvents: [CalendarEvent]
        var cancellable: AnyCancellable?
        init() {
            self.calendarEvents = [CalendarEvent()]
        }
        func fetchCalendarEvents() {
            cancellable = calendarService.getEvents()
                .sink(
                    receiveCompletion: { _ in },
                    receiveValue: {
                        calendarEvents in self.calendarEvents = calendarEvents
                        NotificationCenter.default.post(name: .onEventLoaded, object: nil)
                    })
        }
    }
}

日历服务只是用于存储库单一化的服务

import Foundation
import Combine
struct CalendarService {
    private var calendarRepository = CalendarRepository()
    func getEvents() -> AnyPublisher<[CalendarEvent], Error> {
        return calendarRepository.getEvents()
    }
}

calendarRepository只是我的API的简单URL请求

import Combine
struct CalendarRepository {
  private let agent = Agent()
  private let calendarurl = "\(api)/calendars_events"
  func getEvents() -> AnyPublisher<[CalendarEvent], Error>{
    let urlString = "\(calendarurl)"
    let url = URL(string: urlString)!
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("Bearer \(AuthManager.shared.token)", forHTTPHeaderField: "Authorization")
    return agent.run(request)
  }
}

代理正在处理请求

class Agent {
    let session = URLSession.shared
    
    var cancelBag: Set<AnyCancellable> = []

    func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<T, Error> {
        return session
            .dataTaskPublisher(for: request)
            .decode(type: T.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
    }

所有内容都将从CalendarKit库进入CalendarViewController,如下所示:

import SwiftUI
import UIKit

class CalendarViewController: DayViewController {
    convenience init(viewModel: CalendarScreen.ViewModel) {
        self.init()
        self.viewModel = viewModel
    }
    var viewModel = CalendarScreen.ViewModel()
    var refresh: Bool = false
    override func viewDidLoad() {
        super.viewDidLoad()
        subscribeToNotification()
    }
    func subscribeToNotification() {
        NotificationCenter.default.addObserver(
            self, selector: #selector(eventChanged(_:)), name: .onDataImported, object: nil)
    }
    @objc func eventChanged(_ notification: Notification) {
        print("notification")
        reloadData()
    }
    override func eventsForDate(_ date: Date) -> [EventDescriptor] {
        // HOW CAN I WAIT FOR THIS LINE TO FINISH FETCH DATA FROM API
        viewModel.fetchCalendarEvents()
        //
        let calendarKitEvents = viewModel.calendarEvents.filter {
            dateTimeFormat.date(from: $0.start) ?? Date() >= date
                && dateTimeFormat.date(from: $0.end) ?? Date() <= date
        }.map { item in
            let event = Event()
            event.dateInterval = DateInterval(
                start: self.dateTimeFormat.date(from: item.start) ?? Date(),
                end: self.dateTimeFormat.date(from: item.end) ?? Date())
            event.color = UIColor(InvoiceColor(title: item.title))
            event.isAllDay = false
            event.text = item.title
            return event
        }
        return calendarKitEvents
    }
    let dateTimeFormat: DateFormatter = {
        let df = DateFormatter()
        df.locale = Locale(identifier: "pl")
        df.timeZone = TimeZone(abbreviation: "CET")
        df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
        return df
    }()
}

SwiftUI和UIKit由UIViewControllerRepresentable桥接

import SwiftUI
import UIKit

struct CalendarKitDisplayView: UIViewControllerRepresentable {
    @ObservedObject var viewModel: CalendarScreen.ViewModel
    func makeUIViewController(context: Context) -> DayViewController {
        let dayViewCalendar = CalendarViewController(viewModel: viewModel)
        return dayViewCalendar
    }
    func updateUIViewController(_ uiViewController: DayViewController, context: Context) {

    }
}

以及编码为CalendarKit事件的实体CalendarEvent

public struct CalendarEvent: Codable, Identifiable {
  public var id: Int = 0
  var title: String = ""
  var start: String = ""
  var end: String = ""
  var note: String?
}

我的目标是等待viewModel.fetchCalendarEvents()从API获取数据,然后启动其他任务。

override func eventsForDate(_ date: Date) -> [EventDescriptor] {
        // HOW CAN I WAIT FOR THIS LINE TO FINISH FETCH DATA FROM API
        viewModel.fetchCalendarEvents()
        //

我尝试使用变量refresh实现NotificationCenter,但当我向CalendarViewController变量var refresh: Bool = false添加和更改函数并将通知推送到ViewModel时

func fetchCalendarEvents() {
            cancellable = calendarService.getEvents()
                .sink(
                    receiveCompletion: { _ in },
                    receiveValue: {
                        calendarEvents in self.calendarEvents = calendarEvents
                        NotificationCenter.default.post(name: .eventChanged, object: nil)
                    })
        }

之后,我在CalendarViewController的init()函数和#选择器中添加了订阅事件,如下所示

@objc func eventChanged(_ notification: Notification) {
        print("notification")
        refresh = true
        reloadData()
    }

我尝试添加,但它停留在无限循环中,变量从未更改

override func eventsForDate(_ date: Date) -> [EventDescriptor] {
        
        viewModel.fetchCalendarEvents()
        while refresh == true {
        }
    }

我正在考虑使用conclusioncompletion处理程序,但我是Swift编程的新手,不知道它应该是什么样子。

f4t66c6m

f4t66c6m1#

使用完成行程常式时,您的函式应该如下所示:

func fetchCalendarEvents(_ completion: @escaping () -> Void) {
    cancellable = calendarService.getEvents()
        .sink(
            receiveCompletion: { _ in },
            receiveValue: {
                calendarEvents in self.calendarEvents = calendarEvents
                NotificationCenter.default.post(name: .eventChanged, object: nil)
                completion()
            })
}

而当调用它时:

fetchCalendarEvents {
    //finished, run some code.
}

相关问题