SwiftUI 中来自 BindableObject 的去抖动方法调用
Posted
技术标签:
【中文标题】SwiftUI 中来自 BindableObject 的去抖动方法调用【英文标题】:Debounce method call from BindableObject in SwiftUI 【发布时间】:2019-07-23 16:32:00 【问题描述】:我是 Swift 的新手,对 SwiftUI 更是如此。我开始创建一个小的基本项目。我使用 Github API 来获取存储库列表。
所以我创建了一个“搜索栏”,就像 SwiftUI 没有 SearchBar 组件一样。每次更改我的 Textfield 内容时,我都想执行 fetch 操作。
我不希望过于频繁地调用 fetch 方法。我决定去抖动它。我面临一个问题,我找不到/理解示例。
我尝试实施去抖动解决方案,但它不起作用,我的应用程序崩溃了。
这是我的 BindableObject
import SwiftUI
import Combine
class ReposStore: BindableObject
private var service: GithubService
let didChange = PassthroughSubject<Void, Never>()
@Published var searchText: String = ""
var repos: [Repository] = []
didSet
didChange.send()
var error: String = ""
didSet
didChange.send()
var test: String = ""
didSet
didChange.send()
private var cancellable: AnyCancellable? = nil
init(service: GithubService)
self.service = service
cancellable = AnyCancellable($searchText
.removeDuplicates()
.debounce(for: 2, scheduler: DispatchQueue.main)
.flatMap self.fetch(matching: $0)
.assign(to: \.test, on: self)
)
func fetch(matching query: String = "")
print("### QUERY \(query)")
self.service.getUserRepositories(matching: query) [weak self] result in
DispatchQueue.main.async
print("### RESULT HERE \(result)")
switch result
case .success(let repos): self?.repos = repos
case .failure(let error): self?.error = error.localizedDescription
这是我的观点
import SwiftUI
struct RepositoryList : View
@EnvironmentObject var repoStore: ReposStore
@State private var userName: String = ""
var body: some View
VStack
NavigationView
VStack(spacing: 0)
HStack
Image(systemName: "magnifyingglass").background(Color.blue).padding(.leading, 10.0)
TextField($repoStore.repoUser, placeholder: Text("Search")).background(Color.red)
.padding(.vertical, 4.0)
.padding(.trailing, 10.0)
.border(Color.secondary, width: 1, cornerRadius: 5)
.padding()
List
ForEach(self.repoStore.repos) repository in
NavigationLink(
destination: RepositoryDetail(repository: repository).environmentObject(self.repoStore)
)
RepositoryRow(repository: repository)
.navigationBarTitle(Text("Repositories"))
我尝试使用 Timer 并每 8 秒进行一次调度和操作,但这种方法会导致我的应用程序崩溃。
更多,我真的不知道用“@objc”注释声明一个函数是否是一个好习惯......
有人可以帮我实现一个正确的方法来消除 BindableObject 中的方法吗?
提前谢谢你:)
【问题讨论】:
Combine 已经有一个 debounce 操作符。 谢谢,能不能说的更准确一点? 我会说观看有关 Combine 框架的 WWDC 2019 视频。他们使用的示例与您正在处理的示例完全相同:我们不希望在用户在文本字段中键入时经常执行的网络操作。 谢谢,我会看的:) 所以我用我的新代码编辑了我的问题。我一直在观看 WWDC 2019,但我仍然无法在“flatMap”指令中调用我的异步方法。你能给我一些线索/告诉我我的代码是否更好吗? 【参考方案1】:我终于设法设置了去抖动。
如果它可以帮助某人,这是我的实现:
import SwiftUI
import Combine
class Store : ObservableObject
private var cancellable: AnyCancellable? = nil
@Published var searchText: String= ""
@Published var user: User? = nil
init()
cancellable = AnyCancellable(
$searchText.removeDuplicates()
.debounce(for: 0.8, scheduler: DispatchQueue.main)
.sink searchText in
self.searchUser()
)
func searchUser()
var urlComponents = URLComponents(string: "https://api.github.com/users/\(searchText)")!
urlComponents.queryItems = [
URLQueryItem(name: "access_token", value: EnvironmentConfiguration.shared.github_token)
]
var request = URLRequest(url: urlComponents.url!)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
searchCancellable = URLSession.shared.send(request: request)
.decode(type: User.self, decoder: JSONDecoder())
.map $0
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.assign(to: \.user, on: self)
extension URLSession
func send(request: URLRequest) -> AnyPublisher<Data, URLSessionError>
dataTaskPublisher(for: request)
.mapError URLSessionError.urlError($0)
.flatMap data, response -> AnyPublisher<Data, URLSessionError> in
guard let response = response as? HTTPURLResponse else
return .fail(.invalidResponse)
guard 200..<300 ~= response.statusCode else
return .fail(.serverErrorMessage(statusCode: response.statusCode,
data: data))
return .just(data)
.eraseToAnyPublisher()
enum URLSessionError: Error
case invalidResponse
case serverErrorMessage(statusCode: Int, data: Data)
case urlError(URLError)
extension Publisher
static func empty() -> AnyPublisher<Output, Failure>
return Empty()
.eraseToAnyPublisher()
static func just(_ output: Output) -> AnyPublisher<Output, Failure>
return Just(output)
.catch _ in AnyPublisher<Output, Failure>.empty()
.eraseToAnyPublisher()
static func fail(_ error: Failure) -> AnyPublisher<Output, Failure>
return Fail(error: error)
.eraseToAnyPublisher()
struct User: Hashable, Identifiable, Decodable
var id: Int
var login: String
var avatar_url: URL
var name: String?
enum CodingKeys: String, CodingKey
case id, login, avatar_url, name
【讨论】:
这不是引入了一个保留周期(cancellable
和self
)吗?
你能给我这段代码的完整演示或任何 github 链接吗?以上是关于SwiftUI 中来自 BindableObject 的去抖动方法调用的主要内容,如果未能解决你的问题,请参考以下文章
在 SwiftUI 列表中呈现来自 Realm 的数据的正确方法是啥
在 SwiftUI 列表中呈现来自 Realm 的数据的正确方法是啥