
// MARK: - Properties
private let mapView = NMFMapView()
private let locationManager = CLLocationManager()
private let lottoAPIManager = LottoAPIManager.shared
private var stores: [LottoStore] = []
private var visibleMarkers: [String: NMFMarker] = [:]
private lazy var markerManager = MarkerManager(mapView: mapView)
private let geocodingService = GeocodingService()
private let notificationCenter = UNUserNotificationCenter.current()
private let monitoringRadius: CLLocationDistance = 1000 // 1km 반경
private var monitoredRegions: [CLCircularRegion] = []
fetchAllLottoStores)fetchNearbyLottoStores) func fetchAllLottoStores(completion: @escaping (Result<[LottoStore], Error>) -> Void) {
// 모든 매장을 불러오는 로직 구현
let allStores = CoreDataManager.shared.fetchStores()
completion(.success(allStores))
}
// // 현재 위치 기반 로또 판매점 조회 메서드 추가
// func fetchNearbyLottoStores(latitude: Double,
// longitude: Double,
// radius: Int = 3000,
// completion: @escaping (Result<[LottoStore], Error>) -> Void) {
// print("📡 검색 반경: \(radius)m")
// // 이전 작업 취소
// currentTask?.cancel()
do {
currentTask = Task {
do {
// 1. CoreData에서 전체 데이터 로드
let allStores = CoreDataManager.shared.fetchStores()
let currentLocation = CLLocation(latitude: latitude, longitude: longitude)
// 2. 현재 위치 기준으로 반경 내 판매점 필터링
let currentLocation = CLLocation(latitude: latitude, longitude: longitude)
let nearbyStores = allStores.filter { store in
let nearbyStores = filterStoresByDistance(allStores, currentLocation: currentLocation, radius: Double(radius))
let storeLng = Double(store.longitude ?? "") else {
if Task.isCancelled { return }
}
DispatchQueue.main.async {
completion(.success(nearbyStores))
}
return distance <= Double(radius)
} catch {
if !Task.isCancelled {
DispatchQueue.main.async {
completion(.failure(error))
}
}
// }
}
}
saveStores)fetchStores)class CoreDataManager {
static let shared = CoreDataManager()
private init() {}
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "LottoStore")
container.loadPersistentStores { _, error in
if let error = error {
fatalError("CoreData 로드 실패: \(error)")
}
}
return container
}()
var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
// 판매점 저장
func saveStores(_ stores: [LottoStore]) {
context.perform { [weak self] in
guard let self = self else { return }
do {
// 기존 데이터 삭제 전에 백업
let oldStores = self.fetchStores()
// 기존 데이터 삭제
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "LottoStore")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
// 새로운 데이터 저장
try self.context.execute(deleteRequest)
let storeEntity = NSEntityDescription.insertNewObject(forEntityName: "LottoStore", into: self.context)
// 새로운 데이터 저장
for store in stores {
let storeEntity = NSEntityDescription.insertNewObject(forEntityName: "LottoStore", into: self.context)
storeEntity.setValue(String(store.number), forKey: "id")
storeEntity.setValue(store.name, forKey: "name")
storeEntity.setValue(store.address, forKey: "address")
storeEntity.setValue(store.roadAddress, forKey: "roadAddress")
storeEntity.setValue(store.latitude, forKey: "latitude")
storeEntity.setValue(store.longitude, forKey: "longitude")
storeEntity.setValue(store.number, forKey: "number")
storeEntity.setValue(Date(), forKey: "lastUpdated")
}
try self.context.save()
print("✅ 판매점 데이터 저장 완료: \(stores.count)개")
} catch {
print("❌ CoreData 저장 실패: \(error.localizedDescription)")
// 에러 발생 시 이전 데이터 복구 로직 추가 필요
}
}
}
class MarkerManager: NSObject {
// MARK: - Properties
private let mapView: NMFMapView
private var markers: [NMFMarker] = []
private var clusters: [NMFMarker] = []
// MARK: - Constants
private enum ClusterSize {
static let small = 10
static let medium = 100
static let large = 500
static let clusteringDistance: Double = 100.0 // 클러스터링 거리 (미터)
}
// MARK: - Initialization
init(mapView: NMFMapView) {
self.mapView = mapView
super.init()
setupMapView()
setupMapViewDelegate()
}
private func setupMapView() {
mapView.minZoomLevel = 6
mapView.maxZoomLevel = 18
}
private func setupMapViewDelegate() {
// 지도 줌 레벨 변경 시 클러스터링 업데이트
mapView.addCameraDelegate(delegate: self)
}
// MARK: - Public Methods
func clearMarkers() {
markers.forEach { $0.mapView = nil }
markers.removeAll()
clusters.forEach { $0.mapView = nil }
clusters.removeAll()
}
func removeAllMarkers() {
clearMarkers()
}
// MARK: - Private Methods
func createMarkers(for stores: [LottoStore]) {
clearMarkers()
// 마커 생성 및 즉시 지도에 표시
stores.forEach { store in
if let marker = createMarker(for: store) {
marker.mapView = mapView // 즉시 지도에 표시
markers.append(marker)
}
}
}
func requestLocationAuthorization() {
locationManager.requestAlwaysAuthorization()
}
func startMonitoringStores(_ stores: [LottoStore]) {
print("🔍 LocationManager: 판매점 모니터링 시작...")
// 기존 모니터링 중지
monitoredRegions.forEach { locationManager.stopMonitoring(for: $0) }
monitoredRegions.removeAll()
for store in stores {
guard let latitude = Double(store.latitude ?? ""),
let longitude = Double(store.longitude ?? ""),
let storeId = store.id else {
print("⚠️ 판매점 좌표 오류: \(store.name)")
continue
}
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
앱 실행 시:
판매점 데이터 처리:
LottoAPIManager가 Core Data에서 데이터 로드MarkerManager에 전달마커 표시:
MarkerManager가 받은 데이터로 마커 생성현재 Core Data 모델 로딩 실패 문제가 있습니다:
Failed to load model named LottoStore 오류 발생이 문제를 해결하기 위해서는 Core Data 모델을 올바르게 생성하고 설정해야 합니다.