From 57599d7fa108b1db4310d2945af538498d3d2ace Mon Sep 17 00:00:00 2001 From: Tarek Tolba Date: Thu, 20 Mar 2025 15:11:45 +0200 Subject: [PATCH 1/2] Migrate iOS plugin and example to swift package manager --- arcgis_map_sdk_ios/ios/.gitignore | 5 +- arcgis_map_sdk_ios/ios/Assets/.gitkeep | 0 .../ios/Classes/ArcgisMapPlugin.h | 4 - .../ios/Classes/ArcgisMapPlugin.m | 18 ----- .../ios/arcgis_map_sdk_ios.podspec | 2 +- .../ios/arcgis_map_sdk_ios/Package.swift | 25 ++++++ .../arcgis_map_sdk_ios/ArcgisMapPlugin.swift} | 5 +- .../arcgis_map_sdk_ios}/ArcgisMapView.swift | 1 + .../ArcgisMapViewFactory.swift | 0 .../CenterPositionStreamHandler.swift | 1 + .../arcgis_map_sdk_ios}/GraphicsParser.swift | 1 + .../arcgis_map_sdk_ios}/JsonUtil.swift | 0 .../ManualLocationDataSource.swift | 0 .../Models/AnimationOptions.swift | 0 .../Models/ArcgisMapOptions.swift | 0 .../arcgis_map_sdk_ios}/Models/LatLng.swift | 0 .../arcgis_map_sdk_ios}/Models/MapColor.swift | 3 +- .../Symbols/PictureMarkerSymbolPayload.swift | 0 .../Symbols/SimpleFillSymbolPayload.swift | 0 .../Symbols/SimpleLineSymbolPayload.swift | 0 .../Symbols/SimpleMarkerSymbolPayload.swift | 0 .../Models/UserPosition.swift | 0 .../Models/ViewPadding.swift | 0 .../arcgis_map_sdk_ios}/ParseException.swift | 0 .../ZoomStreamHandler.swift | 1 + example/ios/.gitignore | 2 + example/ios/Podfile.lock | 17 ----- example/ios/Runner.xcodeproj/project.pbxproj | 27 ++++++- .../xcshareddata/xcschemes/Runner.xcscheme | 19 +++++ example/pubspec.lock | 76 +++++++++---------- 30 files changed, 122 insertions(+), 85 deletions(-) delete mode 100644 arcgis_map_sdk_ios/ios/Assets/.gitkeep delete mode 100644 arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.h delete mode 100644 arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.m create mode 100644 arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift rename arcgis_map_sdk_ios/ios/{Classes/SwiftArcgisMapPlugin.swift => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapPlugin.swift} (81%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/ArcgisMapView.swift (99%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/ArcgisMapViewFactory.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/CenterPositionStreamHandler.swift (97%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/GraphicsParser.swift (99%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/JsonUtil.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/ManualLocationDataSource.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/AnimationOptions.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/ArcgisMapOptions.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/LatLng.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/MapColor.swift (96%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/Symbols/PictureMarkerSymbolPayload.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/Symbols/SimpleFillSymbolPayload.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/Symbols/SimpleLineSymbolPayload.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/Symbols/SimpleMarkerSymbolPayload.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/UserPosition.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/Models/ViewPadding.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/ParseException.swift (100%) rename arcgis_map_sdk_ios/ios/{Classes => arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios}/ZoomStreamHandler.swift (97%) diff --git a/arcgis_map_sdk_ios/ios/.gitignore b/arcgis_map_sdk_ios/ios/.gitignore index 0c885071e..e266b1580 100644 --- a/arcgis_map_sdk_ios/ios/.gitignore +++ b/arcgis_map_sdk_ios/ios/.gitignore @@ -35,4 +35,7 @@ Icon? /Flutter/Generated.xcconfig /Flutter/ephemeral/ -/Flutter/flutter_export_environment.sh \ No newline at end of file +/Flutter/flutter_export_environment.sh + +.swiftpm/ +Package.resolved diff --git a/arcgis_map_sdk_ios/ios/Assets/.gitkeep b/arcgis_map_sdk_ios/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.h b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.h deleted file mode 100644 index 9df35be08..000000000 --- a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface ArcgisMapPlugin : NSObject -@end diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.m b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.m deleted file mode 100644 index 4884e5c8f..000000000 --- a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapPlugin.m +++ /dev/null @@ -1,18 +0,0 @@ -#import "ArcgisMapPlugin.h" - -#if __has_include() - -#import - -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "arcgis_map_sdk_ios-Swift.h" -#endif - -@implementation ArcgisMapPlugin -+ (void)registerWithRegistrar:(NSObject *)registrar { - [SwiftArcgisMapPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec index 771fbf77b..4971088cf 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec @@ -12,7 +12,7 @@ A new Flutter project. s.homepage = 'http://example.com' s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/**/*' + s.source_files = 'arcgis_map_sdk_ios/Sources/**/*' s.dependency 'Flutter' s.dependency 'ArcGIS-Runtime-Toolkit-iOS' s.platform = :ios, '13.0' diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift new file mode 100644 index 000000000..c16447b7e --- /dev/null +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "arcgis_map_sdk_ios", + platforms: [ + .iOS("14.0") + ], + products: [ + .library(name: "arcgis-map-sdk-ios", targets: ["arcgis_map_sdk_ios"]) + ], + dependencies: [ + .package(url: "https://github.com/Esri/arcgis-runtime-ios", .upToNextMinor(from: "100.15.0")), + ], + targets: [ + .target( + name: "arcgis_map_sdk_ios", + dependencies: [ + .product(name: "ArcGIS", package: "arcgis-runtime-ios") + ] + ), + ] +) diff --git a/arcgis_map_sdk_ios/ios/Classes/SwiftArcgisMapPlugin.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapPlugin.swift similarity index 81% rename from arcgis_map_sdk_ios/ios/Classes/SwiftArcgisMapPlugin.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapPlugin.swift index 20ec718d8..03e244736 100644 --- a/arcgis_map_sdk_ios/ios/Classes/SwiftArcgisMapPlugin.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapPlugin.swift @@ -2,12 +2,11 @@ import Flutter import UIKit import ArcGIS -public class SwiftArcgisMapPlugin: NSObject, FlutterPlugin { - +public class ArcgisMapPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { registrar.register(ArcgisMapViewFactory(registrar: registrar), withId: "") - let instance = SwiftArcgisMapPlugin() + let instance = ArcgisMapPlugin() let channel = FlutterMethodChannel( name: "dev.fluttercommunity.arcgis_map_sdk", binaryMessenger: registrar.messenger() diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift similarity index 99% rename from arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift index 1c570f7b3..a7d86fea4 100644 --- a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift @@ -1,5 +1,6 @@ import ArcGIS import Foundation +import Flutter class ArcgisMapView: NSObject, FlutterPlatformView { diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapViewFactory.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapViewFactory.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/ArcgisMapViewFactory.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapViewFactory.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/CenterPositionStreamHandler.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CenterPositionStreamHandler.swift similarity index 97% rename from arcgis_map_sdk_ios/ios/Classes/CenterPositionStreamHandler.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CenterPositionStreamHandler.swift index d7fe6fa8e..5a790aca8 100644 --- a/arcgis_map_sdk_ios/ios/Classes/CenterPositionStreamHandler.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CenterPositionStreamHandler.swift @@ -6,6 +6,7 @@ // import Foundation +import Flutter class CenterPositionStreamHandler: NSObject, FlutterStreamHandler { diff --git a/arcgis_map_sdk_ios/ios/Classes/GraphicsParser.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift similarity index 99% rename from arcgis_map_sdk_ios/ios/Classes/GraphicsParser.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift index a671614be..b2da69487 100644 --- a/arcgis_map_sdk_ios/ios/Classes/GraphicsParser.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift @@ -7,6 +7,7 @@ import Foundation import ArcGIS +import Flutter class GraphicsParser { let registrar: FlutterPluginRegistrar diff --git a/arcgis_map_sdk_ios/ios/Classes/JsonUtil.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/JsonUtil.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/JsonUtil.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/JsonUtil.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/ManualLocationDataSource.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/ManualLocationDataSource.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/AnimationOptions.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/AnimationOptions.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/ArcgisMapOptions.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/ArcgisMapOptions.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/ArcgisMapOptions.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/ArcgisMapOptions.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/LatLng.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/LatLng.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/MapColor.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/MapColor.swift similarity index 96% rename from arcgis_map_sdk_ios/ios/Classes/Models/MapColor.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/MapColor.swift index a19a5b2fc..44c7644a8 100644 --- a/arcgis_map_sdk_ios/ios/Classes/Models/MapColor.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/MapColor.swift @@ -3,6 +3,7 @@ // import Foundation +import UIKit struct MapColor: Codable { var red: Double @@ -20,4 +21,4 @@ extension MapColor { alpha: opacity ) } -} \ No newline at end of file +} diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/Symbols/PictureMarkerSymbolPayload.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/PictureMarkerSymbolPayload.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/Symbols/PictureMarkerSymbolPayload.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/PictureMarkerSymbolPayload.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/Symbols/SimpleFillSymbolPayload.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleFillSymbolPayload.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/Symbols/SimpleFillSymbolPayload.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleFillSymbolPayload.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/Symbols/SimpleLineSymbolPayload.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/Symbols/SimpleLineSymbolPayload.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/Symbols/SimpleMarkerSymbolPayload.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleMarkerSymbolPayload.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/Symbols/SimpleMarkerSymbolPayload.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleMarkerSymbolPayload.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/UserPosition.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/UserPosition.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/UserPosition.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/UserPosition.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/ViewPadding.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/ViewPadding.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/Models/ViewPadding.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/ViewPadding.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/ParseException.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ParseException.swift similarity index 100% rename from arcgis_map_sdk_ios/ios/Classes/ParseException.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ParseException.swift diff --git a/arcgis_map_sdk_ios/ios/Classes/ZoomStreamHandler.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ZoomStreamHandler.swift similarity index 97% rename from arcgis_map_sdk_ios/ios/Classes/ZoomStreamHandler.swift rename to arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ZoomStreamHandler.swift index 8a78362e0..092724b88 100644 --- a/arcgis_map_sdk_ios/ios/Classes/ZoomStreamHandler.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ZoomStreamHandler.swift @@ -6,6 +6,7 @@ // import Foundation +import Flutter class ZoomStreamHandler: NSObject, FlutterStreamHandler { diff --git a/example/ios/.gitignore b/example/ios/.gitignore index 7a7f9873a..fad3bebad 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -32,3 +32,5 @@ Runner/GeneratedPluginRegistrant.* !default.mode2v3 !default.pbxuser !default.perspectivev3 + +Package.resolved diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index beb177e9e..e9d4ff56a 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,36 +1,19 @@ PODS: - - ArcGIS-Runtime-SDK-iOS (100.14.1) - - ArcGIS-Runtime-Toolkit-iOS (100.14.0): - - ArcGIS-Runtime-SDK-iOS (~> 100.14.0) - - arcgis_map_sdk_ios (0.0.1): - - ArcGIS-Runtime-Toolkit-iOS - - Flutter - Flutter (1.0.0) - geolocator_apple (1.2.0): - Flutter DEPENDENCIES: - - arcgis_map_sdk_ios (from `.symlinks/plugins/arcgis_map_sdk_ios/ios`) - Flutter (from `Flutter`) - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) -SPEC REPOS: - trunk: - - ArcGIS-Runtime-SDK-iOS - - ArcGIS-Runtime-Toolkit-iOS - EXTERNAL SOURCES: - arcgis_map_sdk_ios: - :path: ".symlinks/plugins/arcgis_map_sdk_ios/ios" Flutter: :path: Flutter geolocator_apple: :path: ".symlinks/plugins/geolocator_apple/ios" SPEC CHECKSUMS: - ArcGIS-Runtime-SDK-iOS: 6ab51d28f8831ac73c00d34998cff3a555fe304f - ArcGIS-Runtime-Toolkit-iOS: e30bb45bd0bd0152bcb1ec73f9b99022a5c7d02d - arcgis_map_sdk_ios: deb0d9d2dfedca86984c0aa81e6245eed03c8344 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 geolocator_apple: d981750b9f47dbdb02427e1476d9a04397beb8d9 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index b475bba3b..203a25522 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 605FA5D163A7B2040C9667D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6DFBB57AA011A2DA64754497 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -54,6 +55,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 605FA5D163A7B2040C9667D1 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -88,7 +90,6 @@ 97C146EF1CF9000F007C117D /* Products */, EC58D96A64CE4D3D7740F25D /* Pods */, 3776939CBC3633F36418AFD9 /* Frameworks */, - 838A69020171B4AA9B387170 /* SimpleMarkerSymbolPayload.swift */, ); sourceTree = ""; }; @@ -122,7 +123,6 @@ AFBD59F3D7EBE98D9F0820DA /* Pods-Runner.release.xcconfig */, 48FDB075D82D2D5142D6BACF /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -147,6 +147,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -175,6 +178,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -363,6 +369,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -492,6 +499,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -515,6 +523,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -551,6 +560,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 5e31d3d34..128d5765a 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/example/pubspec.lock b/example/pubspec.lock index 91231b194..6071eddfb 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -47,42 +47,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" crypto: dependency: transitive description: @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" fixnum: dependency: transitive description: @@ -206,18 +206,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -238,10 +238,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -254,18 +254,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" plugin_platform_interface: dependency: transitive description: @@ -278,15 +278,15 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -299,42 +299,42 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.4" typed_data: dependency: transitive description: @@ -363,10 +363,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.1" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" From f4ccd88732f09de7ae0e92e6e9ef95a7d18c2f75 Mon Sep 17 00:00:00 2001 From: Tarek Tolba <98803924+TarekTolba1@users.noreply.github.com> Date: Tue, 6 May 2025 15:53:55 +0300 Subject: [PATCH 2/2] Upgrade iOS SDK to v200.7 (#90) * Upgrade arcgis sdk * Upgrade arcgis sdk + create MapContentView * Fix few issues after upgrading sdk * Fix few issues after upgrading sdk * Clean code * fix build * reformat code * Update CustomLocationProvider.swift * Fix location indicator example --------- Co-authored-by: Julian Bissekkou --- .../ios/arcgis_map_sdk_ios.podspec | 3 +- .../ios/arcgis_map_sdk_ios/Package.swift | 6 +- .../arcgis_map_sdk_ios/ArcgisMapView.swift | 398 +++++++++--------- .../CustomLocationProvider.swift | 48 +++ .../arcgis_map_sdk_ios/GraphicsParser.swift | 61 +-- .../ManualLocationDataSource.swift | 22 - .../arcgis_map_sdk_ios/MapContentView.swift | 84 ++++ .../Models/AnimationOptions.swift | 16 - .../arcgis_map_sdk_ios/Models/LatLng.swift | 4 +- .../Symbols/SimpleLineSymbolPayload.swift | 10 +- .../src/method_channel_arcgis_map_plugin.dart | 8 +- example/ios/Podfile | 2 +- example/ios/Podfile.lock | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 10 +- .../lib/location_indicator_example_page.dart | 2 +- 15 files changed, 385 insertions(+), 291 deletions(-) create mode 100644 arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift delete mode 100644 arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift create mode 100644 arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec index 4971088cf..b930899da 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec @@ -14,8 +14,7 @@ A new Flutter project. s.source = { :path => '.' } s.source_files = 'arcgis_map_sdk_ios/Sources/**/*' s.dependency 'Flutter' - s.dependency 'ArcGIS-Runtime-Toolkit-iOS' - s.platform = :ios, '13.0' + s.platform = :ios, '16.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift index c16447b7e..dde81e367 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift @@ -6,19 +6,19 @@ import PackageDescription let package = Package( name: "arcgis_map_sdk_ios", platforms: [ - .iOS("14.0") + .iOS("16.0") ], products: [ .library(name: "arcgis-map-sdk-ios", targets: ["arcgis_map_sdk_ios"]) ], dependencies: [ - .package(url: "https://github.com/Esri/arcgis-runtime-ios", .upToNextMinor(from: "100.15.0")), + .package(url: "https://github.com/Esri/arcgis-maps-sdk-swift", .upToNextMinor(from: "200.7.0")), ], targets: [ .target( name: "arcgis_map_sdk_ios", dependencies: [ - .product(name: "ArcGIS", package: "arcgis-runtime-ios") + .product(name: "ArcGIS", package: "arcgis-maps-sdk-swift") ] ), ] diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift index a7d86fea4..6088a4ba4 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift @@ -1,10 +1,10 @@ import ArcGIS import Foundation import Flutter +import SwiftUI -class ArcgisMapView: NSObject, FlutterPlatformView { - private let defaultGraphicsOverlay = AGSGraphicsOverlay() +class ArcgisMapView: NSObject, FlutterPlatformView { private let methodChannel: FlutterMethodChannel private let zoomEventChannel: FlutterEventChannel @@ -13,29 +13,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { private let centerPositionStreamHandler = CenterPositionStreamHandler() private let flutterPluginRegistrar: FlutterPluginRegistrar - private var mapLoadStatusObservation: NSKeyValueObservation? - - private var mapScaleObservation: NSKeyValueObservation? - private var mapVisibleAreaObservation: NSKeyValueObservation? - - private let initialZoom: Int - - private var mapView: AGSMapView - private let map = AGSMap() - private let graphicsOverlay = AGSGraphicsOverlay() - private let userIndicatorGraphic = AGSGraphic() - private let pinGraphic = AGSGraphic() - private let routeLineGraphic = AGSGraphic() - - private var routeLineGraphics = [AGSGraphic]() - - private var routePoints = Array() - - - private static let defaultDuration = 0.8 - + private var mapContentView: MapContentView + + private var hostingController: UIHostingController func view() -> UIView { - return mapView + return hostingController.view } init( @@ -61,85 +43,76 @@ class ArcgisMapView: NSObject, FlutterPlatformView { centerPositionEventChannel.setStreamHandler(centerPositionStreamHandler) if let apiKey = mapOptions.apiKey { - AGSArcGISRuntimeEnvironment.apiKey = apiKey + ArcGISEnvironment.apiKey = APIKey(apiKey) } if let licenseKey = mapOptions.licenseKey { do { - try AGSArcGISRuntimeEnvironment.setLicenseKey(licenseKey) + try ArcGISEnvironment.setLicense(with: LicenseKey(licenseKey)!) } catch { print("setLicenseKey failed. \(error)") } } + + let viewpoint = Viewpoint( + latitude: mapOptions.initialCenter.latitude, + longitude: mapOptions.initialCenter.longitude, + scale: ArcgisMapView.convertZoomLevelToMapScale(Int(mapOptions.zoom)) + ) - initialZoom = Int(mapOptions.zoom) - - mapView = AGSMapView.init(frame: frame) - + mapContentView = MapContentView(viewModel: MapViewModel(viewpoint: viewpoint)) + + // Embed the SwiftUI MapView into a UIHostingController + hostingController = UIHostingController(rootView: mapContentView) + hostingController.view.frame = frame + hostingController.view.backgroundColor = .clear + super.init() if let isAttributionTextVisible = mapOptions.isAttributionTextVisible { - mapView.isAttributionTextVisible = isAttributionTextVisible + mapContentView.viewModel.attributionBarHidden = !isAttributionTextVisible } if mapOptions.basemap != nil { - map.basemap = AGSBasemap(style: parseBaseMapStyle(mapOptions.basemap!)) + mapContentView.viewModel.map.basemap = Basemap(style: parseBaseMapStyle(mapOptions.basemap!)) } else { let layers = mapOptions.vectorTilesUrls!.map { url in - AGSArcGISVectorTiledLayer(url: URL(string: url)!) + ArcGISVectorTiledLayer(url: URL(string: url)!) } - map.basemap = AGSBasemap(baseLayers: layers, referenceLayers: nil) + mapContentView.viewModel.map.basemap = Basemap(baseLayers: layers) } - map.minScale = convertZoomLevelToMapScale(mapOptions.minZoom) - map.maxScale = convertZoomLevelToMapScale(mapOptions.maxZoom) - - mapView.map = map - mapView.graphicsOverlays.add(defaultGraphicsOverlay) + mapContentView.viewModel.map.minScale = ArcgisMapView.convertZoomLevelToMapScale(mapOptions.minZoom) + mapContentView.viewModel.map.maxScale = ArcgisMapView.convertZoomLevelToMapScale(mapOptions.maxZoom) - mapScaleObservation = mapView.observe(\.mapScale) { [weak self] (map, notifier) in - DispatchQueue.main.async { - guard let self = self else { - return - } - let newZoom = self.convertScaleToZoomLevel(self.mapView.mapScale) - self.zoomStreamHandler.addZoom(zoom: newZoom) - } + mapContentView.viewModel.onScaleChanged = { [weak self] scale in + guard let self = self else { return } + guard !scale.isNaN else { return } + let newZoom = self.convertScaleToZoomLevel(scale) + self.zoomStreamHandler.addZoom(zoom: newZoom) } - mapVisibleAreaObservation = mapView.observe(\.visibleArea) { [weak self] (map, notifier) in - DispatchQueue.main.async { - guard let self = self else { - return - } - guard let center = self.mapView.visibleArea?.extent.center else { - return - } - guard let wgs84Center = AGSGeometryEngine.projectGeometry(center, to: .wgs84()) as? AGSPoint else {return} + mapContentView.viewModel.onVisibleAreaChanged = { [weak self] polygon in + guard let self = self else { return } + let center = polygon.extent.center + if let wgs84Center = GeometryEngine.project(center, into: .wgs84) as? Point { self.centerPositionStreamHandler.add(center: LatLng(latitude: wgs84Center.y, longitude: wgs84Center.x)) } } - - let viewpoint = AGSViewpoint( - latitude: mapOptions.initialCenter.latitude, - longitude: mapOptions.initialCenter.longitude, - scale: convertZoomLevelToMapScale(Int(mapOptions.zoom)) - ) - mapView.setViewpoint(viewpoint) - setMapInteractive(mapOptions.isInteractive) setupMethodChannel() - - mapLoadStatusObservation = map.observe(\.loadStatus, options: .initial) { [weak self] (map, notifier) in - DispatchQueue.main.async { - let status = map.loadStatus - self?.notifyStatus(status) - } - } + + mapContentView.viewModel.onLoadStatusChanged = { [weak self] status in + guard let self = self else { return } + DispatchQueue.main.async { + self.notifyStatus(status) + } + } } private func setupMethodChannel() { methodChannel.setMethodCallHandler({ [self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch (call.method) { + case "on_init_complete": waitForViewToInit(call, result) case "zoom_in": onZoomIn(call, result) case "zoom_out": onZoomOut(call, result) case "rotate" : onRotate(call, result) @@ -170,12 +143,19 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } }) } + + private func waitForViewToInit(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { + if mapContentView.viewModel.mapViewProxy != nil { + result(true) + } else { + mapContentView.viewModel.onViewInit = { [weak self] in + result(true) + } + } + } private func onZoomIn(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if(mapView.mapScale.isNaN) { - result(FlutterError(code: "unknown_error", message: "MapView.mapScale is NaN. Maybe the map is not completely loaded.", details: nil)) - return - } + let currentScale = mapContentView.viewModel.viewpoint.targetScale guard let args = call.arguments as? [String: Any] else { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) @@ -186,23 +166,26 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "lodFactor not provided", details: nil)) return } - - let currentZoomLevel = convertScaleToZoomLevel(mapView.mapScale) + + let currentZoomLevel = convertScaleToZoomLevel(currentScale) let totalZoomLevel = currentZoomLevel + lodFactor - if (totalZoomLevel > convertScaleToZoomLevel(map.maxScale)) { - return + if let maxScale = mapContentView.viewModel.map.maxScale { + if totalZoomLevel > convertScaleToZoomLevel(maxScale) { + result(true) + return + } } - let newScale = convertZoomLevelToMapScale(totalZoomLevel) - mapView.setViewpointScale(newScale) { _ in - result(true) + let newScale = ArcgisMapView.convertZoomLevelToMapScale(totalZoomLevel) + Task { + do { + await mapContentView.viewModel.mapViewProxy?.setViewpointScale(newScale) + result(true) + } } } private func onZoomOut(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if(mapView.mapScale.isNaN) { - result(FlutterError(code: "unknown_error", message: "MapView.mapScale is NaN. Maybe the map is not completely loaded.", details: nil)) - return - } + let currentScale = mapContentView.viewModel.viewpoint.targetScale guard let args = call.arguments as? [String: Any] else { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) @@ -214,14 +197,20 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - let currentZoomLevel = convertScaleToZoomLevel(mapView.mapScale) + let currentZoomLevel = convertScaleToZoomLevel(currentScale) let totalZoomLevel = currentZoomLevel - lodFactor - if (totalZoomLevel < convertScaleToZoomLevel(map.minScale)) { - return + if let minScale = mapContentView.viewModel.map.minScale { + if totalZoomLevel < convertScaleToZoomLevel(minScale) { + result(true) + return + } } - let newScale = convertZoomLevelToMapScale(totalZoomLevel) - mapView.setViewpointScale(newScale) { success in - result(success) + let newScale = ArcgisMapView.convertZoomLevelToMapScale(totalZoomLevel) + Task { + do { + let success = await mapContentView.viewModel.mapViewProxy?.setViewpointScale(newScale) + result(success) + } } } @@ -230,9 +219,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) return } - - mapView.setViewpointRotation(angleDouble) { success in - result(success) + Task { + do { + let success = await mapContentView.viewModel.mapViewProxy?.setViewpointRotation(angleDouble) + result(success) + } } } @@ -245,11 +236,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { do { let padding: ViewPadding = try JsonUtil.objectOfJson(args) - mapView.contentInset = UIEdgeInsets( + mapContentView.viewModel.contentInsets = EdgeInsets( top: padding.top, - left: padding.left, + leading: padding.left, bottom: padding.bottom, - right: padding.right + trailing: padding.right ) result(true) @@ -263,29 +254,28 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) return } - do { - let point: LatLng = try JsonUtil.objectOfJson(args["point"] as! Dictionary) - let zoomLevel = args["zoomLevel"] as? Int - let animationDict = args["animationOptions"] as? Dictionary - let animationOptions: AnimationOptions? = animationDict == nil ? nil : try JsonUtil.objectOfJson(animationDict!) - - let scale: Double - - if let zoomLevel = zoomLevel { - scale = convertZoomLevelToMapScale(zoomLevel) - } else { - scale = mapView.mapScale.isNaN ? convertZoomLevelToMapScale(initialZoom) : mapView.mapScale - } - - mapView.setViewpoint( - AGSViewpoint(center: point.toAGSPoint(), scale: scale), - duration: (animationOptions?.duration ?? 0) / 1000, - curve: animationOptions?.arcgisAnimationCurve() ?? .linear - ) { success in + Task { + do { + let point: LatLng = try JsonUtil.objectOfJson(args["point"] as! Dictionary) + let zoomLevel = args["zoomLevel"] as? Int + let animationDict = args["animationOptions"] as? Dictionary + let animationOptions: AnimationOptions? = animationDict == nil ? nil : try JsonUtil.objectOfJson(animationDict!) + + let scale: Double + + if let zoomLevel = zoomLevel { + scale = ArcgisMapView.convertZoomLevelToMapScale(zoomLevel) + } else { + scale = mapContentView.viewModel.viewpoint.targetScale + } + let success = await mapContentView.viewModel.mapViewProxy?.setViewpoint( + Viewpoint(center: point.toAGSPoint(), scale: scale), + duration: (animationOptions?.duration ?? 0) / 1000 + ) result(success) + } catch { + result(FlutterError(code: "error", message: "Error onMoveCamera. Provided: \(args)", details: nil)) } - } catch { - result(FlutterError(code: "error", message: "Error onMoveCamera. Provided: \(args)", details: nil)) } } @@ -294,28 +284,27 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) return } - - do { - let payload: MoveToPointsPayload = try JsonUtil.objectOfJson(args) - let polyline = AGSPolyline(points: payload.points.map { latLng in AGSPoint(x: latLng.longitude, y:latLng.latitude, spatialReference: .wgs84()) }) - - if(payload.padding != nil) { - mapView.setViewpointGeometry(polyline.extent, padding: payload.padding!) { success in + Task { + do { + let payload: MoveToPointsPayload = try JsonUtil.objectOfJson(args) + let polyline = Polyline(points: payload.points.map { latLng in Point(x: latLng.longitude, y:latLng.latitude, spatialReference: .wgs84) }) + + if(payload.padding != nil) { + let success = try await mapContentView.viewModel.mapViewProxy!.setViewpointGeometry(polyline.extent, padding: payload.padding!) result(success) - } - } else { - mapView.setViewpointGeometry(polyline.extent) { success in + } else { + let success = try await mapContentView.viewModel.mapViewProxy!.setViewpointGeometry(polyline.extent) result(success) } + } catch { + result(FlutterError(code: "error", message: "Error onMoveCameraToPoints. Provided: \(args)", details: nil)) } - } catch { - result(FlutterError(code: "error", message: "Error onMoveCameraToPoints. Provided: \(args)", details: nil)) } } private func onAddGraphic(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { let parser = GraphicsParser(registrar: flutterPluginRegistrar) - var newGraphics = [AGSGraphic]() + var newGraphics = [Graphic]() do { newGraphics.append(contentsOf: try parser.parse(dictionary: call.arguments as! Dictionary)) } catch { @@ -324,8 +313,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } - let existingIds = defaultGraphicsOverlay.graphics.compactMap { object in - let graphic = object as! AGSGraphic + let existingIds = mapContentView.viewModel.defaultGraphicsOverlay.graphics.compactMap { object in + let graphic = object as! Graphic return graphic.attributes["id"] as? String } @@ -347,7 +336,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { // them in this for loop instead. // ArcGis is the best <3. newGraphics.forEach { - defaultGraphicsOverlay.graphics.add($0) + mapContentView.viewModel.defaultGraphicsOverlay.addGraphic($0) } result(true) } @@ -358,15 +347,15 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - let newGraphics = defaultGraphicsOverlay.graphics.filter({ element in - let graphic = element as! AGSGraphic - let id = graphic.attributes["id"] as? String - return id != graphicId - }) + let selectedGraphics = mapContentView.viewModel.defaultGraphicsOverlay.graphics.filter { element in + if let graphic = element as? Graphic, + let id = graphic.attributes["id"] as? String { + return id == graphicId + } + return false + } - defaultGraphicsOverlay.graphics.removeAllObjects() - defaultGraphicsOverlay.graphics.addObjects(from: newGraphics) - + mapContentView.viewModel.defaultGraphicsOverlay.removeGraphics(selectedGraphics) result(true) } @@ -376,17 +365,20 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - map.basemap = AGSBasemap(style: parseBaseMapStyle(baseMapString)) - + mapContentView.viewModel.map.basemap = Basemap(style: parseBaseMapStyle(baseMapString)) result(true) } private func onRetryLoad(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - mapView.map!.retryLoad() - result(true) + Task { + do { + try await mapContentView.viewModel.map.retryLoad() + result(true) + } + } } - private func notifyStatus(_ status: AGSLoadStatus) { + private func notifyStatus(_ status: LoadStatus) { methodChannel.invokeMethod("onStatusChanged", arguments: status.jsonValue()) } @@ -406,16 +398,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } private func setMapInteractive(_ enabled: Bool) { - mapView.interactionOptions.isZoomEnabled = enabled - mapView.interactionOptions.isPanEnabled = enabled - mapView.interactionOptions.isFlickEnabled = enabled - mapView.interactionOptions.isRotateEnabled = enabled - mapView.interactionOptions.isEnabled = enabled - // don't set "isMagnifierEnabled" since we don't want to use this feature + mapContentView.viewModel.interactionModes = enabled ? .all : [] } - private func parseBaseMapStyle(_ string: String) -> AGSBasemapStyle { - let baseMapStyle = AGSBasemapStyle.allCases.first { enumValue in + private func parseBaseMapStyle(_ string: String) -> Basemap.Style { + let baseMapStyle = Basemap.Style.allCases.first { enumValue in enumValue.getJsonValue() == string } if baseMapStyle == nil { @@ -437,42 +424,47 @@ class ArcgisMapView: NSObject, FlutterPlatformView { * Convert zoom level to map scale * https://developers.arcgis.com/documentation/mapping-apis-and-services/reference/zoom-levels-and-scale/#conversion-tool * */ - private func convertZoomLevelToMapScale(_ zoomLevel: Int) -> Double { + private static func convertZoomLevelToMapScale(_ zoomLevel: Int) -> Double { 591657527 * (exp(-0.693 * Double(zoomLevel))) } private func onStartLocationDisplayDataSource(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - mapView.locationDisplay.dataSource.start { error in - if let error = error { + Task { + do { + try await mapContentView.viewModel.locationDisplay.dataSource.start(); + result(true) + } + catch{ let flutterError = FlutterError( code: "generic_error", message: "Failed to start data source: \(error.localizedDescription)", details: nil ) result(flutterError) - } else { - result(true) } } } private func onStopLocationDisplayDataSource(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - mapView.locationDisplay.dataSource.stop { - result(true) + Task { + do { + await mapContentView.viewModel.locationDisplay.dataSource.stop() + result(true) + } } } private func onSetLocationDisplayDefaultSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - operationWithSymbol(call, result) { mapView.locationDisplay.defaultSymbol = $0 } + operationWithSymbol(call, result) { mapContentView.viewModel.locationDisplay.defaultSymbol = $0 } } private func onSetLocationDisplayAccuracySymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - operationWithSymbol(call, result) { mapView.locationDisplay.accuracySymbol = $0 } + operationWithSymbol(call, result) { mapContentView.viewModel.locationDisplay.accuracySymbol = $0 } } private func onSetLocationDisplayPingAnimationSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - operationWithSymbol(call, result) { mapView.locationDisplay.pingAnimationSymbol = $0 } + operationWithSymbol(call, result) { mapContentView.viewModel.locationDisplay.pingAnimationSymbol = $0 } } private func onSetLocationDisplayUseCourseSymbolOnMove(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { @@ -481,13 +473,13 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.locationDisplay.useCourseSymbolOnMovement = active + mapContentView.viewModel.locationDisplay.usesCourseSymbolOnMovement = active result(true) } private func onUpdateLocationDisplaySourcePositionManually(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let dataSource = mapView.locationDisplay.dataSource - guard let source = dataSource as? ManualLocationDataSource else { + let dataSource = mapContentView.viewModel.locationDisplay.dataSource + guard let source = dataSource as? CustomLocationDataSource else { result(FlutterError(code: "invalid_state", message: "Expected ManualLocationDataSource but got \(dataSource)", details: nil)) return } @@ -497,7 +489,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - source.setNewLocation(position) + source.currentProvider?.setNewLocation(position) result(true) } @@ -512,14 +504,14 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.locationDisplay.autoPanMode = autoPanMode + mapContentView.viewModel.locationDisplay.autoPanMode = autoPanMode result(true) } private func onGetAutoPanMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { // autoPanMode.rawValue is any of [0; 3]: // https://developers.arcgis.com/ios/api-reference/_a_g_s_location_display_8h.html - guard let stringName = mapView.locationDisplay.autoPanMode.toName() else { + guard let stringName = mapContentView.viewModel.locationDisplay.autoPanMode.toName() else { result(FlutterError(code: "invalid_data", message: "AutoPanMode has invalid state", details: nil)) return } @@ -532,16 +524,16 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.locationDisplay.wanderExtentFactor = Float(factor) + mapContentView.viewModel.locationDisplay.wanderExtentFactor = Float(factor) result(true) } private func onGetWanderExtentFactor(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - return result(mapView.locationDisplay.wanderExtentFactor) + return result(mapContentView.viewModel.locationDisplay.wanderExtentFactor) } private func onSetLocationDisplayDataSourceType(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if(mapView.locationDisplay.dataSource.status == .started) { + if(mapContentView.viewModel.locationDisplay.dataSource.status == .started) { result(FlutterError(code: "invalid_state", message: "Current data source is running. Make sure to stop it before setting a new data source", details: nil)) return } @@ -553,10 +545,12 @@ class ArcgisMapView: NSObject, FlutterPlatformView { switch(type) { case "manual" : - mapView.locationDisplay.dataSource = ManualLocationDataSource() + mapContentView.viewModel.locationDisplay.dataSource = CustomLocationDataSource{ + return CustomLocationProvider() + } result(true) case "system" : - mapView.locationDisplay.dataSource = AGSCLLocationDataSource() + mapContentView.viewModel.locationDisplay.dataSource = SystemLocationDataSource() result(true) default: result(FlutterError(code: "invalid_data", message: "Unknown data source type \(String(describing: type))", details: nil)) @@ -569,26 +563,26 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.isAttributionTextVisible = isVisible + mapContentView.viewModel.attributionBarHidden = !isVisible result(true) } private func onExportImage(_ result: @escaping FlutterResult) { - mapView.exportImage { image, error in - if let error = error { - result(FlutterError(code: "export_error", message: error.localizedDescription, details: nil)) - return - } - - if let image = image, let imageData = image.pngData() { - result(FlutterStandardTypedData(bytes: imageData)) - } else { - result(FlutterError(code: "conversion_error", message: "Failed to convert image to PNG data", details: nil)) - } - } - } - - private func operationWithSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, handler: (AGSSymbol) -> Void) { + Task { + do { + let image = try await mapContentView.viewModel.mapViewProxy!.exportImage() + if let imageData = image.pngData() { + result(FlutterStandardTypedData(bytes: imageData)) + } else { + result(FlutterError(code: "conversion_error", message: "Failed to convert image to PNG data", details: nil)) + } + } catch { + result(FlutterError(code: "export_error", message: error.localizedDescription, details: nil)) + } + } + } + + private func operationWithSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, handler: (Symbol) -> Void) { do { guard let args = call.arguments as? [String: Any] else { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) @@ -604,8 +598,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } } -extension AGSBasemapStyle: CaseIterable { - public static var allCases: [AGSBasemapStyle] { +extension Basemap.Style: CaseIterable { + public static var allCases: [Basemap.Style] { [ .arcGISImagery, .arcGISImageryStandard, @@ -657,7 +651,7 @@ extension AGSBasemapStyle: CaseIterable { } } -extension AGSBasemapStyle { +extension Basemap.Style { func getJsonValue() -> String? { switch self { case .arcGISImagery: @@ -763,19 +757,17 @@ struct MoveToPointsPayload : Codable { let padding : Double? } -extension AGSLoadStatus { +extension LoadStatus { func jsonValue() -> String { switch self { case .loaded: return "loaded" case .loading: return "loading" - case .failedToLoad: - return "failedToLoad" + case .failed: + return "failed" case .notLoaded: return "notLoaded" - case .unknown: - return "unknown" @unknown default: return "unknown" } @@ -783,7 +775,7 @@ extension AGSLoadStatus { } extension String { - func autoPanModeFromString() -> AGSLocationDisplayAutoPanMode? { + func autoPanModeFromString() -> LocationDisplay.AutoPanMode? { switch self { case "compassNavigation": return .compassNavigation @@ -799,7 +791,7 @@ extension String { } } -extension AGSLocationDisplayAutoPanMode { +extension LocationDisplay.AutoPanMode { func toName() -> String? { switch self { case .off: diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift new file mode 100644 index 000000000..586f10dbe --- /dev/null +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift @@ -0,0 +1,48 @@ +// +// Created by Julian Bissekkou on 27.11.23. +// + +import Foundation +import ArcGIS + +final class CustomLocationProvider: LocationProvider { + private var locationContinuation: AsyncStream.Continuation? + private var headingContinuation: AsyncStream.Continuation? + + + // Exposed stream + var locations: AsyncStream { + AsyncStream { @Sendable continuation in + self.locationContinuation = continuation + continuation.onTermination = { _ in + self.locationContinuation = nil + } + } + } + + var headings: AsyncStream { + AsyncStream { continuation in + self.headingContinuation = continuation + continuation.onTermination = { @Sendable _ in + self.headingContinuation = nil + } + } + } + + // Push location from outside + public func setNewLocation(_ position: UserPosition) { + let loc = Location( + position: position.latLng.toAGSPoint(), + horizontalAccuracy: position.accuracy ?? 0, + verticalAccuracy: position.accuracy ?? 0, + speed: position.velocity ?? 0, + course: position.heading ?? 0, + isLastKnown: false + ) + locationContinuation?.yield(loc) + if let heading = position.heading { + headingContinuation?.yield(heading) + } + } +} + diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift index b2da69487..7ed30b1ea 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift @@ -16,10 +16,10 @@ class GraphicsParser { self.registrar = registrar } - func parse(dictionary: Dictionary) throws -> [AGSGraphic] { + func parse(dictionary: Dictionary) throws -> [Graphic] { let type = dictionary["type"] as! String - let newGraphics: [AGSGraphic] + let newGraphics: [Graphic] switch (type) { case "point": newGraphics = try! parsePoint(dictionary) @@ -31,26 +31,29 @@ class GraphicsParser { throw ParseException(message: "Unknown graphic type: \(type)") } - let attributes = dictionary["attributes"] as? Dictionary - if let attributes = attributes { - newGraphics.forEach { - $0.attributes.addEntries(from: attributes) + // Apply attributes to each graphic, if present + if let attributes = dictionary["attributes"] as? [String: Any] { + newGraphics.forEach { graphic in + for (key, value) in attributes { + graphic.setAttributeValue(value, forKey: key) + } } } + return newGraphics } - private func parsePoint(_ dictionary: [String: Any]) throws -> [AGSGraphic] { + private func parsePoint(_ dictionary: [String: Any]) throws -> [Graphic] { let point: LatLng = try! JsonUtil.objectOfJson(dictionary["point"] as! Dictionary) - let graphic = AGSGraphic() + let graphic = Graphic() graphic.geometry = point.toAGSPoint() graphic.symbol = try! parseSymbol(dictionary["symbol"] as! Dictionary) return [graphic] } - private func parsePolyline(_ dictionary: [String: Any]) throws -> [AGSGraphic] { + private func parsePolyline(_ dictionary: [String: Any]) throws -> [Graphic] { let payload: PathPayload = try! JsonUtil.objectOfJson(dictionary) return try payload.paths.map { coordinates in @@ -59,28 +62,28 @@ class GraphicsParser { throw ParseException(message: "Size of coordinates need to be at least 2. Got \(array)") } if(array.count > 2) { - return AGSPoint(x: array[0], y: array[1], z: array[2], spatialReference: .wgs84()) + return Point(x: array[0], y: array[1], z: array[2], spatialReference: .wgs84) } - return AGSPoint(x: array[0], y: array[1], spatialReference: .wgs84()) + return Point(x: array[0], y: array[1], spatialReference: .wgs84) } - let graphic = AGSGraphic() - graphic.geometry = AGSPolyline(points: points) + let graphic = Graphic() + graphic.geometry = Polyline(points: points) graphic.symbol = try! parseSymbol(dictionary["symbol"] as! Dictionary) return graphic } } - private func parsePolygon(_ dictionary: [String: Any]) throws -> [AGSGraphic] { + private func parsePolygon(_ dictionary: [String: Any]) throws -> [Graphic] { let payload: PolygonPayload = try! JsonUtil.objectOfJson(dictionary) return payload.rings.map { ring in - let graphic = AGSGraphic() + let graphic = Graphic() let points = ring.map { coordinate in - AGSPoint(x: coordinate[0], y: coordinate[1], spatialReference: .wgs84()) + Point(x: coordinate[0], y: coordinate[1], spatialReference: .wgs84) } - graphic.geometry = AGSPolygon(points: points) + graphic.geometry = Polygon(points: points) graphic.symbol = try! parseSymbol(dictionary["symbol"] as! Dictionary) return graphic @@ -89,7 +92,7 @@ class GraphicsParser { // region symbol parsing - func parseSymbol(_ dictionary: [String: Any]) throws -> AGSSymbol { + func parseSymbol(_ dictionary: [String: Any]) throws -> Symbol { let type = dictionary["type"] as! String; switch (type) { case "simple-marker": @@ -105,13 +108,13 @@ class GraphicsParser { } } - private func parseSimpleMarkerSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parseSimpleMarkerSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: SimpleMarkerSymbolPayload = try! JsonUtil.objectOfJson(dictionary) - let symbol = AGSSimpleMarkerSymbol() + let symbol = SimpleMarkerSymbol() symbol.color = payload.color.toUIColor() symbol.size = payload.size - symbol.outline = AGSSimpleLineSymbol( + symbol.outline = SimpleLineSymbol( style: .solid, color: payload.outlineColor.toUIColor(), width: payload.outlineWidth @@ -119,13 +122,13 @@ class GraphicsParser { return symbol } - private func parseSimpleFillMarkerSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parseSimpleFillMarkerSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: SimpleFillSymbolPayload = try! JsonUtil.objectOfJson(dictionary) - let symbol = AGSSimpleFillSymbol() + let symbol = SimpleFillSymbol() symbol.color = payload.fillColor.toUIColor() - let outline = AGSSimpleLineSymbol() + let outline = SimpleLineSymbol() outline.width = payload.outlineWidth outline.color = payload.outlineColor.toUIColor() symbol.outline = outline @@ -133,12 +136,12 @@ class GraphicsParser { return symbol } - private func parsePictureMarkerSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parsePictureMarkerSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: PictureMarkerSymbolPayload = try! JsonUtil.objectOfJson(dictionary) if(!payload.assetUri.isWebUrl()) { let uiImage = getFlutterUiImage(payload.assetUri) - let symbol = AGSPictureMarkerSymbol(image: uiImage!) + let symbol = PictureMarkerSymbol(image: uiImage!) symbol.width = payload.width symbol.height = payload.height symbol.offsetX = payload.xOffset @@ -146,7 +149,7 @@ class GraphicsParser { return symbol } - let symbol = AGSPictureMarkerSymbol(url: URL(string: payload.assetUri)!) + let symbol = PictureMarkerSymbol(url: URL(string: payload.assetUri)!) symbol.width = payload.width symbol.height = payload.height symbol.offsetX = payload.xOffset @@ -161,9 +164,9 @@ class GraphicsParser { return UIImage(named: path!) } - private func parseSimpleLineSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parseSimpleLineSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: SimpleLineSymbolPayload = try! JsonUtil.objectOfJson(dictionary) - let symbol = AGSSimpleLineSymbol() + let symbol = SimpleLineSymbol() if let color = payload.color { symbol.color = color.toUIColor() diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift deleted file mode 100644 index 0d266a535..000000000 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// ManualLocationDataSource.swift -// arcgis_map_sdk_ios -// -// Created by Julian Bissekkou on 27.11.23. -// - -import Foundation -import ArcGIS - -class ManualLocationDataSource: AGSLocationDataSource { - public func setNewLocation(_ position: UserPosition) { - let loc = AGSLocation( - position: position.latLng.toAGSPoint(), - horizontalAccuracy: position.accuracy ?? 0, - velocity: position.velocity ?? 0, - course: position.heading ?? 0, - lastKnown: false - ) - didUpdate(loc) - } -} diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift new file mode 100644 index 000000000..ba06ae400 --- /dev/null +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift @@ -0,0 +1,84 @@ +// +// Created by Tarek Tolba on 29/04/2025. +// + +import SwiftUI +import ArcGIS +import CoreLocation + + +struct MapContentView: View { + @ObservedObject var viewModel: MapViewModel + + init(viewModel: MapViewModel) { + self.viewModel = viewModel + } + + var body: some View { + MapViewReader { mapViewProxy in + MapView( + map: viewModel.map, + viewpoint: viewModel.viewpoint, + graphicsOverlays: [viewModel.defaultGraphicsOverlay] + ) + .attributionBarHidden(viewModel.attributionBarHidden) + .locationDisplay(viewModel.locationDisplay) + .contentInsets(viewModel.contentInsets) + .interactionModes(viewModel.interactionModes) + .onViewpointChanged(kind: .centerAndScale) { newViewpoint in + viewModel.viewpoint = newViewpoint + } + .onScaleChanged { scale in + viewModel.onScaleChanged?(scale) + } + .onVisibleAreaChanged { polygon in + viewModel.onVisibleAreaChanged?(polygon) + } + .onChange(of: viewModel.map.basemap?.loadStatus) { newValue in + if let newValue { + viewModel.onLoadStatusChanged?(newValue) + } + } + .task { + // Store the mapViewProxy for external access + viewModel.mapViewProxy = mapViewProxy + viewModel.onViewInit?(); + } + .onDisappear { + viewModel.stopLocationDataSource() + // Clear the mapViewProxy reference when view disappears + viewModel.mapViewProxy = nil + } + .ignoresSafeArea(edges: .all) + } + } +} + + +class MapViewModel: ObservableObject { + let map = Map() + let locationDisplay = LocationDisplay() + + @Published var viewpoint: Viewpoint + @Published var mapViewProxy: MapViewProxy? + @Published var attributionBarHidden: Bool = false + @Published var contentInsets: EdgeInsets = EdgeInsets() + @Published var interactionModes: MapViewInteractionModes = .all + @Published var defaultGraphicsOverlay = GraphicsOverlay() + + var onScaleChanged: ((Double) -> Void)? + var onVisibleAreaChanged: ((Polygon) -> Void)? + var onLoadStatusChanged: ((LoadStatus) -> Void)? + var onViewInit: (() -> Void)? + + init(viewpoint : Viewpoint) { + self.viewpoint = viewpoint + } + + /// Stops the location data source. + func stopLocationDataSource() { + Task { + await locationDisplay.dataSource.stop() + } + } +} diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift index f4e65e3b2..d6434e708 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift @@ -13,19 +13,3 @@ struct AnimationOptions: Codable { var animationCurve: String } -extension AnimationOptions { - func arcgisAnimationCurve() -> AGSAnimationCurve { - switch animationCurve { - case "linear": - return .linear - case "easeIn": - return .easeInCirc - case "easeOut": - return .easeOutCirc - case "easeInOut": - return .easeInOutCirc - default: - return .linear - } - } -} diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift index 3d7a14320..2922ac960 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift @@ -15,7 +15,7 @@ struct LatLng: Codable { } extension LatLng { - func toAGSPoint() -> AGSPoint { - AGSPoint(x: longitude, y: latitude, spatialReference: .wgs84()) + func toAGSPoint() -> Point { + Point(x: longitude, y: latitude, spatialReference: .wgs84) } } diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift index fd159b27b..08f2d2ce7 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift @@ -33,7 +33,7 @@ enum PolylineStyle: String, Codable { case shortDot case solid - func toAGSStyle() -> AGSSimpleLineSymbolStyle { + func toAGSStyle() -> SimpleLineSymbol.Style { switch (self) { case .dash: return .dash @@ -48,7 +48,7 @@ enum PolylineStyle: String, Codable { case .longDashDotDot: return .longDashDot case .none: - return .null + return .noLine case .shortDash: return .shortDash case .shortDashDot: @@ -68,7 +68,7 @@ enum MarkerPlacement: String, Codable { case end case beginEnd - func toAGSStyle() -> AGSSimpleLineSymbolMarkerPlacement { + func toAGSStyle() -> SimpleLineSymbol.MarkerPlacement { switch (self) { case .begin: return .begin @@ -88,12 +88,12 @@ enum MarkerStyle: String, Codable { self = try MarkerStyle(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .none } - func toAGSStyle() -> AGSSimpleLineSymbolMarkerStyle { + func toAGSStyle() -> SimpleLineSymbol.MarkerStyle { switch (self) { case .arrow: return .arrow case .none: - return .none + return .noMarkers } } } diff --git a/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart b/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart index 3b4061026..8b0231357 100644 --- a/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart +++ b/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:arcgis_map_sdk_method_channel/src/model_extension.dart'; import 'package:arcgis_map_sdk_platform_interface/arcgis_map_sdk_platform_interface.dart'; import 'package:flutter/services.dart'; @@ -14,7 +16,11 @@ class MethodChannelArcgisMapPlugin extends ArcgisMapPlatform { /// This method is called when the plugin is first initialized. @override - Future init(int mapId) async {} + Future init(int mapId) async { + if (Platform.isIOS) { + return _methodChannelBuilder(mapId).invokeMethod('on_init_complete'); + } + } @override Future addFeatureLayer( diff --git a/example/ios/Podfile b/example/ios/Podfile index 10f3c9b47..dfab90da9 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '13.0' +platform :ios, '16.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e9d4ff56a..db90865b1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -17,6 +17,6 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 geolocator_apple: d981750b9f47dbdb02427e1476d9a04397beb8d9 -PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b +PODFILE CHECKSUM: ae273562e2241d2b1bd4b6d2d349a48b8fe1a2a7 COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 203a25522..582383e83 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -179,7 +179,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; @@ -369,7 +369,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -499,7 +499,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -523,7 +523,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -562,7 +562,7 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; diff --git a/example/lib/location_indicator_example_page.dart b/example/lib/location_indicator_example_page.dart index deca08208..6609f8a0b 100644 --- a/example/lib/location_indicator_example_page.dart +++ b/example/lib/location_indicator_example_page.dart @@ -135,7 +135,6 @@ class _LocationIndicatorExamplePageState await Geolocator.requestPermission(); final location = await Geolocator.getLastKnownPosition(); if (!mounted || location == null) return; - await _controller!.moveCamera( point: LatLng(location.latitude, location.longitude), zoomLevel: 16, @@ -166,6 +165,7 @@ class _LocationIndicatorExamplePageState Future _switchLocationSource() async { await _controller!.locationDisplay.stopSource(); + _isStarted = false; await _controller!.setLocationDisplay( _isManualLocationSource ? ArcgisLocationDisplay()