Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 14 - Add Geolocation support for user's current location #23

Merged
merged 2 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public struct AddFavoriteLocationsSheet: View {
@EnvironmentObject private var sheetEnvironment: OriginDestinationSheetEnvironment

@State private var search = ""
private let userLocation = UserLocationServices.shared.currentLocation

@FocusState private var isSearchActive: Bool

private var filteredCompletions: [Location] {
let favorites = sheetEnvironment.favoriteLocations
Expand Down Expand Up @@ -48,6 +51,7 @@ public struct AddFavoriteLocationsSheet: View {
Image(systemName: "magnifyingglass")
TextField("Search for a place", text: $search)
.autocorrectionDisabled()
.focused($isSearchActive)
}
.padding(.vertical, 8)
.padding(.horizontal, 12)
Expand All @@ -56,6 +60,31 @@ public struct AddFavoriteLocationsSheet: View {
.padding(.horizontal, 16)

List {
if search.isEmpty, let userLocation = userLocation {
Button(action: {
switch UserDefaultsServices.shared.saveFavoriteLocationData(data: userLocation) {
case .success:
sheetEnvironment.refreshFavoriteLocations()
dismiss()
case let .failure(error):
print(error)
}
}, label: {
HStack {
VStack(alignment: .leading) {
Text(userLocation.title)
.font(.headline)
Text(userLocation.subTitle)
}.foregroundStyle(Color.black)

Spacer()

Image(systemName: "plus")
}

})
}

ForEach(filteredCompletions) { location in
Button(action: {
switch UserDefaultsServices.shared.saveFavoriteLocationData(data: location) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ public struct OriginDestinationSheetView: View {

@State private var search: String = ""

private let userLocation = UserLocationServices.shared.currentLocation

// Sheet States
@State private var isAddSavedLocationsSheetOpen = false
@State private var isFavoriteLocationSheetOpen = false
@State private var isRecentLocationSheetOpen = false
@State private var isFavoriteLocationDetailSheetOpen = false

@FocusState private var isSearchFocused: Bool

// Public initializer
public init() {}

Expand All @@ -45,6 +49,7 @@ public struct OriginDestinationSheetView: View {
Image(systemName: "magnifyingglass")
TextField("Search for a place", text: $search)
.autocorrectionDisabled()
.focused($isSearchFocused)
}
.padding(.vertical, 8)
.padding(.horizontal, 12)
Expand Down Expand Up @@ -125,7 +130,6 @@ public struct OriginDestinationSheetView: View {
}

// swiftlint:enable function_body_length

private func recentsSection() -> some View {
if sheetEnvironment.recentLocations.isEmpty {
return AnyView(EmptyView())
Expand Down Expand Up @@ -162,23 +166,50 @@ public struct OriginDestinationSheetView: View {
}

private func searchResultsSection() -> some View {
ForEach(locationService.completions) { location in
Button(action: {
switch UserDefaultsServices.shared.saveRecentLocations(data: location) {
case .success:
dismiss()
case .failure:
break
}
Group {
ForEach(locationService.completions) { location in
Button(action: {
switch UserDefaultsServices.shared.saveRecentLocations(data: location) {
case .success:
dismiss()
case .failure:
break
}

}, label: {
VStack(alignment: .leading) {
Text(location.title)
.font(.headline)
Text(location.subTitle)
}
})
.buttonStyle(PlainButtonStyle())
}, label: {
VStack(alignment: .leading) {
Text(location.title)
.font(.headline)
Text(location.subTitle)
}
})
.buttonStyle(PlainButtonStyle())
}
}
}

private func currentUserSection() -> some View {
Group {
if let userLocation = userLocation {
Button(action: {
switch UserDefaultsServices.shared.saveRecentLocations(data: userLocation) {
case .success:
dismiss()
case .failure:
break
}

}, label: {
VStack(alignment: .leading) {
Text(userLocation.title)
.font(.headline)
Text(userLocation.subTitle)
}
})
.buttonStyle(PlainButtonStyle())
} else {
EmptyView()
}
}
}

Expand All @@ -191,7 +222,9 @@ public struct OriginDestinationSheetView: View {
.padding(.horizontal, 16)

List {
if search.isEmpty {
if search.isEmpty && isSearchFocused {
currentUserSection()
} else if search.isEmpty {
favoritesSection()
recentsSection()
} else {
Expand All @@ -201,6 +234,9 @@ public struct OriginDestinationSheetView: View {
.onChange(of: search) { searchValue in
locationService.update(queryFragment: searchValue)
}
.onChange(of: isSearchFocused) { value in
print(value)
}

Spacer()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct OriginDestinationView: View {

// Public Initializer
public init() {}

public var body: some View {
VStack {
List {
Expand Down
62 changes: 62 additions & 0 deletions OTPKit/Services/UserLocationServices.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// UserLocationServices.swift
// OTPKit
//
// Created by Hilmy Veradin on 06/07/24.
//

import Foundation
import MapKit

/// `UserLocationServices` responsible for asking permission, and manage current users location
public final class UserLocationServices: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var currentLocation: Location?

public static let shared = UserLocationServices()

var locationManager: CLLocationManager = .init()

override private init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}

public func checkIfLocationServicesIsEnabled() {
DispatchQueue.global().async {
if CLLocationManager.locationServicesEnabled() {
self.checkLocationAuthorization()
}
}
}

private func checkLocationAuthorization() {
switch locationManager.authorizationStatus {
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted, .denied:
// Handle restricted or denied
break
case .authorizedAlways, .authorizedWhenInUse:
locationManager.startUpdatingLocation()
@unknown default:
break
}
}

public func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
DispatchQueue.main.async {
self.currentLocation = Location(
title: "My Location",
subTitle: "Your current location",
latitude: location.coordinate.latitude,
longitude: location.coordinate.longitude
)
}
}

public func locationManagerDidChangeAuthorization(_: CLLocationManager) {
checkLocationAuthorization()
}
}
5 changes: 5 additions & 0 deletions OTPKitDemo/MapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import SwiftUI
public struct MapView: View {
@StateObject private var sheetEnvironment = OriginDestinationSheetEnvironment()

@StateObject private var locationServices = UserLocationServices.shared

static let mockCoordinate = CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275)
static let mockSpan = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)

Expand All @@ -29,6 +31,9 @@ public struct MapView: View {
OriginDestinationView()
.environmentObject(sheetEnvironment)
}
.onAppear {
locationServices.checkIfLocationServicesIsEnabled()
}
}
}

Expand Down
Loading