Skip to content

Commit

Permalink
Merge branch 'iOS/release' into iOS/task/Rewind-Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
SwiftyJunnos authored Jan 26, 2024
2 parents e659656 + be4b1bf commit fe42a21
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ public final class MapViewController: UIViewController {
self?.drawPolyline(using: points)
}
.store(in: &self.cancellables)

viewModel.state.filteredCoordinate
.receive(on: DispatchQueue.main)
.sink { coordinate in
guard let filteredCoordinate2D = coordinate else { return }
viewModel.trigger(.locationDidUpdated(filteredCoordinate2D))
viewModel.trigger(.locationsShouldRecorded([filteredCoordinate2D]))
}
.store(in: &self.cancellables)
}

// MARK: - Functions: Annotation
Expand Down Expand Up @@ -347,9 +356,7 @@ extension MapViewController: CLLocationManagerDelegate {

let coordinate2D = CLLocationCoordinate2D(latitude: newCurrentLocation.coordinate.latitude,
longitude: newCurrentLocation.coordinate.longitude)

recordJourneyViewModel.trigger(.locationDidUpdated(coordinate2D))
recordJourneyViewModel.trigger(.locationsShouldRecorded([coordinate2D]))
recordJourneyViewModel.trigger(.tenLocationsDidRecorded(coordinate2D))
}

private func handleAuthorizationChange(_ manager: CLLocationManager) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ public final class RecordJourneyViewModel: MapViewModel {
public enum Action {
case locationDidUpdated(CLLocationCoordinate2D)
case locationsShouldRecorded([CLLocationCoordinate2D])
case tenLocationsDidRecorded(CLLocationCoordinate2D)
}

public struct State {
// CurrentValue
public var previousCoordinate = CurrentValueSubject<CLLocationCoordinate2D?, Never>(nil)
public var currentCoordinate = CurrentValueSubject<CLLocationCoordinate2D?, Never>(nil)
public var recordingJourney: CurrentValueSubject<RecordingJourney, Never>
public var recordedCoordinates = CurrentValueSubject<[CLLocationCoordinate2D], Never>([])
public var filteredCoordinate = PassthroughSubject<CLLocationCoordinate2D?, Never>()
}

// MARK: - Properties
Expand Down Expand Up @@ -51,20 +54,157 @@ public final class RecordJourneyViewModel: MapViewModel {
let previousCoordinate = self.state.currentCoordinate.value
self.state.previousCoordinate.send(previousCoordinate)
self.state.currentCoordinate.send(coordinate)

/// ์ €์žฅํ•˜๊ณ ์ž ํ•˜๋Š” ์œ„์น˜ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์— ์ „์†ก
case .locationsShouldRecorded(let coordinates):
Task {
let recordingJourney = self.state.recordingJourney.value
let coordinates = coordinates.map { Coordinate(latitude: $0.latitude, longitude: $0.longitude) }
let coordinates = coordinates.map { Coordinate(latitude: $0.latitude,
longitude: $0.longitude) }
let result = await self.journeyRepository.recordJourney(journeyID: recordingJourney.id,
at: coordinates)
switch result {
case .success(let recordingJourney):
self.state.recordingJourney.send(recordingJourney)
self.state.filteredCoordinate.send(nil)
case .failure(let error):
MSLogger.make(category: .home).error("\(error)")
}
}
/// ์—…๋ฐ์ดํŠธ๋˜๋Š” ์œ„์น˜ ์ •๋ณด๋ฅผ ๋ฐ›์•„์™€ 10๊ฐœ๊ฐ€ ๋  ๊ฒฝ์šฐ ํ•„ํ„ฐ๋ง ํ›„ ์ €์žฅํ•˜๋Š” ๋กœ์ง
case .tenLocationsDidRecorded(let coordinate):
self.filterCoordinate(coordinate: coordinate)
}
}

}

private extension RecordJourneyViewModel {

func calculateDistance(from coordinate1: CLLocationCoordinate2D,
to coordinate2: CLLocationCoordinate2D) -> CLLocationDistance {
let location1 = CLLocation(latitude: coordinate1.latitude,
longitude: coordinate1.longitude)
let location2 = CLLocation(latitude: coordinate2.latitude,
longitude: coordinate2.longitude)
return location1.distance(from: location2)
}

/// ๋ฐฐ์—ด์˜ ์ค‘์•™๊ฐ’์„ ๋„์ถœ
func findMedianFrom(array: [CLLocationDegrees]) -> CLLocationDegrees {
let sortedArray = array.sorted()
let count = sortedArray.count

if count % 2 == 0 {
// Array has even number of elements
let middle1 = sortedArray[count / 2 - 1]
let middle2 = sortedArray[count / 2]
return (middle1 + middle2) / 2.0
} else {
// Array has odd number of elements
return sortedArray[count / 2]
}
}

/// ์œ„๋„ ๋ฐฐ์—ด, ๊ฒฝ๋„ ๋ฐฐ์—ด๋กœ๋ถ€ํ„ฐ ์ค‘์•™๊ฐ’์„ ๋„์ถœ
func medianCoordinate(recordedCoordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {

// 10๊ฐœ์˜ ์œ„๋„ ๊ฐ’ ๋ฐฐ์—ด ์ƒ์„ฑ
let latitudes = recordedCoordinates.map { coord in
coord.latitude
}.sorted()

// 10๊ฐœ์˜ ๊ฒฝ๋„ ๊ฐ’ ๋ฐฐ์—ด ์ƒ์„ฑ
let longitudes = recordedCoordinates.map { coord in
coord.longitude
}.sorted()

return CLLocationCoordinate2D(latitude: findMedianFrom(array: latitudes),
longitude: findMedianFrom(array: longitudes))
}

/// ์œ„๋„ ๋ฐฐ์—ด, ๊ฒฝ๋„ ๋ฐฐ์—ด๋กœ๋ถ€ํ„ฐ ํ‰๊ท ๊ฐ’์„ ๋„์ถœ
func averageCoordinate(recordedCoordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
let recordedLength = recordedCoordinates.count
let initialCoordinate = recordedCoordinates.reduce(CLLocationCoordinate2D(latitude: 0,
longitude: 0)) { result, coordinate in
return CLLocationCoordinate2D(latitude: result.latitude + coordinate.latitude,
longitude: result.longitude + coordinate.longitude)
}
let initialLat = initialCoordinate.latitude
let initialLong = initialCoordinate.longitude
return CLLocationCoordinate2D(latitude: initialLat / Double(recordedLength),
longitude: initialLong / Double(recordedLength))
}

/// ๋‘ ์œ„์น˜์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํ•˜๊ธฐ
func distanceWith(_ coordinate1: CLLocationCoordinate2D,
_ coordinate2: CLLocationCoordinate2D) -> Double {
let location1 = CLLocation(latitude: coordinate1.latitude,
longitude: coordinate1.longitude)
let location2 = CLLocation(latitude: coordinate2.latitude,
longitude: coordinate2.longitude)

return location1.distance(from: location2)
}

/// ํ˜„์žฌ location๋“ค ์ค‘ ํ‰๊ท ์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ€์žฅ ๋จผ ์ง€์ ์„ ์ œ๊ฑฐ
func deleteFarLocation(recordedCoordinates: [CLLocationCoordinate2D],
average: CLLocationCoordinate2D) -> [CLLocationCoordinate2D] {
var coordinates = recordedCoordinates
var maxDistance = 0.0
var maxDistanceIndex = -1
for (index, coordinate) in recordedCoordinates.enumerated() {
let location1 = CLLocation(latitude: coordinate.latitude,
longitude: coordinate.longitude)
let location2 = CLLocation(latitude: average.latitude,
longitude: average.longitude)
let distance = location1.distance(from: location2)
if distance > maxDistance {
maxDistance = distance
maxDistanceIndex = index
}
}
coordinates.remove(at: maxDistanceIndex)
return coordinates

}

func filterCoordinate(coordinate: CLLocationCoordinate2D) {
if let previousCoordinate = self.state.previousCoordinate.value,
calculateDistance(from: previousCoordinate,
to: coordinate) <= 5 {
return
}
var recordedCoords = self.state.recordedCoordinates.value
recordedCoords.append(coordinate)
self.state.recordedCoordinates.send(recordedCoords)

if self.state.recordedCoordinates.value.count >= 10 {

var finalAverage = CLLocationCoordinate2D()

while true {
let average = averageCoordinate(recordedCoordinates: recordedCoords)
let median = medianCoordinate(recordedCoordinates: recordedCoords)

if distanceWith(average,
median) <= 10 {
finalAverage = average
break
} else {
recordedCoords = deleteFarLocation(recordedCoordinates: recordedCoords,
average: average)
}

if recordedCoords.count == 1 {
finalAverage = recordedCoords[0]
break
}
}

self.state.filteredCoordinate.send(finalAverage)
self.state.recordedCoordinates.send([])
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@ public struct UserRepositoryImplementation: UserRepository {
let result = await self.networking.request(UserResponseDTO.self, router: router)
switch result {
case .success(let userResponse):
#if DEBUG
MSLogger.make(category: .network).debug("์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ์œ ์ €๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.")
#endif
if let storedUserID = try? self.storeUUID(userID) {
return .success(storedUserID)
} else {
MSLogger.make(category: .keychain).warning("Keychain์— ์ƒˆ๋กœ์šด ์œ ์ € ์ •๋ณด๋ฅผ ์ €์žฅํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
return .success(userResponse.userID)
}
MSLogger.make(category: .keychain).warning("์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ์œ ์ €๋ฅผ ์ƒ์„ฑํ–ˆ์ง€๋งŒ, Keychain์— ์ €์žฅํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
return .success(userResponse.userID)
case .failure(let error):
return .failure(error)
}
Expand All @@ -66,6 +70,9 @@ public struct UserRepositoryImplementation: UserRepository {

do {
try self.keychain.set(value: userID, account: account)
#if DEBUG
MSLogger.make(category: .keychain).debug("Keychain์— ์„œ๋ฒ„ ์ •๋ณด๋ฅผ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.")
#endif
return userID
} catch {
throw MSKeychainStorage.KeychainError.creationError
Expand Down

0 comments on commit fe42a21

Please sign in to comment.