我正在构建一个带有滚动视图的应用程序来创建提要。我有一个提供数据的视图模型,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
按钮来加载类,它会再次启动,需要永远加载。
知道我做错了什么吗?如果问题是代码在主线程上运行,有人可以添加一个关于如何使其在后台运行的示例吗?
谢谢
有时在主线程上编码/解码大量数据可能会导致挂起,因为它非常昂贵。您可以将其分派到后台线程,如下所示:
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))
}
您可能还想在数据解码时显示加载程序/进度视图。