-
-
Save ruslankuksa/69dc127093ec0ae5b709a629519cfa72 to your computer and use it in GitHub Desktop.
Composable Architecture Crash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import ComposableArchitecture | |
struct AdminScheduleView: View { | |
let store: Store<UserState, UserAction> | |
init(store: Store<UserState, UserAction>) { | |
self.store = store | |
} | |
var body: some View { | |
IfLetStore(store.scope(state: \.adminState, action: UserAction.adminAction)) { adminStore in | |
WithViewStore(adminStore) { viewStore in | |
ZStack { | |
VStack(alignment: .leading, spacing: 20) { | |
//... | |
HStack { | |
FilterButton { | |
viewStore.send(.filterButtonTapped) | |
} | |
.sheet(isPresented: viewStore.binding(get: \.showFiltersSheet, send: .dismissFiltersSheet).removeDuplicates()) { | |
IfLetStore(adminStore.scope(state: \.filterState, action: AdminAction.filtersAction)) { filterStore in | |
FilterView(store: filterStore) | |
} | |
} | |
Spacer() | |
//... | |
} | |
.padding(.horizontal, 20) | |
} | |
} | |
} | |
} | |
} | |
} | |
struct AdminState: Equatable { | |
var showFiltersSheet: Bool = false | |
var filterState: FiltersState? | |
} | |
let adminReducer = Reducer<AdminState, AdminAction, AdminEnvironment>.combine( | |
filterReducer.optional().pullback(state: \.filterState, | |
action: /AdminAction.filtersAction, | |
environment: { _ in FiltersEnvironment(client: .live, | |
mainQueue: DispatchQueue.main.eraseToAnyScheduler()) } | |
), | |
Reducer { state, action, environment in | |
switch action { | |
case .filterButtonTapped: | |
state.filterState = FiltersState() | |
state.showFiltersSheet = true | |
return .none | |
case .dismissFiltersSheet: | |
state.showFiltersSheet = false | |
state.filterState = nil | |
debugPrint("Dismiss filters") | |
return .none | |
//... | |
} | |
} | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import SwiftUI | |
import QGrid | |
import ComposableArchitecture | |
struct DashboardView: View { | |
let store: Store<UserState, UserAction> | |
init(store: Store<UserState, UserAction>) { | |
self.store = store | |
} | |
var body: some View { | |
WithViewStore(store) { viewStore in | |
ZStack { | |
VStack(alignment: .leading) { | |
Text("Choose your company") | |
.font(.system(size: 28, weight: .heavy)) | |
.foregroundColor(.scPrimaryGray) | |
QGrid(viewStore.user.companies, columns: 2, vSpacing: 15, hSpacing: 15, vPadding: 0, hPadding: 0, isScrollable: true, showScrollIndicators: false) { company in | |
DashboardButton(title: company.role.capitalizeFirst, subtitle: company.name) { | |
viewStore.send(.setCompany(company)) | |
} | |
} | |
Spacer() | |
} | |
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) | |
.padding(.horizontal, 20) | |
.padding(.vertical, 16) | |
ZStack { | |
//Workaround for navigation bug in iOS 14.5 | |
NavigationLink(destination: EmptyView()) { | |
EmptyView() | |
} | |
NavigationLink( | |
destination: DriverScheduleView(store: store), | |
tag: .driver, | |
selection: viewStore.binding(get: \.user.role, | |
send: UserAction.scheduleDismissed) | |
.removeDuplicates() | |
) { | |
EmptyView() | |
} | |
NavigationLink( | |
destination: AdminScheduleView(store: store), | |
tag: .admin, | |
selection: viewStore.binding(get: \.user.role, | |
send: UserAction.scheduleDismissed) | |
.removeDuplicates() | |
) { | |
EmptyView() | |
} | |
} | |
} | |
.navigationBarTitle("", displayMode: .inline) | |
.navigationBarHidden(true) | |
.overlay( | |
viewStore.requestInProgress ? OverlayView() : nil | |
) | |
.onAppear() { | |
viewStore.send(.sendDeviceToken) | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import ComposableArchitecture | |
enum FilterType: String, CaseIterable, Equatable { | |
case all = "All" | |
case quick = "Quick" | |
} | |
enum FiltersAction: Equatable { | |
case setTicketsFilter(FilterType) | |
case setDriverFilter(Driver) | |
case showSearchSheet | |
case fetchDrivers(_ search: String) | |
case applyFilters(FilterType, Driver?) | |
case changeDriverName(String) | |
case fetchDriverResult(Result<[Driver], ApiError>) | |
} | |
struct FiltersEnvironment { | |
var client: TicketCreationClient | |
var mainQueue: AnySchedulerOf<DispatchQueue> | |
} | |
private struct DriverId: Hashable {} | |
struct FiltersState: Equatable, Identifiable { | |
var id: String { | |
UUID().uuidString | |
} | |
var driverName: String = "" | |
var ticketFilter: FilterType = .all | |
var selectedDriver: Driver? | |
var drivers: [Driver] = [] | |
var presentSearchSheet: Bool = false | |
var requestInProgress: Bool = false | |
} | |
let filterReducer = Reducer<FiltersState, FiltersAction, FiltersEnvironment> { state, action, environment in | |
switch action { | |
case .setTicketsFilter(let filter): | |
state.ticketFilter = filter | |
return .none | |
case .setDriverFilter(let driver): | |
let driverName = "\(driver.firstName) \(driver.lastName)" | |
state.selectedDriver = driver | |
state.presentSearchSheet = false | |
return Effect(value: .changeDriverName(driverName)) | |
case .showSearchSheet: | |
state.presentSearchSheet.toggle() | |
return .none | |
case .fetchDrivers(let name): | |
state.requestInProgress = true | |
return environment.client.fetchDrivers(name) | |
.map(\.drivers) | |
.receive(on: environment.mainQueue) | |
.catchToEffect() | |
.debounce(id: DriverId(), for: 0.3, scheduler: environment.mainQueue) | |
.map(FiltersAction.fetchDriverResult) | |
case .fetchDriverResult(.success(let drivers)): | |
state.requestInProgress = false | |
state.drivers = drivers | |
return .none | |
case .fetchDriverResult(.failure(let error)): | |
state.requestInProgress = false | |
debugPrint(error) | |
return .none | |
case .applyFilters(let filter, let driver): | |
return .none | |
case .changeDriverName(let name): | |
state.driverName = name | |
return .none | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import ComposableArchitecture | |
enum UserAction: Equatable { | |
case getCompanies | |
case setCompany(UserCompany) | |
case setCompanyResponse(Result<DefaultResponse, ApiError>) | |
case scheduleDismissed | |
case signOut | |
case driverAction(DriverAction) | |
case adminAction(AdminAction) | |
case sendDeviceToken | |
case sendDeviceTokenResult(Result<DefaultResponse, ApiError>) | |
} | |
struct UserEnvironment { | |
let userServicesClient: UserServicesClient = .liveClient | |
let mainQueue: AnySchedulerOf<DispatchQueue> = DispatchQueue.main.eraseToAnyScheduler() | |
} | |
struct UserState: Equatable { | |
var user: User | |
var driverState: DriverState? | |
var adminState: AdminState? | |
var requestInProgress: Bool = false | |
} | |
let userReducer = Reducer<UserState, UserAction, UserEnvironment>.combine( | |
driverReducer.optional().pullback(state: \.driverState, | |
action: /UserAction.driverAction, | |
environment: { _ in SharedEnvironment.live(environment: DriverEnvironment())} | |
), | |
adminReducer.optional().pullback(state: \.adminState, | |
action: /UserAction.adminAction, | |
environment: { _ in AdminEnvironment(ticketsOperationClient: .live, | |
mainQueue: DispatchQueue.main.eraseToAnyScheduler())} | |
), | |
Reducer { state, action, environment in | |
switch action { | |
case .getCompanies: | |
return .none | |
case .setCompany(let company): | |
state.user.currentCompany = company | |
state.requestInProgress = true | |
return environment.userServicesClient.setUserCompany(company.id, company.role) | |
.receive(on: environment.mainQueue) | |
.catchToEffect() | |
.map(UserAction.setCompanyResponse) | |
case .setCompanyResponse(.success(let response)): | |
state.requestInProgress = false | |
guard let company = state.user.currentCompany else { | |
debugPrint("User doesn't have an active company") | |
return .none | |
} | |
state.user.role = UserRole(rawValue: company.role) | |
switch state.user.role { | |
case .driver: | |
state.driverState = DriverState() | |
case .admin: | |
state.adminState = AdminState() | |
case .scheduler, .user, .none: | |
return .none | |
} | |
return .none | |
case .setCompanyResponse(.failure(let error)): | |
print(error) | |
state.requestInProgress = false | |
return .none | |
case .sendDeviceToken: | |
let value = UserDefaults.standard.bool(forKey: "tokenIsSent") | |
if value { return .none } | |
return environment.userServicesClient.sendDeviceToken() | |
.catchToEffect() | |
.map(UserAction.sendDeviceTokenResult) | |
case .sendDeviceTokenResult(.success(_)): | |
UserDefaults.standard.set(true, forKey: "tokenIsSent") | |
debugPrint("Token has been sent") | |
return .none | |
case .sendDeviceTokenResult(.failure(let error)): | |
debugPrint("Failed sending user device token: \(error)") | |
return .none | |
case .scheduleDismissed: | |
state.user.role = .user | |
state.driverState = nil | |
state.adminState = nil | |
print("Schedule dismissed") | |
return .none | |
case .signOut: | |
return .none | |
//MARK: - Driver actions | |
case .driverAction(_): | |
return .none | |
//MARK: - Admin actions | |
case .adminAction(_): | |
return .none | |
} | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment