Move towards having views be initialized with a view model.

- As opposed to being responsible for initializing their own.
This commit is contained in:
CypherPoet 2020-01-06 13:28:43 -06:00
parent 1a183b2f7b
commit ddb4f20990
6 changed files with 95 additions and 54 deletions

View File

@ -13,12 +13,7 @@ struct EditLocationView: View {
@Environment(\.presentationMode) var presentationMode @Environment(\.presentationMode) var presentationMode
@EnvironmentObject var store: AppStore @EnvironmentObject var store: AppStore
@ObservedObject private(set) var viewModel: EditLocationViewModel @ObservedObject var viewModel: EditLocationViewModel
init(location: Location) {
self.viewModel = EditLocationViewModel(location: location)
}
} }
@ -28,12 +23,47 @@ extension EditLocationView {
var body: some View { var body: some View {
NavigationView { NavigationView {
Form { Form {
Section { Section(header: Text("Details").font(.headline)) {
TextField("Location Name", text: Binding($viewModel.location.title, "Untitled Location")) TextField(
// TextField("Description", text: Binding($viewModel.location.longDescription, "")) "Location Name",
TextField("Description", text: Binding($viewModel.location.longDescription, replacingNilWith: "")) text: Binding($viewModel.location.title, "Untitled Location")
)
TextField(
"Description",
text: Binding($viewModel.location.longDescription, replacingNilWith: "")
)
}
Section(header: Text("Add A Photo").font(.headline)) {
Button(action: {
}) {
Group {
// if viewModel.location.userPhoto != nil {
if false {
} else {
HStack {
Spacer()
VStack {
Image(systemName: "camera")
.resizable()
.scaledToFit()
.frame(width: 120)
Text("No Photo Selected")
.font(.title)
}
Spacer()
}
}
}
.frame(height: 200)
}
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
} }
.padding(.bottom)
Section(header: Text("Nearby Locations...").font(.headline)) { Section(header: Text("Nearby Locations...").font(.headline)) {
@ -52,8 +82,6 @@ extension EditLocationView {
.navigationBarItems(trailing: saveButton) .navigationBarItems(trailing: saveButton)
} }
.onAppear { .onAppear {
self.viewModel.store = self.store
self.store.send(.wikiPages(.fetchStateSet(.fetching))) self.store.send(.wikiPages(.fetchStateSet(.fetching)))
self.store.send(WikiPagesSideEffect.fetchPages(near: self.viewModel.location)) self.store.send(WikiPagesSideEffect.fetchPages(near: self.viewModel.location))
} }
@ -87,7 +115,10 @@ struct EditLocationView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
EditLocationView( EditLocationView(
location: SampleData.Locations.santorini viewModel: EditLocationViewModel(
location: SampleData.Locations.santorini,
wikiPagesState: SampleData.SampleAppStore.default.state.wikiPagesState
)
) )
.environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext) .environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext)
.environmentObject(SampleData.SampleAppStore.default) .environmentObject(SampleData.SampleAppStore.default)

View File

@ -14,23 +14,24 @@ import Combine
final class EditLocationViewModel: ObservableObject { final class EditLocationViewModel: ObservableObject {
private var subscriptions = Set<AnyCancellable>() private var subscriptions = Set<AnyCancellable>()
var store: AppStore! {
didSet {
guard store != nil else { return }
self.setupSubscribers()
}
}
@ObservedObject var location: Location @ObservedObject var location: Location
private let wikiPagesState: WikiPagesState
init(location: Location) {
self.location = location
}
// MARK: - Published Outputs // MARK: - Published Outputs
@Published var wikiPagesFetchState: WikiPagesState.FetchState = .inactive @Published var wikiPagesFetchState: WikiPagesState.FetchState = .inactive
// MARK: - Init
init(
location: Location,
wikiPagesState: WikiPagesState
) {
self.location = location
self.wikiPagesState = wikiPagesState
setupSubscribers()
}
} }
@ -38,8 +39,7 @@ final class EditLocationViewModel: ObservableObject {
extension EditLocationViewModel { extension EditLocationViewModel {
private var wikiPagesFetchStatePublisher: AnyPublisher<WikiPagesState.FetchState, Never> { private var wikiPagesFetchStatePublisher: AnyPublisher<WikiPagesState.FetchState, Never> {
store.$state CurrentValueSubject(wikiPagesState.currentFetchState)
.map(\.wikiPagesState.currentFetchState)
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
} }

View File

@ -13,17 +13,12 @@ import MapKit
struct LocationCollectionView: View { struct LocationCollectionView: View {
@EnvironmentObject var store: AppStore @EnvironmentObject var store: AppStore
@ObservedObject private(set) var viewModel: LocationCollectionViewModel @ObservedObject var viewModel: LocationCollectionViewModel
@State private var centerCoordinate = CLLocationCoordinate2D() @State private var centerCoordinate = CLLocationCoordinate2D()
@State var selectedLocation: Location? = nil @State var selectedLocation: Location? = nil
@State private var isShowingEditView = false @State private var isShowingEditView = false
@State private var isShowingSelectedLocationAlert = false @State private var isShowingSelectedLocationAlert = false
init(collection: LocationCollection) {
self.viewModel = LocationCollectionViewModel(collection: collection)
}
} }
@ -49,8 +44,13 @@ extension LocationCollectionView {
} }
) { ) {
if self.selectedLocation != nil { if self.selectedLocation != nil {
EditLocationView(location: self.selectedLocation!) EditLocationView(
.environmentObject(self.store) viewModel: EditLocationViewModel(
location: self.selectedLocation!,
wikiPagesState: self.store.state.wikiPagesState
)
)
.environmentObject(self.store)
} else { } else {
Text("No Location found for editing") Text("No Location found for editing")
} }
@ -59,14 +59,14 @@ extension LocationCollectionView {
Alert( Alert(
title: Text(selectedLocation?.title ?? "Undisclosed Location"), title: Text(selectedLocation?.title ?? "Undisclosed Location"),
message: Text(selectedLocation?.longDescription ?? "No description has been provided yet."), message: Text(selectedLocation?.longDescription ?? "No description has been provided yet."),
primaryButton: .default(Text("Edit"), action: { primaryButton: .default(
self.isShowingEditView = true Text("Edit"),
}), action: { self.isShowingEditView = true }
),
secondaryButton: .cancel(Text("OK")) secondaryButton: .cancel(Text("OK"))
) )
} }
} }
} }
@ -152,7 +152,9 @@ private extension LocationCollectionView {
struct LocationCollectionView_Previews: PreviewProvider { struct LocationCollectionView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
LocationCollectionView(collection: SampleData.LocationCollections.default) LocationCollectionView(
.environmentObject(SampleData.SampleAppStore.default) viewModel: LocationCollectionViewModel(collection: SampleData.LocationCollections.default)
)
.environmentObject(SampleData.SampleAppStore.default)
} }
} }

View File

@ -20,13 +20,6 @@ final class LocationCollectionViewModel: NSObject, ObservableObject {
// MARK: - Published Outputs // MARK: - Published Outputs
@Published var locations: [Location] = [] @Published var locations: [Location] = []
@Published var isShowingEditView: Bool = false
@Published var isShowingSelectedLocationAlert: Bool = false {
didSet {
print("isShowingSelectedLocationAlert - didSet: \(isShowingSelectedLocationAlert)")
}
}
// MARK: - Init // MARK: - Init
@ -34,9 +27,6 @@ final class LocationCollectionViewModel: NSObject, ObservableObject {
self.collection = collection self.collection = collection
super.init() super.init()
// self.fetchedResultsController.delegate = self
// fetchLocations()
} }

View File

@ -22,7 +22,7 @@ extension LocationCollectionsContainerView {
Group { Group {
if viewModel.isAuthenticated { if viewModel.isAuthenticated {
LocationCollectionsListView( LocationCollectionsListView(
buildDestination: LocationCollectionView.init(collection:) buildDestination: LocationCollectionsContainerViewModel.destination(for:)
) )
} else { } else {
authenticationNotice authenticationNotice
@ -83,6 +83,16 @@ extension LocationCollectionsContainerView {
} }
private extension LocationCollectionsContainerView {
func destination(for locationCollection: LocationCollection) -> LocationCollectionView {
let viewModel = LocationCollectionViewModel(collection: locationCollection)
return LocationCollectionView(viewModel: viewModel)
}
}
// MARK: - Preview // MARK: - Preview
struct LocationCollectionsContainerView_Previews: PreviewProvider { struct LocationCollectionsContainerView_Previews: PreviewProvider {

View File

@ -64,9 +64,17 @@ extension LocationCollectionsContainerViewModel {
) )
.store(in: &subscriptions) .store(in: &subscriptions)
} }
static func destination(for locationCollection: LocationCollection) -> LocationCollectionView {
let viewModel = LocationCollectionViewModel(collection: locationCollection)
return .init(viewModel: viewModel)
}
} }
// MARK: - Computed
// MARK: - Computeds
extension LocationCollectionsContainerViewModel { extension LocationCollectionsContainerViewModel {
var authenticationErrorAlertTitle: String { "Authentication Failed" } var authenticationErrorAlertTitle: String { "Authentication Failed" }