在滚动视图上显示数据时应用程序速度变慢

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

我正在构建一个带有滚动视图的应用程序来创建提要。我有一个提供数据的视图模型,SwiftUI 显示它。到目前为止,我遵循了一个干净的模式,并且还没有从后端提取数据。我在存储库级别创建了一个

Mocked
文件来解码并返回 JSON 文件。
json
文件存储在本地。

下面是代码

SwiftUI 视图

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, .bottom], 20)
            .onAppear {
                if(isFeedSelected) {
                    viewModel.getFeed()
                } else {
                    viewModel.getClasses()
                }
            }
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    HStack {
                        Button(action: {
                            isFeedSelected = true
                        }) {
                            SelectButton(text: "Feed", isSelected: isFeedSelected == true)
                        }
                        Button(action: {
                            isFeedSelected = false
                        }) {
                            SelectButton(text: "Classes", isSelected: isFeedSelected == false)
                        }
                    }
                }

提供

feed
classes
的视图模型如下”

func getFeed() {
        feedUseCase
            .getFeed()
            .sink { res in
                switch res {
                case .failure(let err):
                    self.state = .error(error: err)
                default:
                    break
                }
            } receiveValue: { [weak self] feedsValue in
                self?.feed = feedsValue
                self?.state = .success
            }
            .store(in: &cancellables)
    }
    
    func getClasses() {
        feedUseCase
            .getClasses()
            .sink { res in
                switch res {
                case .failure(let err):
                    self.state = .error(error: err)
                default:
                    break
                }
            } receiveValue: { [weak self] value in
                self?.classFeed = value
                self?.state = .success
            }
            .store(in: &cancellables)
    }

对于这个例子,我将继续仅显示

feed
部分而不是
classes
,但代码基本相同。

用例文件:


final class FeedUseCase {
    
    static let shared = FeedUseCase()
    
    let repository: FeedRepositoryProtocol
    
    init() {
        self.repository = FeedRepository.shared
    }
    
    func getFeed() -> AnyPublisher<[FeedItemEntity], Error> {
        return repository.getFeed()
            .eraseToAnyPublisher()
    }
    
    func getClasses() -> AnyPublisher<[ClassItemEntity], Error> {
        return repository.getClasses()
            .eraseToAnyPublisher()
    }
    
}

存储库界面:

protocol FeedRepositoryProtocol {
    func getFeed() -> AnyPublisher<[FeedItemEntity], Error>
    func getClasses() -> AnyPublisher<[ClassItemEntity], Error>
}


final class FeedRepository: FeedRepositoryProtocol {
        
    static let shared = FeedRepository()
    
    #if MOCKED
        let service: MockedFeedService
    #else
        let service: FeedService
    #endif
    
    init() {
        
        #if MOCKED
            self.service =  MockedFeedService.shared
        #else
            self.service =  FeedService.shared
        #endif 

    }
    
    func getFeed() -> AnyPublisher<[FeedItemEntity], Error> {
        return service.getFeed().eraseToAnyPublisher()
    }
    
    func getClasses() -> AnyPublisher<[ClassItemEntity], Error> {
        return service.getClasses().eraseToAnyPublisher()
    }
    
    
}

我创建了一个

mocked
方案,因此将
MOCKED
设置为 true 并使用
MockedFeedService

MockedFeedService
看起来像这样:

class MockedFeedService: FeedServiceProtocol {
    
    static let shared = MockedFeedService()

    
    func getFeed() -> Future<[ios_foodloose_app.FeedItemEntity], Error> {
        return Future<[FeedItemEntity], Error> { promise in
            
            guard let url = Bundle(for: MockedFeedService.self).url(forResource: "basic-test-feed-response",withExtension: "json"),
                  let data = try? Data(contentsOf: url) else {
                return promise(.failure(NSError(domain: "", code: 401, userInfo: [ NSLocalizedDescriptionKey: "Error"])))
            }
            
            do {
                let response = try JSONDecoder().decode( [FeedItemEntity].self, from: data)
                promise(.success(response))
            } catch {
                print("JSON Decode error: \(error)")
                promise(.failure(error))
            }
        }
    }

FeedItemEntity
看起来像这样:

struct FeedItemEntity: Codable {
    var ts: Int
    var userName: String
    var userLocation: String
    var userImageUrl: String
    var title: String
    var postImageUrl: String
    var postType: PostType
    var rating: Int
    var comments: CommentsEntity
    var likes: Int
    var hashTag: [String]
}

当我说速度变慢时,该应用程序有 5 分钟无法使用......然后它就可以工作了。如果我点击

classes
按钮来加载类,它会再次启动,需要永远加载。

知道我做错了什么吗?如果问题是代码在主线程上运行,有人可以添加一个关于如何使其在后台运行的示例吗?

谢谢

swift swiftui uiscrollview combine
1个回答
0
投票

有时在主线程上编码/解码大量数据可能会导致挂起,因为它非常昂贵。您可以将其分派到后台线程,如下所示:

do {
    DispatchQueue.global(qos: .background).async {
        // this happens on the background thread
        let response = try JSONDecoder().decode( [FeedItemEntity].self, from: data)
        
        DispatchQueue.main.async {
            // this happens on the main thread
            promise(.success(response))
        }
    }
} catch {
    print("JSON Decode error: \(error)")
    promise(.failure(error))
}

您可能还想在数据解码时显示加载程序/进度视图。

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