Move towards having views be initialized with a view model.
- As opposed to being responsible for initializing their own.
This commit is contained in:
parent
1a183b2f7b
commit
ddb4f20990
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,7 +44,12 @@ extension LocationCollectionView {
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if self.selectedLocation != nil {
|
if self.selectedLocation != nil {
|
||||||
EditLocationView(location: self.selectedLocation!)
|
EditLocationView(
|
||||||
|
viewModel: EditLocationViewModel(
|
||||||
|
location: self.selectedLocation!,
|
||||||
|
wikiPagesState: self.store.state.wikiPagesState
|
||||||
|
)
|
||||||
|
)
|
||||||
.environmentObject(self.store)
|
.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(
|
||||||
|
viewModel: LocationCollectionViewModel(collection: SampleData.LocationCollections.default)
|
||||||
|
)
|
||||||
.environmentObject(SampleData.SampleAppStore.default)
|
.environmentObject(SampleData.SampleAppStore.default)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
Loading…
Reference in New Issue