This commit is contained in:
John Estropia
2026-07-15 11:50:43 +09:00
parent 7db1cfecfb
commit 890e150b95
135 changed files with 2895 additions and 2526 deletions
@@ -13,51 +13,58 @@ extension Modern.PlacemarksDemo {
// MARK: Geocoder
@MainActor
final class Geocoder {
// MARK: Internal
func geocode(
place: ObjectSnapshot<Modern.PlacemarksDemo.Place>,
completion: @escaping (_ title: String?, _ subtitle: String?) -> Void
) {
place: ObjectSnapshot<Modern.PlacemarksDemo.Place>
) async -> (title: String?, subtitle: String?) {
self.geocoder?.cancelGeocode()
let geocoder = CLGeocoder()
self.geocoder = geocoder
geocoder.reverseGeocodeLocation(
CLLocation(latitude: place.$latitude, longitude: place.$longitude),
completionHandler: { (placemarks, error) -> Void in
defer {
self.geocoder = nil
}
guard let placemark = placemarks?.first else {
return
}
let address = CNMutablePostalAddress()
address.street = placemark.thoroughfare ?? ""
address.subLocality = placemark.subThoroughfare ?? ""
address.city = placemark.locality ?? ""
address.subAdministrativeArea = placemark.subAdministrativeArea ?? ""
address.state = placemark.administrativeArea ?? ""
address.postalCode = placemark.postalCode ?? ""
address.country = placemark.country ?? ""
address.isoCountryCode = placemark.isoCountryCode ?? ""
completion(
placemark.name,
CNPostalAddressFormatter.string(
from: address,
style: .mailingAddress
)
)
defer {
if self.geocoder === geocoder {
self.geocoder = nil
}
)
}
do {
let placemarks = try await geocoder.reverseGeocodeLocation(
CLLocation(latitude: place.$latitude, longitude: place.$longitude)
)
guard let placemark = placemarks.first else {
return (nil, nil)
}
let address = CNMutablePostalAddress()
address.street = placemark.thoroughfare ?? ""
address.subLocality = placemark.subThoroughfare ?? ""
address.city = placemark.locality ?? ""
address.subAdministrativeArea = placemark.subAdministrativeArea ?? ""
address.state = placemark.administrativeArea ?? ""
address.postalCode = placemark.postalCode ?? ""
address.country = placemark.country ?? ""
address.isoCountryCode = placemark.isoCountryCode ?? ""
return (
placemark.name,
CNPostalAddressFormatter.string(
from: address,
style: .mailingAddress
)
)
}
catch {
return (nil, nil)
}
}
// MARK: Private
@@ -3,7 +3,6 @@
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreLocation
import Combine
import CoreStore
import Foundation
import MapKit
@@ -14,7 +13,7 @@ import SwiftUI
extension Modern.PlacemarksDemo {
// MARK: - Modern.PlacemarksDemo.MainView
struct MainView: View {
/**
@@ -38,7 +37,7 @@ extension Modern.PlacemarksDemo {
- Important: `perform(synchronous:)` was used here for illustration purposes. In practice, `perform(asynchronous:completion:)` is the preferred transaction type as synchronous transactions are very likely to cause deadlocks.
*/
private func demoSynchronousTransaction() {
_ = try? Modern.PlacemarksDemo.dataStack.perform(
synchronous: { (transaction) in
@@ -68,41 +67,12 @@ extension Modern.PlacemarksDemo {
print("Commit failed: \(error as Any)")
}
}
// MARK: Internal
@ObjectState(Modern.PlacemarksDemo.placePublisher)
var place: ObjectSnapshot<Modern.PlacemarksDemo.Place>?
init() {
self.sinkCancellable = self.$place?.reactive.snapshot().sink(
receiveCompletion: { _ in
// Deleted, do nothing
},
receiveValue: { [self] (snapshot) in
guard let snapshot = snapshot else {
return
}
self.geocoder.geocode(place: snapshot) { (title, subtitle) in
guard self.place == snapshot else {
return
}
self.demoUnsafeTransaction(
title: title,
subtitle: subtitle,
for: snapshot
)
}
}
)
}
// MARK: View
@@ -130,34 +100,41 @@ extension Modern.PlacemarksDemo {
)
}
}
.navigationBarTitle("Placemarks")
.navigationBarItems(
trailing: Button("Random") {
.task(id: self.place.map({ "\($0.$latitude),\($0.$longitude)" })) {
guard let place = self.place else {
return
}
let geocoded = await self.geocoder.geocode(place: place)
guard self.place?.objectID() == place.objectID() else {
return
}
guard geocoded.title != nil || geocoded.subtitle != nil else {
return
}
self.demoUnsafeTransaction(
title: geocoded.title,
subtitle: geocoded.subtitle,
for: place
)
}
.navigationTitle("Placemarks")
.toolbar {
Button("Random") {
self.demoSynchronousTransaction()
}
)
}
}
// MARK: Private
private var sinkCancellable: AnyCancellable?
private let geocoder = Modern.PlacemarksDemo.Geocoder()
@State
private var geocoder = Modern.PlacemarksDemo.Geocoder()
}
}
#if DEBUG
struct _Demo_Modern_PlacemarksDemo_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Modern.PlacemarksDemo.MainView()
}
}
#endif
@@ -11,8 +11,8 @@ extension Modern {
// MARK: - Modern.PlacemarksDemo
/**
Sample usages for `CoreStoreObject` transactions
*/
Sample usages for `CoreStoreObject` transactions
*/
enum PlacemarksDemo {
// MARK: Internal
@@ -20,6 +20,7 @@ extension Modern {
/**
Sample 1: Setting up the `DataStack` and storage
*/
@MainActor
static let dataStack: DataStack = {
let dataStack = DataStack(
@@ -45,7 +46,8 @@ extension Modern {
)
return dataStack
}()
@MainActor
static let placePublisher: ObjectPublisher<Modern.PlacemarksDemo.Place> = {
let dataStack = Modern.PlacemarksDemo.dataStack