Skip to content
18 changes: 18 additions & 0 deletions PresentationLayer/Extensions/DomainExtensions/PublicHex+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// PublicHexes+.swift
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix filename in header comment.

The filename in the header comment says "PublicHexes+.swift" but the actual filename is "PublicHex+.swift" (singular, not plural).

Apply this diff:

-//  PublicHexes+.swift
+//  PublicHex+.swift
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// PublicHexes+.swift
// PublicHex+.swift
🤖 Prompt for AI Agents
In PresentationLayer/Extensions/DomainExtensions/PublicHex+.swift around line 2,
the file header comment has the wrong filename "PublicHexes+.swift"; update the
header comment to the correct filename "PublicHex+.swift" so the header matches
the actual file name.

// wxm-ios
//
// Created by Pantelis Giazitsis on 26/9/25.
//

import DomainLayer

extension PublicHex {
var cellCapacityReached: Bool {
guard let deviceCount, let capacity else {
return false
}

return deviceCount >= capacity
}
}
3 changes: 2 additions & 1 deletion PresentationLayer/Navigation/DeepLinkHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,9 @@
let lon = cell.center.lon
let lat = cell.center.lat
let cellCenter = CLLocationCoordinate2D(latitude: lat, longitude: lon)
let cellCapacity = cell.capacity ?? 0

let route = Route.explorerList(ViewModelsFactory.getExplorerStationsListViewModel(cellIndex: index, cellCenter: cellCenter))
let route = Route.explorerList(ViewModelsFactory.getExplorerStationsListViewModel(cellIndex: index, cellCenter: cellCenter, cellCapacity: cellCapacity))
router.navigateTo(route)
}
}
Expand All @@ -254,7 +255,7 @@
case .weatherxmPro:
let viewModel = ViewModelsFactory.getProPromotionalViewModel()
router.showBottomSheet(.proPromo(viewModel))
break

Check warning on line 258 in PresentationLayer/Navigation/DeepLinkHandler.swift

View workflow job for this annotation

GitHub Actions / swiftLint

Unneeded Break in Switch Violation: Avoid using unneeded break statements (unneeded_break_in_switch)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ struct SelectLocationMapView: View {
annotationTitle: Binding(get: { viewModel.selectedDeviceLocation?.name },
set: { _ in }),
geometryProxyForFrameOfMapView: proxy.frame(in: .local),
mapControls: viewModel.mapControls)
polygonPoints: viewModel.explorerData?.cellCapacityPoints,
polylinePoints: viewModel.explorerData?.cellBorderPoints,
textPoints: viewModel.explorerData?.cellCapacityTextPoints,
mapControls: viewModel.mapControls) { annotations in
viewModel.handlePointedAnnotationsChange(annotations: annotations)
}

searchArea
}
Expand Down Expand Up @@ -126,5 +131,6 @@ private extension SelectLocationMapView {

#Preview {
let useCase = SwinjectHelper.shared.getContainerForSwinject().resolve(DeviceLocationUseCaseApi.self)!
return SelectLocationMapView(viewModel: .init(useCase: useCase))
let explorerUseCase = SwinjectHelper.shared.getContainerForSwinject().resolve(ExplorerUseCaseApi.self)!
return SelectLocationMapView(viewModel: .init(useCase: useCase, explorerUseCase: explorerUseCase))
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import Combine
import DomainLayer
import Toolkit
import MapboxMaps

@MainActor
protocol SelectLocationMapViewModelDelegate: AnyObject {
Expand Down Expand Up @@ -43,16 +44,26 @@
}
}
@Published private(set) var searchResults: [DeviceLocationSearchResult] = []
@Published private(set) var explorerData: ExplorerData?
var mapControls: MapControls = .init()
private var cancellableSet: Set<AnyCancellable> = .init()
private var latestTask: Cancellable?
private var latestPointedAnnotations: [PolygonAnnotation]?
let useCase: DeviceLocationUseCaseApi
let explorerUseCase: ExplorerUseCaseApi
weak var delegate: SelectLocationMapViewModelDelegate?
let linkNavigation: LinkNavigation

init(useCase: DeviceLocationUseCaseApi, initialCoordinate: CLLocationCoordinate2D? = nil, delegate: SelectLocationMapViewModelDelegate? = nil) {
init(useCase: DeviceLocationUseCaseApi,
explorerUseCase: ExplorerUseCaseApi,
initialCoordinate: CLLocationCoordinate2D? = nil,
delegate: SelectLocationMapViewModelDelegate? = nil,
linkNavigation: LinkNavigation = LinkNavigationHelper()) {
self.useCase = useCase
self.explorerUseCase = explorerUseCase
self.delegate = delegate
self.selectedCoordinate = initialCoordinate ?? useCase.getSuggestedDeviceLocation() ?? .init()
self.linkNavigation = linkNavigation
$selectedCoordinate
.debounce(for: 1.0, scheduler: DispatchQueue.main)
.sink { [weak self] _ in
Expand All @@ -70,6 +81,10 @@
useCase.searchResults.sink { [weak self] results in
self?.searchResults = results
}.store(in: &cancellableSet)

Task { @MainActor in
await getExplorerData()
}
Comment on lines +85 to +87
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Store and cancel the explorer data fetch task.

Spawning a Task in an initializer without storing a reference means it cannot be cancelled if the view model is deallocated. This can lead to:

  • Memory leaks if the task captures self strongly
  • Crashes if the task completes after deallocation
  • Wasted network requests

Apply this diff to store and cancel the task:

+	private var explorerDataTask: Task<Void, Never>?
+
 	init(useCase: DeviceLocationUseCaseApi,
 		 explorerUseCase: ExplorerUseCaseApi,
 		 initialCoordinate: CLLocationCoordinate2D? = nil,
 		 delegate: SelectLocationMapViewModelDelegate? = nil,
 		 linkNavigation: LinkNavigation = LinkNavigationHelper()) {
 		// ... existing initialization ...
-		Task { @MainActor in
+		explorerDataTask = Task { @MainActor in
 			await getExplorerData()
 		}
 	}
+
+	deinit {
+		explorerDataTask?.cancel()
+	}

}

func handleSearchResultTap(result: DeviceLocationSearchResult) {
Expand Down Expand Up @@ -105,6 +120,28 @@
}
}
}

func handlePointedAnnotationsChange(annotations: [PolygonAnnotation]) {
latestPointedAnnotations = annotations
let capacityReached = isPointedCellCapacityReached()

if capacityReached {
Toast.shared.show(text: LocalizableString.ClaimDevice.cellCapacityReachedMessage.localized.attributedMarkdown ?? "",
type: .info,
retryButtonTitle: LocalizableString.readMore.localized) { [weak self] in
self?.linkNavigation.openUrl(DisplayedLinks.cellCapacity.linkURL)
}
}
}

func isPointedCellCapacityReached() -> Bool {
guard let annotation = latestPointedAnnotations?.first,
let count = annotation.userInfo?[ExplorerKeys.deviceCount.rawValue] as? Int,

Check notice on line 139 in PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift

View check run for this annotation

Xcode Cloud / WeatherXM | Unit tests | wxm-ios - iOS

PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift#L139

'userInfo' is deprecated: Use customData instead.
let capacity = annotation.userInfo?[ExplorerKeys.cellCapacity.rawValue] as? Int else {

Check notice on line 140 in PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift

View check run for this annotation

Xcode Cloud / WeatherXM | Unit tests | wxm-ios - iOS

PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift#L140

'userInfo' is deprecated: Use customData instead.
return false
}
return count >= capacity
}
}

private extension SelectLocationMapViewModel {
Expand All @@ -116,4 +153,18 @@
self?.selectedDeviceLocation = location
}
}

func getExplorerData() async {
do {
let result = try await explorerUseCase.getPublicHexes()
switch result {
case .success(let hexes):
self.explorerData = ExplorerFactory(publicHexes: hexes).generateExplorerData()
case .failure(let error):
print(error)
}
} catch {
print(error)
}
}
}
123 changes: 119 additions & 4 deletions PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
@Binding var location: CLLocationCoordinate2D
@Binding var annotationTitle: String?
let geometryProxyForFrameOfMapView: CGRect
let polygonPoints: [PolygonAnnotation]?
let polylinePoints: [PolylineAnnotation]?
let textPoints: [PointAnnotation]?
let annotationPointCallback: GenericMainActorCallback<[PolygonAnnotation]>

private let markerSize: CGSize = CGSize(width: 44.0, height: 44.0)
@State private var locationPoint: CGPoint = .zero
Expand All @@ -26,19 +30,31 @@
init(location: Binding<CLLocationCoordinate2D>,
annotationTitle: Binding<String?>,
geometryProxyForFrameOfMapView: CGRect,
mapControls: MapControls) {
polygonPoints: [PolygonAnnotation]?,
polylinePoints: [PolylineAnnotation]?,
textPoints: [PointAnnotation]?,
mapControls: MapControls,
annotationPointCallback: @escaping GenericMainActorCallback<[PolygonAnnotation]>) {
_location = location
_annotationTitle = annotationTitle
self.geometryProxyForFrameOfMapView = geometryProxyForFrameOfMapView
self.polygonPoints = polygonPoints
self.polylinePoints = polylinePoints
self.textPoints = textPoints
_controls = StateObject(wrappedValue: mapControls)
self.annotationPointCallback = annotationPointCallback
}

var body: some View {
ZStack {
MapBoxClaimDevice(location: $location,
locationPoint: $locationPoint,
geometryProxyForFrameOfMapView: geometryProxyForFrameOfMapView,
controls: controls)
polygonPoints: polygonPoints,
polylinePoints: polylinePoints,
textPoints: textPoints,
controls: controls,
annotationPointCallback: annotationPointCallback)

markerAnnotation
.offset(x: 0.0, y: markerAnnotationOffset)
Expand Down Expand Up @@ -90,9 +106,13 @@
@Binding var location: CLLocationCoordinate2D
@Binding var locationPoint: CGPoint
let geometryProxyForFrameOfMapView: CGRect
let polygonPoints: [PolygonAnnotation]?
let polylinePoints: [PolylineAnnotation]?
let textPoints: [PointAnnotation]?
@StateObject var controls: MapControls
let annotationPointCallback: GenericMainActorCallback<[PolygonAnnotation]>

func makeUIViewController(context _: Context) -> MapViewLocationController {
func makeUIViewController(context: Context) -> MapViewLocationController {
let controller = MapViewLocationController(frame: geometryProxyForFrameOfMapView,
location: $location,
locationPoint: $locationPoint)
Expand All @@ -109,11 +129,33 @@
controller?.setCenter(coordinate)
}

controller.delegate = context.coordinator

return controller
}

func makeCoordinator() -> Coordinator {
Coordinator(annotationPointCallback: annotationPointCallback)
}

func updateUIViewController(_ controller: MapViewLocationController, context _: Context) {
controller.polygonManager?.annotations = polygonPoints ?? []
controller.pointManager?.annotations = textPoints ?? []
controller.polylineManager?.annotations = polylinePoints ?? []
}

class Coordinator: MapViewLocationControllerDelegate {
let annotationPointCallback: GenericMainActorCallback<[PolygonAnnotation]>

init(annotationPointCallback: @escaping GenericMainActorCallback<[PolygonAnnotation]>) {
self.annotationPointCallback = annotationPointCallback
}

@MainActor
func didPointAnnotations(_ annotations: [PolygonAnnotation]) {
annotationPointCallback(annotations)
}
}
}

class MapControls: ObservableObject {
Expand All @@ -122,16 +164,27 @@
var setMapCenter: GenericCallback<CLLocationCoordinate2D>?
}

@MainActor
fileprivate protocol MapViewLocationControllerDelegate: AnyObject {

Check warning on line 168 in PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift

View workflow job for this annotation

GitHub Actions / swiftLint

Private over Fileprivate Violation: Prefer `private` over `fileprivate` declarations (private_over_fileprivate)
func didPointAnnotations(_ annotations: [PolygonAnnotation])
}

class MapViewLocationController: UIViewController {
private static let ZOOM_LEVEL: CGFloat = 15

let frame: CGRect
@Binding var location: CLLocationCoordinate2D
@Binding var locationPoint: CGPoint
internal var mapView: MapView!
internal weak var polygonManager: PolygonAnnotationManager?
internal weak var pointManager: PointAnnotationManager?
internal weak var polylineManager: PolylineAnnotationManager?
fileprivate weak var delegate: MapViewLocationControllerDelegate?
private var cancelablesSet = Set<AnyCancelable>()

init(frame: CGRect, location: Binding<CLLocationCoordinate2D>, locationPoint: Binding<CGPoint>) {
init(frame: CGRect,
location: Binding<CLLocationCoordinate2D>,
locationPoint: Binding<CGPoint>) {
self.frame = frame
_location = location
_locationPoint = locationPoint
Expand All @@ -157,6 +210,7 @@
mapView.ornaments.options.scaleBar.visibility = .hidden
mapView.gestures.options.rotateEnabled = false
mapView.gestures.options.pitchEnabled = false

mapView.mapboxMap.setCamera(to: CameraOptions(zoom: 2))

mapView.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -173,6 +227,7 @@
}

self.locationPoint = self.mapView.mapboxMap.point(for: self.location)
self.updateLocationPointedAnnotations(self.locationPoint)
}.store(in: &cancelablesSet)

self.mapView.mapboxMap.onMapIdle.observe { [weak self] _ in
Expand All @@ -182,10 +237,13 @@
if let self = self {
self.location = pointAnnotation.point.coordinates
self.locationPoint = mapView.mapboxMap.point(for: self.location)
self.updateLocationPointedAnnotations(self.locationPoint)
}
}
}.store(in: &cancelablesSet)

setPolygonManagers()

setCenter(location)
}

Expand All @@ -210,11 +268,68 @@
internal func cameraSetup() -> CameraOptions {
return CameraOptions(center: CLLocationCoordinate2D())
}

func updateLocationPointedAnnotations(_ point: CGPoint) {
guard let polygonManager else {
return
}
let layerIds = [polygonManager.layerId]
let annotations = polygonManager.annotations
let options = RenderedQueryOptions(layerIds: layerIds, filter: nil)
mapView.mapboxMap.queryRenderedFeatures(with: point, options: options) { [weak self] result in
switch result {
case .success(let features):
let tappedAnnotations = annotations.filter { annotation in
features.contains(where: { feature in
guard let featureid = feature.queriedFeature.feature.identifier else {
return false
}
switch featureid {
case .string(let str):
return annotation.id == str
case .number(_):

Check warning on line 290 in PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift

View workflow job for this annotation

GitHub Actions / swiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return false
}
})
}

self?.delegate?.didPointAnnotations(tappedAnnotations)
case .failure(let error):
print(error)
}
}
}
Comment on lines +272 to +301
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix feature ID matching to handle numeric identifiers.

The current implementation only handles string feature IDs and returns false for numeric IDs without checking them. This could cause annotations with numeric IDs to be missed.

Apply this diff to properly handle both ID types:

 				let tappedAnnotations = annotations.filter { annotation in
 					features.contains(where: { feature in
-						guard let featureid = feature.queriedFeature.feature.identifier else {
+						guard let featureId = feature.queriedFeature.feature.identifier else {
 							return false
 						}
-						switch featureid {
+						switch featureId {
 							case .string(let str):
 								return annotation.id == str
-							case .number(_):
-								return false
+							case .number(let num):
+								return annotation.id == String(describing: num)
 						}
 					})
 				}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func updateLocationPointedAnnotations(_ point: CGPoint) {
guard let polygonManager else {
return
}
let layerIds = [polygonManager.layerId]
let annotations = polygonManager.annotations
let options = RenderedQueryOptions(layerIds: layerIds, filter: nil)
mapView.mapboxMap.queryRenderedFeatures(with: point, options: options) { [weak self] result in
switch result {
case .success(let features):
let tappedAnnotations = annotations.filter { annotation in
features.contains(where: { feature in
guard let featureid = feature.queriedFeature.feature.identifier else {
return false
}
switch featureid {
case .string(let str):
return annotation.id == str
case .number(_):
return false
}
})
}
self?.delegate?.didPointAnnotations(tappedAnnotations)
case .failure(let error):
print(error)
}
}
}
func updateLocationPointedAnnotations(_ point: CGPoint) {
guard let polygonManager else {
return
}
let layerIds = [polygonManager.layerId]
let annotations = polygonManager.annotations
let options = RenderedQueryOptions(layerIds: layerIds, filter: nil)
mapView.mapboxMap.queryRenderedFeatures(with: point, options: options) { [weak self] result in
switch result {
case .success(let features):
let tappedAnnotations = annotations.filter { annotation in
features.contains(where: { feature in
guard let featureId = feature.queriedFeature.feature.identifier else {
return false
}
switch featureId {
case .string(let str):
return annotation.id == str
case .number(let num):
return annotation.id == String(describing: num)
}
})
}
self?.delegate?.didPointAnnotations(tappedAnnotations)
case .failure(let error):
print(error)
}
}
}
🧰 Tools
🪛 GitHub Check: swiftLint

[warning] 290-290:
Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)

🤖 Prompt for AI Agents
In PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift around lines
272 to 301, the feature ID matching only handles string IDs and ignores numeric
IDs; update the matching so numeric identifiers are also compared to
annotation.id by converting the numeric ID to a string (or otherwise serializing
it to the same format used by annotation.id) before comparing, i.e., handle both
.string and .number cases by deriving a string representation for the .number
case and using that for equality, then return the match result so
numeric-ID-backed annotations are detected correctly.

}


Check warning on line 304 in PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift

View workflow job for this annotation

GitHub Actions / swiftLint

Vertical Whitespace Violation: Limit vertical whitespace to a single empty line; currently 2 (vertical_whitespace)
extension MapViewLocationController {
func locationUpdate(newLocation: Location) {
mapView.mapboxMap.setCamera(to: CameraOptions(center: newLocation.coordinate, zoom: 13))
location = newLocation.coordinate
}

func setPolygonManagers() {
self.polygonManager = mapView.annotations.makePolygonAnnotationManager(id: MapBoxConstants.cellCapacityPolygonManagerId)
self.polylineManager = mapView.annotations.makePolylineAnnotationManager(id: MapBoxConstants.bordersManagerId)

let pointAnnotationManager = mapView.annotations.makePointAnnotationManager(id: MapBoxConstants.pointManagerId)
pointManager = pointAnnotationManager

try? mapView.mapboxMap.updateLayer(withId: MapBoxConstants.pointManagerId, type: SymbolLayer.self) { layer in
layer.minZoom = 10

let stops: [Double: Double] = [
10: CGFloat(.mediumFontSize),
12: CGFloat(.XLTitleFontSize),
16: CGFloat(.maxFontSize)
]

layer.textSize = .expression(Exp(.interpolate) {
Exp(.exponential) { 1.75 }
Exp(.zoom)
stops
})
layer.textColor = .constant(StyleColor(UIColor(colorEnum: .textWhite)))
}
}
Comment on lines +311 to +334
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace force-try with proper error handling.

The try? on line 318 silently ignores layer update failures, which could lead to incorrect map rendering without any indication of what went wrong. Layer updates can fail for various reasons (layer not found, invalid configuration, etc.).

Apply this diff to add proper error handling:

-	try? mapView.mapboxMap.updateLayer(withId: MapBoxConstants.pointManagerId, type: SymbolLayer.self) { layer in
+	do {
+		try mapView.mapboxMap.updateLayer(withId: MapBoxConstants.pointManagerId, type: SymbolLayer.self) { layer in
-		layer.minZoom = 10
-
-		let stops: [Double: Double] = [
-			10: CGFloat(.mediumFontSize),
-			12: CGFloat(.XLTitleFontSize),
-			16: CGFloat(.maxFontSize)
-		]
-
-		layer.textSize = .expression(Exp(.interpolate) {
-			Exp(.exponential) { 1.75 }
-			Exp(.zoom)
-			stops
-		})
-		layer.textColor = .constant(StyleColor(UIColor(colorEnum: .textWhite)))
-	}
+			layer.minZoom = 10
+
+			let stops: [Double: Double] = [
+				10: CGFloat(.mediumFontSize),
+				12: CGFloat(.XLTitleFontSize),
+				16: CGFloat(.maxFontSize)
+			]
+
+			layer.textSize = .expression(Exp(.interpolate) {
+				Exp(.exponential) { 1.75 }
+				Exp(.zoom)
+				stops
+			})
+			layer.textColor = .constant(StyleColor(UIColor(colorEnum: .textWhite)))
+		}
+	} catch {
+		print("Failed to configure point annotation layer: \(error)")
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func setPolygonManagers() {
self.polygonManager = mapView.annotations.makePolygonAnnotationManager(id: MapBoxConstants.cellCapacityPolygonManagerId)
self.polylineManager = mapView.annotations.makePolylineAnnotationManager(id: MapBoxConstants.bordersManagerId)
let pointAnnotationManager = mapView.annotations.makePointAnnotationManager(id: MapBoxConstants.pointManagerId)
pointManager = pointAnnotationManager
try? mapView.mapboxMap.updateLayer(withId: MapBoxConstants.pointManagerId, type: SymbolLayer.self) { layer in
layer.minZoom = 10
let stops: [Double: Double] = [
10: CGFloat(.mediumFontSize),
12: CGFloat(.XLTitleFontSize),
16: CGFloat(.maxFontSize)
]
layer.textSize = .expression(Exp(.interpolate) {
Exp(.exponential) { 1.75 }
Exp(.zoom)
stops
})
layer.textColor = .constant(StyleColor(UIColor(colorEnum: .textWhite)))
}
}
func setPolygonManagers() {
self.polygonManager = mapView.annotations.makePolygonAnnotationManager(id: MapBoxConstants.cellCapacityPolygonManagerId)
self.polylineManager = mapView.annotations.makePolylineAnnotationManager(id: MapBoxConstants.bordersManagerId)
let pointAnnotationManager = mapView.annotations.makePointAnnotationManager(id: MapBoxConstants.pointManagerId)
pointManager = pointAnnotationManager
do {
try mapView.mapboxMap.updateLayer(withId: MapBoxConstants.pointManagerId, type: SymbolLayer.self) { layer in
layer.minZoom = 10
let stops: [Double: Double] = [
10: CGFloat(.mediumFontSize),
12: CGFloat(.XLTitleFontSize),
16: CGFloat(.maxFontSize)
]
layer.textSize = .expression(Exp(.interpolate) {
Exp(.exponential) { 1.75 }
Exp(.zoom)
stops
})
layer.textColor = .constant(StyleColor(UIColor(colorEnum: .textWhite)))
}
} catch {
print("Failed to configure point annotation layer: \(error)")
}
}
🤖 Prompt for AI Agents
In PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift around lines
311 to 334, replace the silent `try? mapView.mapboxMap.updateLayer(...)` with
explicit error handling: wrap the updateLayer call in a do-catch, call `try`
inside the do block, and in the catch block log the error (using your app logger
or print/OSLog) with a clear contextual message including the layer id and the
thrown error; ensure the catch also contains any minimal recovery or fallback
(e.g., skip styling or mark a flag) so failures don’t go unnoticed.

}
5 changes: 3 additions & 2 deletions PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@

func didTapAnnotation(_: MapViewController, _ annotations: [PolygonAnnotation]) {
guard let firstValidAnnotation = annotations.first,
let hexIndex = firstValidAnnotation.userInfo?[ExplorerKeys.cellIndex.rawValue] as? String else {
let hexIndex = firstValidAnnotation.userInfo?[ExplorerKeys.cellIndex.rawValue] as? String,

Check notice on line 89 in PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift

View check run for this annotation

Xcode Cloud / WeatherXM | Unit tests | wxm-ios - iOS

PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift#L89

'userInfo' is deprecated: Use customData instead.
let cellCapacity = firstValidAnnotation.userInfo?[ExplorerKeys.cellCapacity.rawValue] as? Int else {

Check notice on line 90 in PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift

View check run for this annotation

Xcode Cloud / WeatherXM | Unit tests | wxm-ios - iOS

PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift#L90

'userInfo' is deprecated: Use customData instead.
return
}

viewModel.routeToDeviceListFor(hexIndex, firstValidAnnotation.polygon.center)
viewModel.routeToDeviceListFor(hexIndex, firstValidAnnotation.polygon.center, capacity: cellCapacity)
}

func didTapMapArea(_: MapViewController) {
Expand Down Expand Up @@ -201,7 +202,7 @@
case .failure(let error):
print(error)
}
}

Check warning on line 205 in PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift

View workflow job for this annotation

GitHub Actions / swiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
}.store(in: &cancelablesSet)
}

Expand All @@ -210,7 +211,7 @@
delegate?.configureMap(self)
}

internal func configureHeatMapLayer(source: GeoJSONSource) {

Check warning on line 214 in PresentationLayer/UIComponents/MapBox/MapBoxMapView.swift

View workflow job for this annotation

GitHub Actions / swiftLint

Function Body Length Violation: Function body should span 50 lines or less excluding comments and whitespace: currently spans 65 lines (function_body_length)
layer.maxZoom = 10
layer.heatmapColor = .expression(
Exp(.interpolate) {
Expand Down
Loading