diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/DevAcademy.xcodeproj/project.pbxproj b/DevAcademy.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index c92e7fe..aa5ef11 --- a/DevAcademy.xcodeproj/project.pbxproj +++ b/DevAcademy.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 84FA93B92A68319800DFC974 /* Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FA93B82A68319800DFC974 /* Properties.swift */; }; BE1E12592AA5C6CB000BA3D8 /* StoreadAsyncImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE1E12582AA5C6CB000BA3D8 /* StoreadAsyncImage.swift */; }; BE38A88A2AA7294400EB5431 /* UserLocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE38A8892AA7294400EB5431 /* UserLocationService.swift */; }; + BE3DE9622AF3A83400F2028D /* CoreDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE3DE9612AF3A83400F2028D /* CoreDataService.swift */; }; BEEF9EC12A67C74E002126F4 /* PlacesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEEF9EC02A67C74E002126F4 /* PlacesService.swift */; }; C05610492A5C78CA007FB970 /* DevAcademyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C05610482A5C78CA007FB970 /* DevAcademyApp.swift */; }; C056104B2A5C78CA007FB970 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C056104A2A5C78CA007FB970 /* RootView.swift */; }; @@ -47,6 +48,7 @@ BE1E12582AA5C6CB000BA3D8 /* StoreadAsyncImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreadAsyncImage.swift; sourceTree = ""; }; BE38A8882AA7285B00EB5431 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; BE38A8892AA7294400EB5431 /* UserLocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserLocationService.swift; sourceTree = ""; }; + BE3DE9612AF3A83400F2028D /* CoreDataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataService.swift; sourceTree = ""; }; BEEF9EC02A67C74E002126F4 /* PlacesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacesService.swift; sourceTree = ""; }; C05610452A5C78CA007FB970 /* DevAcademy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DevAcademy.app; sourceTree = BUILT_PRODUCTS_DIR; }; C05610482A5C78CA007FB970 /* DevAcademyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevAcademyApp.swift; sourceTree = ""; }; @@ -114,6 +116,7 @@ C0B74A2E2A8280D80018D10F /* Services.swift */, BEEF9EC02A67C74E002126F4 /* PlacesService.swift */, BE38A8892AA7294400EB5431 /* UserLocationService.swift */, + BE3DE9612AF3A83400F2028D /* CoreDataService.swift */, ); path = Services; sourceTree = ""; @@ -286,6 +289,7 @@ 84FA93B12A68313E00DFC974 /* PossibleKind.swift in Sources */, C0CAEACB2A80220A008D87C2 /* Coordinator.swift in Sources */, C0B74A2D2A8280C60018D10F /* ObservableObjects.swift in Sources */, + BE3DE9622AF3A83400F2028D /* CoreDataService.swift in Sources */, BE1E12592AA5C6CB000BA3D8 /* StoreadAsyncImage.swift in Sources */, 84FA93B72A68317F00DFC974 /* Point.swift in Sources */, C056104B2A5C78CA007FB970 /* RootView.swift in Sources */, diff --git a/DevAcademy.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DevAcademy.xcodeproj/project.xcworkspace/contents.xcworkspacedata old mode 100644 new mode 100755 diff --git a/DevAcademy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/DevAcademy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist old mode 100644 new mode 100755 diff --git a/DevAcademy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DevAcademy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved old mode 100644 new mode 100755 diff --git a/DevAcademy.xcodeproj/xcshareddata/xcschemes/DevAcademy.xcscheme b/DevAcademy.xcodeproj/xcshareddata/xcschemes/DevAcademy.xcscheme old mode 100644 new mode 100755 diff --git a/DevAcademy/Assets.xcassets/AccentColor.colorset/Contents.json b/DevAcademy/Assets.xcassets/AccentColor.colorset/Contents.json old mode 100644 new mode 100755 diff --git a/DevAcademy/Assets.xcassets/AppIcon.appiconset/Contents.json b/DevAcademy/Assets.xcassets/AppIcon.appiconset/Contents.json old mode 100644 new mode 100755 diff --git a/DevAcademy/Assets.xcassets/Contents.json b/DevAcademy/Assets.xcassets/Contents.json old mode 100644 new mode 100755 diff --git a/DevAcademy/DevAcademyApp.swift b/DevAcademy/DevAcademyApp.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Environment/Coordinator.swift b/DevAcademy/Environment/Coordinator.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Environment/Environment.swift b/DevAcademy/Environment/Environment.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Environment/ObservableObjects.swift b/DevAcademy/Environment/ObservableObjects.swift old mode 100644 new mode 100755 index d113450..cf3c319 --- a/DevAcademy/Environment/ObservableObjects.swift +++ b/DevAcademy/Environment/ObservableObjects.swift @@ -16,7 +16,7 @@ final class ObservableObjects { extension ObservableObjects { convenience init(services: Services) { - let places = PlacesObservableObject(placesService: services.placesService, locationService: services.locationService) + let places = PlacesObservableObject(placesService: services.placesService, locationService: services.locationService, coreDataService: services.coreDataService) self.init( places: places diff --git a/DevAcademy/Environment/ObservableObjects/PlacesObservableObject.swift b/DevAcademy/Environment/ObservableObjects/PlacesObservableObject.swift old mode 100644 new mode 100755 index 90a814e..338926d --- a/DevAcademy/Environment/ObservableObjects/PlacesObservableObject.swift +++ b/DevAcademy/Environment/ObservableObjects/PlacesObservableObject.swift @@ -3,7 +3,7 @@ import CoreLocation final class PlacesObservableObject: ObservableObject { @Published var places: [Place] = [] - + private(set) var favouritePlaces: [Int]? { get { UserDefaults.standard.array(forKey: "favourites") as? [Int] } set { @@ -11,18 +11,20 @@ final class PlacesObservableObject: ObservableObject { updatePlaces() } } - + private var rawPlaces: [Place] = [] { didSet { updatePlaces() } } private let placesService: PlacesService private let locationService: UserLocationService + private let coreDataService: CoreDataService private var lastUpdatedLocation: CLLocation? - - - init(placesService: PlacesService, locationService: UserLocationService) { + + + init(placesService: PlacesService, locationService: UserLocationService, coreDataService: CoreDataService) { self.placesService = placesService self.locationService = locationService + self.coreDataService = coreDataService self.locationService.listenDidUpdateLocation { [weak self] location in DispatchQueue.main.async { @@ -40,11 +42,11 @@ final class PlacesObservableObject: ObservableObject { } } } - + func set(place: Place, favourite setFavourite: Bool) { var favouritePlaces = self.favouritePlaces ?? [] let currentIndex = favouritePlaces.firstIndex(of: place.attributes.ogcFid) - + switch (setFavourite, currentIndex) { case (true, nil): favouritePlaces.append(place.attributes.ogcFid) @@ -53,10 +55,10 @@ final class PlacesObservableObject: ObservableObject { default: return } - + self.favouritePlaces = favouritePlaces } - + // A. Closure variant func fetchPlaces() { placesService.places { result in @@ -70,7 +72,7 @@ final class PlacesObservableObject: ObservableObject { } } } - + // B. Async with checked continuation variant func fetchPlacesWithCheckedContinuation() async { let result = await placesService.placesWithCheckedContinuation() @@ -83,7 +85,7 @@ final class PlacesObservableObject: ObservableObject { print(error) } } - + // C. Async variant @MainActor func fetchPlacesWithAsync() async { @@ -94,7 +96,7 @@ final class PlacesObservableObject: ObservableObject { print(error) } } - + private func updatePlaces() { var regularPlaces = rawPlaces @@ -106,14 +108,14 @@ final class PlacesObservableObject: ObservableObject { guard let lPoint = lPlace.geometry?.cllocation else { return true } - + return lastUpdatedLocation.distance(from: lPoint).magnitude < lastUpdatedLocation.distance(from: rPoint).magnitude } } var presentOnTop: [Place] = [] let favouritePlaces = self.favouritePlaces ?? [] - + regularPlaces.removeAll { place in if favouritePlaces.contains(place.attributes.ogcFid) { presentOnTop.append(place) @@ -122,7 +124,7 @@ final class PlacesObservableObject: ObservableObject { return false } } - + self.places = presentOnTop + regularPlaces } @@ -139,4 +141,12 @@ final class PlacesObservableObject: ObservableObject { self.lastUpdatedLocation = userLocation updatePlaces() } + + func loadNote(forPlace ogcFid: Int) -> String? { + coreDataService.loadNote(forPlace: ogcFid) + } + + func save(note: String?, forPlace ogcFid: Int) { + coreDataService.save(note: note, forPlace: ogcFid) + } } diff --git a/DevAcademy/Info.plist b/DevAcademy/Info.plist old mode 100644 new mode 100755 diff --git a/DevAcademy/Library/Views/MapView.swift b/DevAcademy/Library/Views/MapView.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Library/Views/StoreadAsyncImage.swift b/DevAcademy/Library/Views/StoreadAsyncImage.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/IdentifiableCoordinate.swift b/DevAcademy/Model/IdentifiableCoordinate.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/Kind.swift b/DevAcademy/Model/Kind.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/Place.swift b/DevAcademy/Model/Place.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/Places.swift b/DevAcademy/Model/Places.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/Point.swift b/DevAcademy/Model/Point.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/PossibleKind.swift b/DevAcademy/Model/PossibleKind.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Model/Properties.swift b/DevAcademy/Model/Properties.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/RootView.swift b/DevAcademy/RootView.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Scenes/PlaceDetail/PlaceDetailView.swift b/DevAcademy/Scenes/PlaceDetail/PlaceDetailView.swift old mode 100644 new mode 100755 index 1cd1ef9..a7703e6 --- a/DevAcademy/Scenes/PlaceDetail/PlaceDetailView.swift +++ b/DevAcademy/Scenes/PlaceDetail/PlaceDetailView.swift @@ -5,6 +5,12 @@ struct PlaceDetailView: View { let state: PlaceDetailViewState var body: some View { + TextEditor(text: .init( + get: { state.note ?? "" }, + set: { state.note = $0 }) + ) + .foregroundColor(Color.gray) + .border(Color.red) ScrollView { VStack { HStack(alignment: .center) { @@ -21,6 +27,8 @@ struct PlaceDetailView: View { } } .padding([.horizontal, .bottom]) + + if let placeImageUrl = state.placeImageUrl { AsyncImage(url: placeImageUrl) { image in image @@ -49,6 +57,12 @@ struct PlaceDetailView: View { } .navigationTitle(state.placeTitle) } + .onAppear { + state.loadNote() + } + .onDisappear { + state.saveNoteState() + } } } diff --git a/DevAcademy/Scenes/PlaceDetail/PlaceDetailViewState.swift b/DevAcademy/Scenes/PlaceDetail/PlaceDetailViewState.swift old mode 100644 new mode 100755 index aa7a858..c31bcaa --- a/DevAcademy/Scenes/PlaceDetail/PlaceDetailViewState.swift +++ b/DevAcademy/Scenes/PlaceDetail/PlaceDetailViewState.swift @@ -3,7 +3,8 @@ import MapKit struct PlaceDetailViewState: DynamicProperty { @EnvironmentObject private var placesObject: PlacesObservableObject - + @State var note: String? = nil + private let place: Place init(place: Place) { @@ -36,5 +37,13 @@ struct PlaceDetailViewState: DynamicProperty { } return CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } + + func loadNote() { + note = placesObject.loadNote(forPlace: place.attributes.ogcFid) + } + + func saveNoteState() { + placesObject.save(note: note, forPlace: place.attributes.ogcFid) + } } diff --git a/DevAcademy/Scenes/Places/PlaceRow.swift b/DevAcademy/Scenes/Places/PlaceRow.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Scenes/Places/PlacesView.swift b/DevAcademy/Scenes/Places/PlacesView.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Scenes/Places/PlacesViewState.swift b/DevAcademy/Scenes/Places/PlacesViewState.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Services/CoreDataService.swift b/DevAcademy/Services/CoreDataService.swift new file mode 100644 index 0000000..ecb94a1 --- /dev/null +++ b/DevAcademy/Services/CoreDataService.swift @@ -0,0 +1,27 @@ +// +// CoreDataService.swift +// DevAcademy +// +// Created by Mikoláš Stuchlík on 02.11.2023. +// + +import CoreData + +protocol CoreDataService { + func loadNote(forPlace ogcFid: Int) -> String? + func save(note: String?, forPlace ogcFid: Int) +} + +final class ProductionsCoreDataService: NSObject, CoreDataService { + func loadNote(forPlace ogcFid: Int) -> String? { + nil + } + + func save(note: String?, forPlace ogcFid: Int) { + } +} + +final class MockCoreDataService: CoreDataService { + func loadNote(forPlace ogcFid: Int) -> String? { nil } + func save(note: String?, forPlace ogcFid: Int) { } +} diff --git a/DevAcademy/Services/PlacesService.swift b/DevAcademy/Services/PlacesService.swift old mode 100644 new mode 100755 diff --git a/DevAcademy/Services/Services.swift b/DevAcademy/Services/Services.swift old mode 100644 new mode 100755 index e89e832..e489c85 --- a/DevAcademy/Services/Services.swift +++ b/DevAcademy/Services/Services.swift @@ -3,13 +3,16 @@ import Foundation final class Services { let placesService: PlacesService let locationService: UserLocationService + let coreDataService: CoreDataService init( placesService: PlacesService, - locationService: UserLocationService + locationService: UserLocationService, + coreDateService: CoreDataService ) { self.placesService = placesService self.locationService = locationService + self.coreDataService = coreDateService } } @@ -17,10 +20,12 @@ extension Services { convenience init() { let placesService = ProductionPlacesService() let locationService = ProductionUserLocationService() + let coreDataService = ProductionsCoreDataService() self.init( placesService: placesService, - locationService: locationService + locationService: locationService, + coreDateService: coreDataService ) } } @@ -30,6 +35,7 @@ extension Services { extension Services { static let mock = Services( placesService: MockPlacesService(), - locationService: MockLocationService() + locationService: MockLocationService(), + coreDateService: MockCoreDataService() ) } diff --git a/DevAcademy/Services/UserLocationService.swift b/DevAcademy/Services/UserLocationService.swift old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/doc/l9assignment.md b/doc/l9assignment.md old mode 100644 new mode 100755