使用最新的 Swift 泛型和 Swift 并发特性实现的单向数据流。要了解更多关于 Swift 中单向数据流的信息,请查看我的专门文章。
struct SearchState: Equatable {
var repos: [Repo] = []
var isLoading = false
}
enum SearchAction: Equatable {
case search(query: String)
case setResults(repos: [Repo])
}
struct SearchReducer: Reducer {
func reduce(oldState: SearchState, with action: SearchAction) -> SearchState {
var state = oldState
switch action {
case .search:
state.isLoading = true
case let .setResults(repos):
state.repos = repos
state.isLoading = false
}
return state
}
}
actor SearchMiddleware: Middleware {
struct Dependencies {
var search: (String) async throws -> SearchResponse
static var production: Dependencies {
.init { query in
guard var urlComponents = URLComponents(string: "https://api.github.com/search/repositories") else {
return .init(items: [])
}
urlComponents.queryItems = [.init(name: "q", value: query)]
guard let url = urlComponents.url else {
return .init(items: [])
}
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(SearchResponse.self, from: data)
}
}
}
let dependencies: Dependencies
init(dependencies: Dependencies) {
self.dependencies = dependencies
}
func process(state: SearchState, with action: SearchAction) async -> SearchAction? {
switch action {
case let .search(query):
let results = try? await dependencies.search(query)
guard !Task.isCancelled else {
return .setResults(repos: state.repos)
}
return .setResults(repos: results?.items ?? [])
default:
return nil
}
}
}
typealias SearchStore = Store<SearchState, SearchAction>
struct SearchContainerView: View {
@State private var store = SearchStore(
initialState: .init(),
reducer: SearchReducer(),
middlewares: [SearchMiddleware(dependencies: .production)]
)
@State private var query = ""
var body: some View {
List(store.repos) { repo in
VStack(alignment: .leading) {
Text(verbatim: repo.name)
.font(.headline)
if let description = repo.description {
Text(verbatim: description)
}
}
}
.redacted(reason: store.isLoading ? .placeholder : [])
.searchable(text: $query)
.task(id: query) {
await store.send(.search(query: query))
}
.navigationTitle("Github Search")
}
}
在 Xcode 中使用其 Github 仓库 URL 添加此 Swift 包。(文件 > Swift Packages > Add Package Dependency...)
v0.3.x - 引入了新的 Observation 框架,仅适用于 iOS 17 和 macOS 14。v0.2.x - 使用以前的版本来针对旧版本的 iOS 和 macOS。
Majid Jabrayilov: cmecid@gmail.com
swift-unidirectional-flow 包是在 MIT 许可证下提供的。 有关更多信息,请参见 LICENSE 文件。