swift 任务/异步不在后台运行并阻塞主线程

问题描述 投票:0回答:1

我正在尝试创建一个提要,但现在使用 JSON 中的模拟数据。但是,速度非常慢。

以下是我对 Feed 的看法

struct FeedView: View {
    
    @StateObject private var viewModel = FeedViewModel()
    @State private var isFeedSelected: Bool = true

    var body: some View {
        NavigationView {
            ScrollView {
                LazyVStack {
                    if(isFeedSelected) {
                        ForEach(viewModel.feed) { post in
                            PostItemView(item: post)
                        }
                    } else {
                        ForEach(viewModel.classFeed) { classItem in
                            Text(classItem.userName)
                        }
                    }
                }
            }
            .padding([.top], 10)
            .task {
                if(isFeedSelected) {
                    await viewModel.getFeed()
                } else {
                    await viewModel.getClasses()
                }
            }

ViewModel如下:

final class FeedViewModel: ObservableObject {
    
    var feedUseCase: FeedUseCase

    @Published var feed: [FeedItemEntity] = []
    @Published var classFeed: [ClassItemEntity] = []
    
    @Published var state: FeedState = .na
    
    @Published var hasError: Bool = false

    init() {
        self.feedUseCase = FeedUseCase.shared
        setupErrorSubscription()
    }
    
    func getFeed() async {
        Task.detached {
            let result = await self.feedUseCase.getFeed()
            switch(result) {
            case .success(let feed):
                self.feed = feed
            case .failure(let error):
                self.feed = []
                self.hasError = true
            }
        }
    }
    
    func getClasses() async {
        Task.detached {
            let result = await self.feedUseCase.getClasses()
            switch(result) {
            case .success(let classes):
                self.classFeed = classes
            case .failure(let error):
                self.classFeed = []
                self.hasError = true
            }
        }
        
    }
    
}

用例正在这样做:

enum UseCaseError: Error{
    case networkError, decodingError
}

final class FeedUseCase {
    
    static let shared = FeedUseCase()
    
    let repository: FeedRepositoryProtocol
    
    init() {
        self.repository = FeedRepository.shared
    }
    
    func getFeed() async -> Result<[FeedItemEntity], UseCaseError> {
        do {
            let feed = try await repository.getFeed()
            return .success(feed)
        } catch (let error) {
            switch(error){
                case APIServiceError.decodingError:
                    return .failure(.decodingError)
                default:
                    return .failure(.networkError)
                }
            
        }
        
    }

最后,存储库只是这样做:

func getFeed() async throws -> [FeedItemEntity] {
        return try await service.getFeed()
    }

然后服务如下:

func getFeed() async throws -> [FeedItemEntity] {
        guard let url = Bundle(for: MockedFeedService.self).url(forResource: "basic-test-feed-response",withExtension: "json"),
              let data = try? Data(contentsOf: url) else {
            throw APIServiceError.badUrl
        }
            
        guard let result = try? JSONDecoder().decode([FeedItemEntity].self, from: data) else {
            throw APIServiceError.decodingError
        }
        
        return result
    }

据我观察,

ForEach
循环似乎不断被调用,我认为这可能会影响性能。该应用程序非常非常慢。

我习惯在Android上开发,协程机制通常在后台运行,但似乎在iOS上,那些调用更复杂,或者我只是错过了一些东西。

知道如何让应用程序高效流畅吗?

谢谢

swift asynchronous swiftui async-await
1个回答
0
投票

由于您正在使用 async/await ,加快速度的一种方法是删除旧的合并

ObservableObject
,例如

struct FeedView: View {
    @Environment(\.feedController) var controller // renamed from feedUseCase
    @State private var feed: [FeedItem] = []

    var body: some View {
        ...
        .task { // normally there is an id param for the feed you want to fetch
           do {
               feed = await controller.getFeed()
           catch {
               // it's good practice to set an error message in a state and show it
           }
        }
        ...

如果你让你的控制器不是主要角色,它在后台线程上会更快。使用

EnvironmentKey
作为控制器的原因是这样您可以在预览时将其切换为模拟。

© www.soinside.com 2019 - 2024. All rights reserved.