diff --git a/Mage/Localizable.xcstrings b/Mage/Localizable.xcstrings index 71ba3521..0b9aebea 100644 --- a/Mage/Localizable.xcstrings +++ b/Mage/Localizable.xcstrings @@ -276,6 +276,9 @@ } } }, + "Delete" : { + "comment" : "Alert delete button" + }, "Downloaded %@ of %@" : { "localizations" : { "en" : { diff --git a/Mage/Mixins/MapDirectionsMixin.swift b/Mage/Mixins/MapDirectionsMixin.swift index dbf68f7d..8b40b047 100644 --- a/Mage/Mixins/MapDirectionsMixin.swift +++ b/Mage/Mixins/MapDirectionsMixin.swift @@ -30,7 +30,6 @@ class MapDirectionsMixin: NSObject, MapMixin { @Injected(\.feedItemRepository) var feedItemRepository: FeedItemRepository - @Injected(\.observationImageRepository) var imageRepository: ObservationImageRepository var directionsToItemObserver: Any? @@ -49,7 +48,8 @@ class MapDirectionsMixin: NSObject, MapMixin { var feedItemFetchedResultsController: NSFetchedResultsController? private var timer: Timer? - init(mapDirections: MapDirections, viewController: UIViewController, mapStack: UIStackView?, scheme: MDCContainerScheming?, locationManager: CLLocationManager? = nil, sourceView: UIView? = nil) { + init(mapDirections: MapDirections, viewController: UIViewController, mapStack: UIStackView?, scheme: MDCContainerScheming?, locationManager: CLLocationManager? = nil, sourceView: UIView? = nil, imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl.shared) { + self.imageRepository = imageRepository self.mapDirections = mapDirections self.mapView = mapDirections.mapView self.viewController = viewController diff --git a/Mage/Model/Observation/ObservationMapItem.swift b/Mage/Model/Observation/ObservationMapItem.swift index 72ff6e29..285ea14d 100644 --- a/Mage/Model/Observation/ObservationMapItem.swift +++ b/Mage/Model/Observation/ObservationMapItem.swift @@ -32,6 +32,7 @@ struct ObservationMapItem: Equatable, Hashable { var error: Bool = false var syncing: Bool = false var important: ObservationImportantModel? + var iconPath: String? var coordinate: CLLocationCoordinate2D? { guard let geometry = geometry, let point = geometry.centroid() else { @@ -76,22 +77,10 @@ struct ObservationMapItem: Equatable, Hashable { ) ) } - - var iconPath: String? { - @Injected(\.observationImageRepository) - var imageRepository: ObservationImageRepository - - return imageRepository.imageName( - eventId: eventId, - formId: formId, - primaryFieldText: primaryFieldText, - secondaryFieldText: secondaryFieldText - ) - } } extension ObservationMapItem { - init(observation: ObservationLocation) { + init(observation: ObservationLocation, imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl.shared) { self.observationId = observation.observation?.objectID.uriRepresentation() self.observationLocationId = observation.objectID.uriRepresentation() self.formId = Int(observation.formId) @@ -106,6 +95,12 @@ extension ObservationMapItem { self.minLongitude = observation.minLongitude self.primaryFieldText = observation.primaryFieldText self.secondaryFieldText = observation.secondaryFieldText + self.iconPath = imageRepository.imageName( + eventId: eventId, + formId: formId, + primaryFieldText: primaryFieldText, + secondaryFieldText: secondaryFieldText + ) // TODO: should we store the primary and secondary feed field text too? if let observation = observation.observation { let style = ObservationShapeStyleParser.style( diff --git a/Mage/Observation/ObservationMap.swift b/Mage/Observation/ObservationMap.swift index 246ac884..37c963ff 100644 --- a/Mage/Observation/ObservationMap.swift +++ b/Mage/Observation/ObservationMap.swift @@ -12,7 +12,6 @@ import DataSourceTileOverlay import MapFramework class ObservationMap: DataSourceMap { - @Injected(\.observationImageRepository) var imageRepository: ObservationImageRepository override var REFRESH_KEY: String { @@ -22,8 +21,10 @@ class ObservationMap: DataSourceMap { init( repository: TileRepository? = nil, - mapFeatureRepository: MapFeatureRepository? = nil + mapFeatureRepository: MapFeatureRepository? = nil, + imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl.shared ) { + self.imageRepository = imageRepository super.init(dataSource: DataSources.observation) viewModel = DataSourceMapViewModel( dataSource: dataSource, @@ -31,7 +32,6 @@ class ObservationMap: DataSourceMap { repository: repository, mapFeatureRepository: mapFeatureRepository ) -// , repository: repository, mapFeatureRepository: mapFeatureRepository) } override func handleFeatureChanges(annotations: [DataSourceAnnotation]) -> Bool { @@ -55,14 +55,16 @@ class ObservationMap: DataSourceMap { annotationView?.isEnabled = true } - if let iconPath = annotation.mapItem.iconPath, let annotationView = annotationView { - let image = imageRepository.imageAtPath(imagePath: iconPath) - annotationView.image = image - annotationView.centerOffset = CGPoint(x: 0, y: -(image.size.height/2.0)) - annotationView.accessibilityLabel = "Observation" - annotationView.accessibilityValue = "Observation" - annotationView.displayPriority = .required - annotationView.canShowCallout = true + Task { + if let iconPath = annotation.mapItem.iconPath, let annotationView = annotationView { + let image = await imageRepository.imageAtPath(imagePath: iconPath) + annotationView.image = image + annotationView.centerOffset = CGPoint(x: 0, y: -(image.size.height/2.0)) + annotationView.accessibilityLabel = "Observation" + annotationView.accessibilityValue = "Observation" + annotationView.displayPriority = .required + annotationView.canShowCallout = true + } } return annotationView } diff --git a/Mage/Observation/ObservationsMap.swift b/Mage/Observation/ObservationsMap.swift index c96fd2dd..885ce05b 100644 --- a/Mage/Observation/ObservationsMap.swift +++ b/Mage/Observation/ObservationsMap.swift @@ -24,15 +24,12 @@ class ObservationsMap: DataSourceMap { @Injected(\.observationIconRepository) var iconRepository: ObservationIconRepository - @Injected(\.observationImageRepository) var imageRepository: ObservationImageRepository - init() { + init(imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl.shared) { + self.imageRepository = imageRepository super.init( dataSource: DataSources.observation -// , -// repository: repository, -// mapFeatureRepository: mapFeatureRepository ) viewModel = DataSourceMapViewModel( dataSource: dataSource, @@ -178,44 +175,72 @@ class ObservationsMap: DataSourceMap { } } } - + override func viewForAnnotation(annotation: MKAnnotation, mapView: MKMapView) -> MKAnnotationView? { guard let mapItemAnnotation = annotation as? ObservationMapItemAnnotation else { return nil } - var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: OBSERVATION_MAP_ITEM_ANNOTATION_VIEW_REUSE_ID) - if let annotationView = annotationView { - annotationView.annotation = annotation + var annotationView = mapView.dequeueReusableAnnotationView( + withIdentifier: OBSERVATION_MAP_ITEM_ANNOTATION_VIEW_REUSE_ID + ) + + if let view = annotationView { + view.annotation = annotation } else { - annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: OBSERVATION_MAP_ITEM_ANNOTATION_VIEW_REUSE_ID) + annotationView = MKAnnotationView( + annotation: annotation, + reuseIdentifier: OBSERVATION_MAP_ITEM_ANNOTATION_VIEW_REUSE_ID + ) annotationView?.isEnabled = true } - let image = imageRepository.imageAtPath(imagePath: mapItemAnnotation.mapItem.iconPath) - if let annotationView = annotationView { - annotationView.image = image - - var size = CGSize(width: 40, height: 40) - let max = max(image.size.height, image.size.width) - size.width *= ((image.size.width) / max) - size.height *= ((image.size.height) / max) - annotationView.frame.size = size - annotationView.canShowCallout = false - annotationView.isEnabled = false - annotationView.accessibilityLabel = "Enlarged" - annotationView.zPriority = .max - annotationView.selectedZPriority = .max - - - annotationView.centerOffset = CGPoint(x: 0, y: -(image.size.height/2.0)) -// annotationView.accessibilityLabel = "Observation" -// annotationView.accessibilityValue = "Observation" - annotationView.displayPriority = .required -// annotationView.canShowCallout = true + // --- Default placeholder immediately --- + let placeholder = UIImage(named: "defaultMarker")! + annotationView?.image = placeholder + annotationView?.frame.size = CGSize(width: 40, height: 40) + annotationView?.centerOffset = CGPoint(x: 0, y: -(placeholder.size.height/2.0)) + annotationView?.displayPriority = .required + + // --- Kick off async image load --- + if let iconPath = mapItemAnnotation.mapItem.iconPath, + let annotationView = annotationView { + + Task { + let image = await imageRepository.imageAtPath(imagePath: iconPath) + + await MainActor.run { + // double-check the annotationView is still in use + guard currentAnnotationViews[mapItemAnnotation.id] === annotationView else { return } + + annotationView.image = image + + // Recalculate size based on real image + var size = CGSize(width: 40, height: 40) + let max = max(image.size.height, image.size.width) + if max > 0 { + size.width *= (image.size.width / max) + size.height *= (image.size.height / max) + } + annotationView.frame.size = size + + annotationView.canShowCallout = false + annotationView.isEnabled = false + annotationView.accessibilityLabel = "Enlarged" + annotationView.zPriority = .max + annotationView.selectedZPriority = .max + annotationView.centerOffset = CGPoint(x: 0, y: -(image.size.height / 2.0)) + } + } } + + // keep references mapItemAnnotation.annotationView = annotationView - currentAnnotationViews[mapItemAnnotation.id] = annotationView + if let annotationView = annotationView { + currentAnnotationViews[mapItemAnnotation.id] = annotationView + } + return annotationView } + } diff --git a/Mage/ObservationAnnotation.swift b/Mage/ObservationAnnotation.swift index 1e5670e0..cff6da60 100644 --- a/Mage/ObservationAnnotation.swift +++ b/Mage/ObservationAnnotation.swift @@ -83,38 +83,52 @@ import DateTools return viewForAnnotation(on: on, with: nil, scheme: scheme) } - @objc public override func viewForAnnotation(on: MKMapView, with: AnnotationDragCallback?, scheme: MDCContainerScheming) -> MKAnnotationView { + @objc public override func viewForAnnotation( + on: MKMapView, + with: AnnotationDragCallback?, + scheme: MDCContainerScheming + ) -> MKAnnotationView { var annotationView = on.dequeueReusableAnnotationView(withIdentifier: OBSERVATION_ANNOTATION_VIEW_REUSE_ID) - - if let annotationView = annotationView { - annotationView.annotation = self + + if let view = annotationView { + view.annotation = self } else { - annotationView = ObservationAnnotationView(annotation: self, reuseIdentifier: OBSERVATION_ANNOTATION_VIEW_REUSE_ID, mapView: on, dragCallback: with) + annotationView = ObservationAnnotationView( + annotation: self, + reuseIdentifier: OBSERVATION_ANNOTATION_VIEW_REUSE_ID, + mapView: on, + dragCallback: with + ) annotationView?.isEnabled = true } - - if point { - if let observation = observation { - @Injected(\.observationImageRepository) - var imageRepository: ObservationImageRepository - - let image = imageRepository.image(observation: observation); - annotationView?.image = image; - annotationView?.centerOffset = CGPoint(x: 0, y: -(image.size.height/2.0)) + + // --- Default placeholder immediately --- + annotationView?.image = UIImage(named: "defaultMarker") + annotationView?.centerOffset = .zero + + if point, let observation = observation, let annotationView { + // --- Kick off async image load --- + Task { + let image = await ObservationImageRepositoryImpl.shared.image(observation: observation) + await MainActor.run { + annotationView.image = image + annotationView.centerOffset = CGPoint(x: 0, y: -(image.size.height/2.0)) + } } - } else { - annotationView?.image = nil - annotationView?.frame = .zero - annotationView?.centerOffset = .zero } - if let annotationView = annotationView { + + if let annotationView { annotationView.accessibilityLabel = "Observation" annotationView.accessibilityValue = "Observation" annotationView.displayPriority = .required view = annotationView return annotationView } else { - return MKAnnotationView(annotation: self, reuseIdentifier: OBSERVATION_ANNOTATION_VIEW_REUSE_ID) + return MKAnnotationView( + annotation: self, + reuseIdentifier: OBSERVATION_ANNOTATION_VIEW_REUSE_ID + ) } } + } diff --git a/Mage/Repository/Observation/ObservationImageRepository.swift b/Mage/Repository/Observation/ObservationImageRepository.swift index dfd62eb6..1d4e7191 100644 --- a/Mage/Repository/Observation/ObservationImageRepository.swift +++ b/Mage/Repository/Observation/ObservationImageRepository.swift @@ -8,48 +8,58 @@ import Foundation -private struct ObservationImageRepositoryProviderKey: InjectionKey { - static var currentValue: ObservationImageRepository = ObservationImageRepositoryImpl() -} - -extension InjectedValues { - var observationImageRepository: ObservationImageRepository { - get { Self[ObservationImageRepositoryProviderKey.self] } - set { Self[ObservationImageRepositoryProviderKey.self] = newValue } - } -} - protocol ObservationImageRepository { - func clearCache() + func clearCache() async func imageName( eventId: Int64?, formId: Int?, primaryFieldText: String?, secondaryFieldText: String? ) -> String? - func imageName(observation: Observation?) -> String? - func imageAtPath(imagePath: String?) -> UIImage - func image(observation: Observation) -> UIImage + func imageName(observation: Observation?) async -> String? + func imageAtPath(imagePath: String?) async -> UIImage + func image(observation: Observation) async -> UIImage } class ObservationImageRepositoryImpl: ObservationImageRepository, ObservableObject { + static let shared = ObservationImageRepositoryImpl() + private let cache: ImageCache + private var documentsDirectory: String let annotationScaleWidth = 35.0 - private var imageCache: NSCache = { - let cache = NSCache() - cache.countLimit = 100 - return cache - }() + // prevents accidental new instances + private init() { + self.cache = ImageCache() + self.documentsDirectory = { + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + let documentsDirectory = paths[0] + return documentsDirectory as String + }() + } - private lazy var documentsDirectory: String = { - let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) - let documentsDirectory = paths[0] - return documentsDirectory as String - }() + actor ImageCache { + private var cache = NSCache() + + init(limit: Int = 100) { + cache.countLimit = limit + } + + func get(for key: NSString) -> UIImage? { + cache.object(forKey: key) + } + + func set(_ image: UIImage, for key: NSString) { + cache.setObject(image, forKey: key) + } + + func clear() { + cache.removeAllObjects() + } + } - func clearCache() { - imageCache.removeAllObjects() + func clearCache() async { + await cache.clear() } func imageName( @@ -144,11 +154,12 @@ class ObservationImageRepositoryImpl: ObservationImageRepository, ObservableObje ) } - func image(observation: Observation) -> UIImage { - return imageAtPath(imagePath: imageName(observation: observation)) + func image(observation: Observation) async -> UIImage { + return await imageAtPath(imagePath: imageName(observation: observation)) } - func imageAtPath(imagePath: String?) -> UIImage { + @MainActor + func imageAtPath(imagePath: String?) async -> UIImage { // 0) Fallback let fallback = UIImage(named: "defaultMarker")! @@ -182,7 +193,7 @@ class ObservationImageRepositoryImpl: ObservationImageRepository, ObservableObje // 4) Cache key based on the resolved path let cacheKey = resolvedPath as NSString - if let cached = imageCache.object(forKey: cacheKey) { + if let cached = await cache.get(for: cacheKey) { cached.accessibilityIdentifier = resolvedPath return cached } @@ -191,7 +202,7 @@ class ObservationImageRepositoryImpl: ObservationImageRepository, ObservableObje if let image = UIImage(contentsOfFile: resolvedPath), let cgImage = image.cgImage { let scale = image.size.width / annotationScaleWidth let scaledImage = UIImage(cgImage: cgImage, scale: scale, orientation: image.imageOrientation) - imageCache.setObject(scaledImage, forKey: cacheKey) + await cache.set(scaledImage, for: cacheKey) scaledImage.accessibilityIdentifier = resolvedPath return scaledImage } @@ -200,53 +211,4 @@ class ObservationImageRepositoryImpl: ObservationImageRepository, ObservableObje fallback.accessibilityIdentifier = resolvedPath return fallback } - - - func imageAtPath_ugh(imagePath: String?) -> UIImage { - // Default if we can’t find anything - let fallback = UIImage(named: "defaultMarker")! - - // 1) Validate input - guard let rawPath = imagePath, !rawPath.isEmpty else { - return fallback - } - - // 2) Resolve to an actual file on disk - var resolvedPath = rawPath - let fm = FileManager.default - - if !fm.fileExists(atPath: rawPath) { - // Treat rawPath as a prefix, search its directory for a file that starts with it - let candidate = URL(fileURLWithPath: rawPath) - let dirURL = candidate.deletingLastPathComponent() - let prefix = candidate.lastPathComponent - - if let urls = try? fm.contentsOfDirectory(at: dirURL, includingPropertiesForKeys: nil) { - if let match = urls.first(where: { $0.lastPathComponent.hasPrefix(prefix) }) { - resolvedPath = match.path - } - } - } - - // 3) Use a cache key based on the resolved path - let cacheKey = resolvedPath as NSString - if let cached = imageCache.object(forKey: cacheKey) { - cached.accessibilityIdentifier = resolvedPath - return cached - } - - // 4) Load, scale, cache - if let image = UIImage(contentsOfFile: resolvedPath), let cgImage = image.cgImage { - let scale = image.size.width / annotationScaleWidth - let scaledImage = UIImage(cgImage: cgImage, scale: scale, orientation: image.imageOrientation) - imageCache.setObject(scaledImage, forKey: cacheKey) - scaledImage.accessibilityIdentifier = resolvedPath - return scaledImage - } - - // 5) Fallback if the file still wasn’t found/readable - fallback.accessibilityIdentifier = resolvedPath - return fallback - } - } diff --git a/Mage/UI/ObservationLocation/ObservationLocationSummary.swift b/Mage/UI/ObservationLocation/ObservationLocationSummary.swift index 23b4b186..c1756cc3 100644 --- a/Mage/UI/ObservationLocation/ObservationLocationSummary.swift +++ b/Mage/UI/ObservationLocation/ObservationLocationSummary.swift @@ -9,8 +9,7 @@ import SwiftUI struct ObservationLocationSummary: View { - @Injected(\.observationImageRepository) - var imageRepository: ObservationImageRepository + @State private var uiImage: UIImage? = nil var timestamp: Date? var user: String? @@ -71,11 +70,29 @@ struct ObservationLocationSummary: View { Spacer() } Spacer() - if let iconPath = iconPath { - Image(uiImage: imageRepository.imageAtPath(imagePath: iconPath)) - .frame(maxWidth: 48, maxHeight: 48) + Group { + if let image = uiImage { + Image(uiImage: image) + .resizable() + .frame(maxWidth: 48, maxHeight: 48) + } else { + // Placeholder while loading + Image(systemName: "photo") + .resizable() + .frame(maxWidth: 48, maxHeight: 48) + } + } + .task(id: iconPath) { // Runs when iconPath changes + await loadImage() } } } } + + @MainActor + private func loadImage() async { + guard let path = iconPath else { return } + let image = await ObservationImageRepositoryImpl.shared.imageAtPath(imagePath: path) + uiImage = image + } } diff --git a/Mage/UseCase/FetchEventsUseCase.swift b/Mage/UseCase/FetchEventsUseCase.swift index 05d31a2b..9980e805 100644 --- a/Mage/UseCase/FetchEventsUseCase.swift +++ b/Mage/UseCase/FetchEventsUseCase.swift @@ -9,15 +9,18 @@ import Foundation class FetchEventsUseCase { - @Injected(\.observationImageRepository) - var imageRepository: ObservationImageRepository - @Injected(\.eventRepository) var eventRepository: EventRepository @Injected(\.userRepository) var userRepository: UserRepository + var imageRepository: ObservationImageRepository + + init(imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl.shared) { + self.imageRepository = imageRepository + } + func execute() { Task { let userModel = await userRepository.fetchMyself() @@ -40,7 +43,9 @@ class FetchEventsUseCase { } let formTask = Form.operationToPullFormIcons(eventId: remoteId) { NSLog("Pulled form for event") - self.imageRepository.clearCache() + Task { + await self.imageRepository.clearCache() + } NotificationCenter.default.post(name: .MAGEFormFetched, object: e) } failure: { error in NSLog("Failed to pull form for event") diff --git a/Mage/ViewModel/Observation/ObservationViewViewModel.swift b/Mage/ViewModel/Observation/ObservationViewViewModel.swift index 726d2887..3a3d084a 100644 --- a/Mage/ViewModel/Observation/ObservationViewViewModel.swift +++ b/Mage/ViewModel/Observation/ObservationViewViewModel.swift @@ -29,7 +29,6 @@ class ObservationViewViewModel: NSObject, ObservableObject { @Injected(\.attachmentRepository) var attachmentRepository: AttachmentRepository - @Injected(\.observationImageRepository) var imageRepository: ObservationImageRepository @Published @@ -99,7 +98,8 @@ class ObservationViewViewModel: NSObject, ObservableObject { var cancellables = Set() - init(uri: URL) { + init(uri: URL, imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl.shared) { + self.imageRepository = imageRepository super.init() $observationModel.sink { [weak self] observationModel in diff --git a/MageTests/Map/ObservationAnnotationTests.swift b/MageTests/Map/ObservationAnnotationTests.swift index 20e52e0b..3dd7c1c6 100644 --- a/MageTests/Map/ObservationAnnotationTests.swift +++ b/MageTests/Map/ObservationAnnotationTests.swift @@ -107,7 +107,7 @@ class ObservationAnnotationTests: KIFSpec { expect(annotation.accessibilityValue).to(equal("Observation Annotation")) let mapView = MKMapView(forAutoLayout: ()); - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() + let imageRepository = ObservationImageRepositoryImpl.shared let annotationView = annotation.viewForAnnotation(on: mapView, scheme: MAGEScheme.scheme()); expect(annotationView).toNot(beNil()); @@ -151,7 +151,7 @@ class ObservationAnnotationTests: KIFSpec { expect(annotation.accessibilityValue).to(equal("Observation Annotation")) let mapView = MKMapView(forAutoLayout: ()); - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() + let imageRepository = ObservationImageRepositoryImpl.shared let annotationView = annotation.viewForAnnotation(on: mapView, scheme: MAGEScheme.scheme()); expect(annotationView).toNot(beNil()); diff --git a/MageTests/Repository/Observation/ObservationImageRepositoryTests.swift b/MageTests/Repository/Observation/ObservationImageRepositoryTests.swift index 3937cf4a..bfce2855 100644 --- a/MageTests/Repository/Observation/ObservationImageRepositoryTests.swift +++ b/MageTests/Repository/Observation/ObservationImageRepositoryTests.swift @@ -82,9 +82,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "testfield": "Hi" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(equal(iconPath)) } } @@ -113,9 +112,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(equal(iconPath)) } } @@ -142,9 +140,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "formId": 26 ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(equal(iconPath)) } } @@ -173,9 +170,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(equal(iconPath)) } } @@ -221,9 +217,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(equal(iconPath)) } } @@ -253,9 +248,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(equal(iconPath)) } } @@ -277,9 +271,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(beNil()) } } @@ -307,9 +300,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let imageName = imageRepository.imageName(observation: observation); + let imageName = ObservationImageRepositoryImpl.shared.imageName(observation: observation); expect(imageName).to(beNil()) } } @@ -337,9 +329,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let image = imageRepository.image(observation: observation) + let image = ObservationImageRepositoryImpl.shared.image(observation: observation) expect(image).to(equal(UIImage(named:"defaultMarker"))) } } @@ -369,9 +360,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let image = imageRepository.image(observation: observation); + let image = ObservationImageRepositoryImpl.shared.image(observation: observation); expect(image).toNot(beNil()); expect(image).toNot(equal(UIImage(named:"defaultMarker"))); } @@ -402,9 +392,8 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { "secondary": "turtle" ] ] - let imageRepository: ObservationImageRepository = ObservationImageRepositoryImpl() - let image = imageRepository.image(observation: observation); + let image = ObservationImageRepositoryImpl.shared.image(observation: observation); expect(image).toNot(beNil()); expect(image).toNot(equal(UIImage(named:"defaultMarker"))); @@ -416,7 +405,7 @@ class ObservationImageRepositoryTests: MageCoreDataTestCase { FileManager.default.createFile(atPath: iconPath, contents: image.pngData()!, attributes: nil) } - let image2 = imageRepository.image(observation: observation); + let image2 = ObservationImageRepositoryImpl.shared.image(observation: observation); expect(image2).toNot(beNil()); expect(image2).to(equal(image)) } diff --git a/MageTests/TestHelpers.swift b/MageTests/TestHelpers.swift index a0bb8bff..3cca2cd7 100644 --- a/MageTests/TestHelpers.swift +++ b/MageTests/TestHelpers.swift @@ -261,7 +261,6 @@ class TestHelpers { defaultAttachmentInjection() defaultRoleInjection() defaultLocationInjection() - defaultObservationImageInjection() defaultStaticLayerInjection() defaultGeoPackageInjection() defaultFeedItemInjection() @@ -325,10 +324,6 @@ class TestHelpers { InjectedValues[\.locationRepository] = LocationRepositoryImpl() } - static func defaultObservationImageInjection() { - InjectedValues[\.observationImageRepository] = ObservationImageRepositoryImpl() - } - static func defaultStaticLayerInjection() { InjectedValues[\.staticLayerLocalDataSource] = StaticLayerCoreDataDataSource() InjectedValues[\.staticLayerRepository] = StaticLayerRepository() diff --git a/MageTests/UI/Observation/ObservationViewViewModelTests.swift b/MageTests/UI/Observation/ObservationViewViewModelTests.swift index d835c3c9..875d9e1c 100644 --- a/MageTests/UI/Observation/ObservationViewViewModelTests.swift +++ b/MageTests/UI/Observation/ObservationViewViewModelTests.swift @@ -39,7 +39,6 @@ final class ObservationViewViewModelTests: MageInjectionTestCase { roleRepository = RoleRepositoryMock() InjectedValues[\.roleRepository] = roleRepository observationImageRepository = ObservationImageRepositoryMock() - InjectedValues[\.observationImageRepository] = observationImageRepository } func testInit() {