summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@kvadrat.se>2023-03-28 16:27:17 +0200
committerAndrej Mihajlov <and@mullvad.net>2023-03-31 18:23:40 +0200
commit4457433e59466d032e9acece42dae9525e8f49db (patch)
tree73d8703d5400f31ad5529df0e27c79cec8fb3355
parent3efcaabe2007e267c54964b96f7a9e02ce21d503 (diff)
downloadmullvadvpn-4457433e59466d032e9acece42dae9525e8f49db.tar.xz
mullvadvpn-4457433e59466d032e9acece42dae9525e8f49db.zip
Replace custom GeoJSON parser with MKGeoJSONDecoder
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj4
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/GeoJSON.swift156
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/MapViewController.swift28
3 files changed, 27 insertions, 161 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 622cfc0fc5..595fa0907b 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -358,7 +358,6 @@
58FC040A27B3EE03001C21F0 /* TunnelMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */; };
58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */; };
58FD5BF42428C67600112C88 /* InAppPurchaseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */; };
- 58FEEB46260A028D00A621A8 /* GeoJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FEEB45260A028D00A621A8 /* GeoJSON.swift */; };
58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */; };
58FF2C03281BDE02009EF542 /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF2C02281BDE02009EF542 /* SettingsManager.swift */; };
E1187ABC289BBB850024E748 /* OutOfTimeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */; };
@@ -942,7 +941,6 @@
58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitor.swift; sourceTree = "<group>"; };
58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKProduct+Formatting.swift"; sourceTree = "<group>"; };
58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchaseButton.swift; sourceTree = "<group>"; };
- 58FEEB45260A028D00A621A8 /* GeoJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeoJSON.swift; sourceTree = "<group>"; };
58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomaticKeyboardResponder.swift; sourceTree = "<group>"; };
58FF2C02281BDE02009EF542 /* SettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = "<group>"; };
7AD8490C29BA1EC500878E53 /* SettingsCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCellFactory.swift; sourceTree = "<group>"; };
@@ -1350,7 +1348,6 @@
583FE01E29C197D5006E85F9 /* Tunnel */ = {
isa = PBXGroup;
children = (
- 58FEEB45260A028D00A621A8 /* GeoJSON.swift */,
58B43C1825F77DB60002C8C3 /* TunnelControlView.swift */,
58C3F4F82964B08300D72515 /* MapViewController.swift */,
58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */,
@@ -2723,7 +2720,6 @@
5878F50229CDB989003D4BE2 /* ChangeLogCoordinator.swift in Sources */,
58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */,
58B26E262943522400D5980C /* NotificationProvider.swift in Sources */,
- 58FEEB46260A028D00A621A8 /* GeoJSON.swift in Sources */,
58CE5E64224146200008646E /* AppDelegate.swift in Sources */,
5878A27329091D6D0096FC88 /* TunnelBlockObserver.swift in Sources */,
5872D6E8286304DE00DB5F4E /* TermsOfService.swift in Sources */,
diff --git a/ios/MullvadVPN/View controllers/Tunnel/GeoJSON.swift b/ios/MullvadVPN/View controllers/Tunnel/GeoJSON.swift
deleted file mode 100644
index c33fab801b..0000000000
--- a/ios/MullvadVPN/View controllers/Tunnel/GeoJSON.swift
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-// GeoJSON.swift
-// MullvadVPN
-//
-// Created by pronebird on 25/02/2021.
-// Copyright © 2021 Mullvad VPN AB. All rights reserved.
-//
-
-import CoreLocation
-import Foundation
-import MapKit
-
-enum GeoJSON {}
-
-extension GeoJSON {
- struct FeatureCollection: Decodable {
- let features: [Feature]
-
- init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let type = try container.decode(String.self, forKey: .type)
-
- if type == "FeatureCollection" {
- features = try container.decode([Feature].self, forKey: .features)
- } else {
- throw DecodingError.dataCorruptedError(
- forKey: .type,
- in: container,
- debugDescription: "FeatureCollection: Invalid type \(type)"
- )
- }
- }
-
- var mkOverlays: [MKOverlay] {
- return features.flatMap { feature -> [MKOverlay] in
- // Some tools like mapshaper output empty features after optimizing out the geometry
- guard let geometry = feature.geometry else { return [] }
-
- switch geometry {
- case let .polygon(polygon):
- return polygon.mkPolygons
-
- case let .multiPolygon(multiPolygon):
- return multiPolygon.mkPolygons
- }
- }
- }
-
- private enum CodingKeys: String, CodingKey {
- case type, features
- }
- }
-
- struct Feature: Decodable {
- let geometry: Geometry?
-
- private enum CodingKeys: String, CodingKey {
- case type, geometry
- }
-
- init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let type = try container.decode(String.self, forKey: .type)
-
- if type == "Feature" {
- geometry = try container.decodeIfPresent(Geometry.self, forKey: .geometry)
- } else {
- throw DecodingError.dataCorruptedError(
- forKey: .type,
- in: container,
- debugDescription: "Feature: Invalid type \(type)"
- )
- }
- }
- }
-
- enum Geometry: Decodable {
- case polygon(Polygon)
- case multiPolygon(MultiPolygon)
-
- init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let type = try container.decode(String.self, forKey: .type)
-
- switch type {
- case "Polygon":
- self = .polygon(try decoder.singleValueContainer().decode(Polygon.self))
-
- case "MultiPolygon":
- self = .multiPolygon(try decoder.singleValueContainer().decode(MultiPolygon.self))
-
- default:
- throw DecodingError.dataCorruptedError(
- forKey: .type,
- in: container,
- debugDescription: "Geometry: Unknown type \(type)"
- )
- }
- }
-
- private enum CodingKeys: String, CodingKey {
- case type
- }
- }
-
- struct Polygon: Decodable {
- let coordinates: [[[Double]]]
-
- var mkPolygons: [MKPolygon] {
- let coords = geoCoordinates
- let exteriorCoordinates = coords.first ?? []
-
- let exteriorPolygon = MKPolygon(
- coordinates: exteriorCoordinates,
- count: exteriorCoordinates.count,
- interiorPolygons: nil
- )
-
- let interiorPolygons = coords.dropFirst().map { interiorCoords -> MKPolygon in
- return MKPolygon(
- coordinates: interiorCoords,
- count: interiorCoords.count
- )
- }
-
- return [exteriorPolygon] + interiorPolygons
- }
-
- private var geoCoordinates: [[CLLocationCoordinate2D]] {
- return coordinates.map { values -> [CLLocationCoordinate2D] in
- return values.map { coordinates -> CLLocationCoordinate2D in
- return CLLocationCoordinate2D(
- latitude: coordinates[1],
- longitude: coordinates[0]
- )
- }
- }
- }
- }
-
- struct MultiPolygon: Decodable {
- let coordinates: [[[[Double]]]]
-
- var mkPolygons: [MKOverlay] {
- return coordinates.flatMap { values -> [MKPolygon] in
- return Polygon(coordinates: values).mkPolygons
- }
- }
- }
-
- static func decodeGeoJSON(_ data: Data) throws -> [MKOverlay] {
- return try JSONDecoder()
- .decode(GeoJSON.FeatureCollection.self, from: data)
- .mkOverlays
- }
-}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/MapViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/MapViewController.swift
index 9a8a434dd3..243672597a 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/MapViewController.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/MapViewController.swift
@@ -171,7 +171,33 @@ final class MapViewController: UIViewController, MKMapViewDelegate {
do {
let data = try Data(contentsOf: fileURL)
- let overlays = try GeoJSON.decodeGeoJSON(data)
+ guard let features = try MKGeoJSONDecoder().decode(data) as? [MKGeoJSONFeature]
+ else { return }
+
+ var overlays = [MKOverlay]()
+
+ for feature in features {
+ for geometry in feature.geometry {
+ if let polygon = geometry as? MKPolygon {
+ if let interiorPolygons = polygon.interiorPolygons,
+ !interiorPolygons.isEmpty
+ {
+ overlays
+ .append(MKPolygon(
+ points: polygon.points(),
+ count: polygon.pointCount
+ ))
+ overlays.append(contentsOf: interiorPolygons)
+ } else {
+ overlays.append(polygon)
+ }
+ }
+
+ if let multiPolygon = geometry as? MKMultiPolygon {
+ overlays.append(contentsOf: multiPolygon.polygons)
+ }
+ }
+ }
mapView.addOverlays(overlays, level: .aboveLabels)
} catch {