diff --git a/.gitignore b/.gitignore index f03e6ffb..1c3e4bb0 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,12 @@ .pub/ **/build/ +# Swift Package Manager +**/.build/ +**/.swiftpm/ +# SPM lock files for library packages (not the main app) +**/packages/google_mlkit_*/ios/google_mlkit_*/Package.resolved + # Android related **/android/**/gradle-wrapper.jar **/android/.gradle diff --git a/packages/example/ios/Flutter/Debug.xcconfig b/packages/example/ios/Flutter/Debug.xcconfig index 30fbdc86..5e45b882 100644 --- a/packages/example/ios/Flutter/Debug.xcconfig +++ b/packages/example/ios/Flutter/Debug.xcconfig @@ -1,3 +1,2 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" #include? "Local.xcconfig" diff --git a/packages/example/ios/Flutter/Profile.xcconfig b/packages/example/ios/Flutter/Profile.xcconfig index 40cd22dd..5e45b882 100644 --- a/packages/example/ios/Flutter/Profile.xcconfig +++ b/packages/example/ios/Flutter/Profile.xcconfig @@ -1,3 +1,2 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig" #include "Generated.xcconfig" #include? "Local.xcconfig" diff --git a/packages/example/ios/Flutter/Release.xcconfig b/packages/example/ios/Flutter/Release.xcconfig index b9c6fb7f..5e45b882 100644 --- a/packages/example/ios/Flutter/Release.xcconfig +++ b/packages/example/ios/Flutter/Release.xcconfig @@ -1,3 +1,2 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" #include? "Local.xcconfig" diff --git a/packages/example/ios/Podfile b/packages/example/ios/Podfile deleted file mode 100644 index 2f65a326..00000000 --- a/packages/example/ios/Podfile +++ /dev/null @@ -1,62 +0,0 @@ -platform :ios, '15.5' # or newer version - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) -require File.expand_path('../../google_mlkit_commons/ios/scripts/apple_silicon_simulator', __dir__) - -flutter_ios_podfile_setup - -target 'Runner' do - # Add language package you need to use - pod 'GoogleMLKit/TextRecognitionChinese', '~> 9.0.0' - pod 'GoogleMLKit/TextRecognitionDevanagari', '~> 9.0.0' - pod 'GoogleMLKit/TextRecognitionJapanese', '~> 9.0.0' - pod 'GoogleMLKit/TextRecognitionKorean', '~> 9.0.0' - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -# add this line: -$iOSVersion = '15.5' # or newer version - -post_install do |installer| - # add these lines: - installer.pods_project.build_configurations.each do |config| - config.build_settings["EXCLUDED_ARCHS[sdk=*]"] = "armv7" - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion - end - - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - - # add these lines: - target.build_configurations.each do |config| - if Gem::Version.new($iOSVersion) > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET']) - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion - end - end - end - - mlkit_apple_silicon_simulator_patch(installer) -end diff --git a/packages/example/ios/Podfile.lock b/packages/example/ios/Podfile.lock deleted file mode 100644 index 212cde70..00000000 --- a/packages/example/ios/Podfile.lock +++ /dev/null @@ -1,463 +0,0 @@ -PODS: - - Flutter (1.0.0) - - google_mlkit_barcode_scanning (0.14.2): - - Flutter - - google_mlkit_commons - - GoogleMLKit/BarcodeScanning (~> 9.0.0) - - google_mlkit_commons (0.11.1): - - Flutter - - MLKitVision (~> 10.0.0) - - google_mlkit_digital_ink_recognition (0.14.2): - - Flutter - - google_mlkit_commons - - GoogleMLKit/DigitalInkRecognition (~> 9.0.0) - - google_mlkit_document_scanner (0.4.1): - - Flutter - - google_mlkit_entity_extraction (0.15.3): - - Flutter - - google_mlkit_commons - - GoogleMLKit/EntityExtraction (~> 9.0.0) - - google_mlkit_face_detection (0.13.2): - - Flutter - - google_mlkit_commons - - GoogleMLKit/FaceDetection (~> 9.0.0) - - google_mlkit_face_mesh_detection (0.4.2): - - Flutter - - google_mlkit_commons - - google_mlkit_genai_image_description (0.1.0): - - Flutter - - google_mlkit_genai_prompt (0.1.0): - - Flutter - - google_mlkit_genai_proofreading (0.1.0): - - Flutter - - google_mlkit_genai_rewriting (0.1.0): - - Flutter - - google_mlkit_genai_speech_recognition (0.1.0): - - Flutter - - google_mlkit_genai_summarization (0.1.0): - - Flutter - - google_mlkit_image_labeling (0.14.2): - - Flutter - - google_mlkit_commons - - GoogleMLKit/ImageLabeling (~> 9.0.0) - - GoogleMLKit/ImageLabelingCustom (~> 9.0.0) - - google_mlkit_language_id (0.13.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/LanguageID (~> 9.0.0) - - google_mlkit_object_detection (0.15.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/ObjectDetection (~> 9.0.0) - - GoogleMLKit/ObjectDetectionCustom (~> 9.0.0) - - google_mlkit_pose_detection (0.14.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/PoseDetection (~> 9.0.0) - - GoogleMLKit/PoseDetectionAccurate (~> 9.0.0) - - google_mlkit_selfie_segmentation (0.10.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/SegmentationSelfie (~> 9.0.0) - - google_mlkit_smart_reply (0.13.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/SmartReply (~> 9.0.0) - - google_mlkit_subject_segmentation (0.0.3): - - Flutter - - google_mlkit_text_recognition (0.15.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/TextRecognition (~> 9.0.0) - - google_mlkit_translation (0.13.1): - - Flutter - - google_mlkit_commons - - GoogleMLKit/Translate (~> 9.0.0) - - GoogleDataTransport (10.1.0): - - nanopb (~> 3.30910.0) - - PromisesObjC (~> 2.4) - - GoogleMLKit/BarcodeScanning (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitBarcodeScanning (~> 8.0.0) - - GoogleMLKit/DigitalInkRecognition (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitDigitalInkRecognition (~> 8.0.0) - - GoogleMLKit/EntityExtraction (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitEntityExtraction (~> 1.0.0-beta15) - - GoogleMLKit/FaceDetection (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitFaceDetection (~> 8.0.0) - - GoogleMLKit/ImageLabeling (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitImageLabeling (~> 8.0.0) - - GoogleMLKit/ImageLabelingCustom (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitImageLabelingCustom (~> 8.0.0) - - GoogleMLKit/LanguageID (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitLanguageID (~> 9.0.0) - - GoogleMLKit/MLKitCore (9.0.0): - - MLKitCommon (~> 14.0.0) - - GoogleMLKit/ObjectDetection (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitObjectDetection (~> 8.0.0) - - GoogleMLKit/ObjectDetectionCustom (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitObjectDetectionCustom (~> 8.0.0) - - GoogleMLKit/PoseDetection (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitPoseDetection (~> 1.0.0-beta16) - - GoogleMLKit/PoseDetectionAccurate (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitPoseDetectionAccurate (~> 1.0.0-beta16) - - GoogleMLKit/SegmentationSelfie (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitSegmentationSelfie (~> 1.0.0-beta14) - - GoogleMLKit/SmartReply (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitSmartReply (~> 8.0.0) - - GoogleMLKit/TextRecognition (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitTextRecognition (~> 7.0.0) - - GoogleMLKit/TextRecognitionChinese (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitTextRecognitionChinese (~> 6.0.0) - - GoogleMLKit/TextRecognitionDevanagari (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitTextRecognitionDevanagari (~> 6.0.0) - - GoogleMLKit/TextRecognitionJapanese (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitTextRecognitionJapanese (~> 6.0.0) - - GoogleMLKit/TextRecognitionKorean (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitTextRecognitionKorean (~> 6.0.0) - - GoogleMLKit/Translate (9.0.0): - - GoogleMLKit/MLKitCore - - MLKitTranslate (~> 8.0.0) - - GoogleToolboxForMac/Defines (4.2.1) - - GoogleToolboxForMac/Logger (4.2.1): - - GoogleToolboxForMac/Defines (= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (4.2.1)": - - GoogleToolboxForMac/Defines (= 4.2.1) - - GoogleToolboxForMac/StringEncoding (4.2.1): - - GoogleToolboxForMac/Defines (= 4.2.1) - - GoogleUtilities/Environment (8.1.0): - - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.1.0): - - GoogleUtilities/Environment - - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.1.0) - - GoogleUtilities/UserDefaults (8.1.0): - - GoogleUtilities/Logger - - GoogleUtilities/Privacy - - GTMSessionFetcher/Core (3.5.0) - - MLImage (1.0.0-beta8) - - MLKitBarcodeScanning (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitVision (~> 10.0) - - MLKitCommon (14.0.0): - - GoogleDataTransport (~> 10.0) - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GoogleUtilities/Logger (~> 8.0) - - GoogleUtilities/UserDefaults (~> 8.0) - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLKitDigitalInkRecognition (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitMDD (~> 10.0) - - SSZipArchive (< 3.0, >= 2.5.5) - - MLKitEntityExtraction (1.0.0-beta15): - - MLKitNaturalLanguage (~> 10.0) - - MLKitFaceDetection (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitVision (~> 10.0) - - MLKitImageLabeling (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitImageLabelingCommon (~> 10.0) - - MLKitVision (~> 10.0) - - MLKitVisionKit (~> 11.0) - - MLKitImageLabelingCommon (10.0.0): - - MLKitCommon (~> 14.0) - - MLKitVision (~> 10.0) - - MLKitImageLabelingCustom (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitImageLabelingCommon (~> 10.0) - - MLKitVision (~> 10.0) - - MLKitVisionKit (~> 11.0) - - MLKitLanguageID (9.0.0): - - MLKitNaturalLanguage (~> 10.0) - - MLKitMDD (10.0.0): - - MLKitCommon (~> 14.0) - - MLKitNaturalLanguage (10.0.0): - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GoogleToolboxForMac/StringEncoding (< 5.0, >= 4.2.1) - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLKitCommon (~> 14.0) - - MLKitObjectDetection (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitObjectDetectionCommon (~> 10.0) - - MLKitVision (~> 10.0) - - MLKitVisionKit (~> 11.0) - - MLKitObjectDetectionCommon (10.0.0): - - MLKitCommon (~> 14.0) - - MLKitVision (~> 10.0) - - MLKitObjectDetectionCustom (8.0.0): - - MLKitCommon (~> 14.0) - - MLKitObjectDetectionCommon (~> 10.0) - - MLKitVision (~> 10.0) - - MLKitVisionKit (~> 11.0) - - MLKitPoseDetection (1.0.0-beta16): - - MLKitCommon (~> 14.0) - - MLKitPoseDetectionCommon (= 1.0.0-beta16) - - MLKitXenoCommon (= 1.0.0-beta16) - - MLKitPoseDetectionAccurate (1.0.0-beta16): - - MLKitCommon (~> 14.0) - - MLKitPoseDetectionCommon (= 1.0.0-beta16) - - MLKitXenoCommon (= 1.0.0-beta16) - - MLKitPoseDetectionCommon (1.0.0-beta16): - - MLKitCommon (~> 14.0) - - MLKitXenoCommon (= 1.0.0-beta16) - - MLKitSegmentationCommon (1.0.0-beta14): - - MLKitCommon (~> 14.0) - - MLKitXenoCommon (= 1.0.0-beta16) - - MLKitSegmentationSelfie (1.0.0-beta14): - - MLKitSegmentationCommon (= 1.0.0-beta14) - - MLKitSmartReply (8.0.0): - - MLKitLanguageID (~> 9.0) - - MLKitNaturalLanguage (~> 10.0) - - MLKitTextRecognition (7.0.0): - - MLKitCommon (~> 14.0) - - MLKitTextRecognitionCommon (= 6.0.0) - - MLKitVision (~> 10.0) - - MLKitTextRecognitionChinese (6.0.0): - - MLKitCommon (~> 14.0) - - MLKitTextRecognitionCommon (= 6.0.0) - - MLKitVision (~> 10.0) - - MLKitTextRecognitionCommon (6.0.0): - - MLKitCommon (~> 14.0) - - MLKitVision (~> 10.0) - - MLKitTextRecognitionDevanagari (6.0.0): - - MLKitCommon (~> 14.0) - - MLKitTextRecognitionCommon (= 6.0.0) - - MLKitVision (~> 10.0) - - MLKitTextRecognitionJapanese (6.0.0): - - MLKitCommon (~> 14.0) - - MLKitTextRecognitionCommon (= 6.0.0) - - MLKitVision (~> 10.0) - - MLKitTextRecognitionKorean (6.0.0): - - MLKitCommon (~> 14.0) - - MLKitTextRecognitionCommon (= 6.0.0) - - MLKitVision (~> 10.0) - - MLKitTranslate (8.0.0): - - MLKitNaturalLanguage (~> 10.0) - - SSZipArchive (< 3.0, >= 2.5.5) - - MLKitVision (10.0.0): - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLImage (= 1.0.0-beta8) - - MLKitCommon (~> 14.0) - - MLKitVisionKit (11.0.0): - - MLKitCommon (~> 14.0) - - MLKitImageLabelingCommon (~> 10.0) - - MLKitObjectDetectionCommon (~> 10.0) - - MLKitVision (~> 10.0) - - MLKitXenoCommon (1.0.0-beta16): - - MLKitCommon (~> 14.0) - - MLKitVision (~> 10.0) - - nanopb (3.30910.0): - - nanopb/decode (= 3.30910.0) - - nanopb/encode (= 3.30910.0) - - nanopb/decode (3.30910.0) - - nanopb/encode (3.30910.0) - - PromisesObjC (2.4.0) - - SSZipArchive (2.6.0) - -DEPENDENCIES: - - Flutter (from `Flutter`) - - google_mlkit_barcode_scanning (from `.symlinks/plugins/google_mlkit_barcode_scanning/ios`) - - google_mlkit_commons (from `.symlinks/plugins/google_mlkit_commons/ios`) - - google_mlkit_digital_ink_recognition (from `.symlinks/plugins/google_mlkit_digital_ink_recognition/ios`) - - google_mlkit_document_scanner (from `.symlinks/plugins/google_mlkit_document_scanner/ios`) - - google_mlkit_entity_extraction (from `.symlinks/plugins/google_mlkit_entity_extraction/ios`) - - google_mlkit_face_detection (from `.symlinks/plugins/google_mlkit_face_detection/ios`) - - google_mlkit_face_mesh_detection (from `.symlinks/plugins/google_mlkit_face_mesh_detection/ios`) - - google_mlkit_genai_image_description (from `.symlinks/plugins/google_mlkit_genai_image_description/ios`) - - google_mlkit_genai_prompt (from `.symlinks/plugins/google_mlkit_genai_prompt/ios`) - - google_mlkit_genai_proofreading (from `.symlinks/plugins/google_mlkit_genai_proofreading/ios`) - - google_mlkit_genai_rewriting (from `.symlinks/plugins/google_mlkit_genai_rewriting/ios`) - - google_mlkit_genai_speech_recognition (from `.symlinks/plugins/google_mlkit_genai_speech_recognition/ios`) - - google_mlkit_genai_summarization (from `.symlinks/plugins/google_mlkit_genai_summarization/ios`) - - google_mlkit_image_labeling (from `.symlinks/plugins/google_mlkit_image_labeling/ios`) - - google_mlkit_language_id (from `.symlinks/plugins/google_mlkit_language_id/ios`) - - google_mlkit_object_detection (from `.symlinks/plugins/google_mlkit_object_detection/ios`) - - google_mlkit_pose_detection (from `.symlinks/plugins/google_mlkit_pose_detection/ios`) - - google_mlkit_selfie_segmentation (from `.symlinks/plugins/google_mlkit_selfie_segmentation/ios`) - - google_mlkit_smart_reply (from `.symlinks/plugins/google_mlkit_smart_reply/ios`) - - google_mlkit_subject_segmentation (from `.symlinks/plugins/google_mlkit_subject_segmentation/ios`) - - google_mlkit_text_recognition (from `.symlinks/plugins/google_mlkit_text_recognition/ios`) - - google_mlkit_translation (from `.symlinks/plugins/google_mlkit_translation/ios`) - - GoogleMLKit/TextRecognitionChinese (~> 9.0.0) - - GoogleMLKit/TextRecognitionDevanagari (~> 9.0.0) - - GoogleMLKit/TextRecognitionJapanese (~> 9.0.0) - - GoogleMLKit/TextRecognitionKorean (~> 9.0.0) - -SPEC REPOS: - trunk: - - GoogleDataTransport - - GoogleMLKit - - GoogleToolboxForMac - - GoogleUtilities - - GTMSessionFetcher - - MLImage - - MLKitBarcodeScanning - - MLKitCommon - - MLKitDigitalInkRecognition - - MLKitEntityExtraction - - MLKitFaceDetection - - MLKitImageLabeling - - MLKitImageLabelingCommon - - MLKitImageLabelingCustom - - MLKitLanguageID - - MLKitMDD - - MLKitNaturalLanguage - - MLKitObjectDetection - - MLKitObjectDetectionCommon - - MLKitObjectDetectionCustom - - MLKitPoseDetection - - MLKitPoseDetectionAccurate - - MLKitPoseDetectionCommon - - MLKitSegmentationCommon - - MLKitSegmentationSelfie - - MLKitSmartReply - - MLKitTextRecognition - - MLKitTextRecognitionChinese - - MLKitTextRecognitionCommon - - MLKitTextRecognitionDevanagari - - MLKitTextRecognitionJapanese - - MLKitTextRecognitionKorean - - MLKitTranslate - - MLKitVision - - MLKitVisionKit - - MLKitXenoCommon - - nanopb - - PromisesObjC - - SSZipArchive - -EXTERNAL SOURCES: - Flutter: - :path: Flutter - google_mlkit_barcode_scanning: - :path: ".symlinks/plugins/google_mlkit_barcode_scanning/ios" - google_mlkit_commons: - :path: ".symlinks/plugins/google_mlkit_commons/ios" - google_mlkit_digital_ink_recognition: - :path: ".symlinks/plugins/google_mlkit_digital_ink_recognition/ios" - google_mlkit_document_scanner: - :path: ".symlinks/plugins/google_mlkit_document_scanner/ios" - google_mlkit_entity_extraction: - :path: ".symlinks/plugins/google_mlkit_entity_extraction/ios" - google_mlkit_face_detection: - :path: ".symlinks/plugins/google_mlkit_face_detection/ios" - google_mlkit_face_mesh_detection: - :path: ".symlinks/plugins/google_mlkit_face_mesh_detection/ios" - google_mlkit_genai_image_description: - :path: ".symlinks/plugins/google_mlkit_genai_image_description/ios" - google_mlkit_genai_prompt: - :path: ".symlinks/plugins/google_mlkit_genai_prompt/ios" - google_mlkit_genai_proofreading: - :path: ".symlinks/plugins/google_mlkit_genai_proofreading/ios" - google_mlkit_genai_rewriting: - :path: ".symlinks/plugins/google_mlkit_genai_rewriting/ios" - google_mlkit_genai_speech_recognition: - :path: ".symlinks/plugins/google_mlkit_genai_speech_recognition/ios" - google_mlkit_genai_summarization: - :path: ".symlinks/plugins/google_mlkit_genai_summarization/ios" - google_mlkit_image_labeling: - :path: ".symlinks/plugins/google_mlkit_image_labeling/ios" - google_mlkit_language_id: - :path: ".symlinks/plugins/google_mlkit_language_id/ios" - google_mlkit_object_detection: - :path: ".symlinks/plugins/google_mlkit_object_detection/ios" - google_mlkit_pose_detection: - :path: ".symlinks/plugins/google_mlkit_pose_detection/ios" - google_mlkit_selfie_segmentation: - :path: ".symlinks/plugins/google_mlkit_selfie_segmentation/ios" - google_mlkit_smart_reply: - :path: ".symlinks/plugins/google_mlkit_smart_reply/ios" - google_mlkit_subject_segmentation: - :path: ".symlinks/plugins/google_mlkit_subject_segmentation/ios" - google_mlkit_text_recognition: - :path: ".symlinks/plugins/google_mlkit_text_recognition/ios" - google_mlkit_translation: - :path: ".symlinks/plugins/google_mlkit_translation/ios" - -SPEC CHECKSUMS: - Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 - google_mlkit_barcode_scanning: 37a95c619ade2966885d549d337c7ce25cdcd6e1 - google_mlkit_commons: fdbddbd42f5f4680ce01932017e31d1e0f02b4ef - google_mlkit_digital_ink_recognition: 4faaf4dc06be7427ca2e5a2b605e8c4e6f8919b5 - google_mlkit_document_scanner: 4286ef97b690c747db9db15337207a631e3efa24 - google_mlkit_entity_extraction: 2500f93887c590c0c68341d9b64523c992346281 - google_mlkit_face_detection: 4cf4f7bda5e0cfdb4e52c11603aa025a81138a47 - google_mlkit_face_mesh_detection: 103cfafc7ee649085abc40b07cedb1c9e165e9cb - google_mlkit_genai_image_description: 0a468e8016204f093f9f352a92823573f1b49728 - google_mlkit_genai_prompt: d527fb9e9b6885c373075a058c96b00c552f14c0 - google_mlkit_genai_proofreading: d4f6e35918c40f08069b4c2c89ce4f2fb9f1b4d9 - google_mlkit_genai_rewriting: b9821623a69e2cb9e54eedf68200a29dd89b3e92 - google_mlkit_genai_speech_recognition: 1064074540af10fdc14cf6810a004c38f0a46434 - google_mlkit_genai_summarization: 40a3ba7e03ab369f1ce7702e0970d6e7a35baa50 - google_mlkit_image_labeling: 7d5de5ee0fbbc23d95d33f9880e2c761beca6f23 - google_mlkit_language_id: 892ac40627c56faece8753d0ca5f1402854bf030 - google_mlkit_object_detection: 960463a8dff3a818a41d704632f9017a3d80a5a6 - google_mlkit_pose_detection: c378a1e88fc7ed97b64219f72edc2880ac40ca23 - google_mlkit_selfie_segmentation: 38f73022f0e63f253a050298f6d89c2978548991 - google_mlkit_smart_reply: ddac7d36acf06981e4e1fb009cfd8e10197af3cc - google_mlkit_subject_segmentation: b6d3bfeee5deb973d2832a68022c28cb8c36668f - google_mlkit_text_recognition: 00511b4b7987ad04770382e343ad73d40250a049 - google_mlkit_translation: f163102828e75ce10a4d5b31852599755a516809 - GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleMLKit: b1eee21a41c57704fe72483b15c85cb2c0cd7444 - GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 - GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - MLImage: 0de5c6c2bf9e93b80ef752e2797f0836f03b58c0 - MLKitBarcodeScanning: 39de223e7b1b8a8fbf10816a536dd292d8a39343 - MLKitCommon: 47d47b50a031d00db62f1b0efe5a1d8b09a3b2e6 - MLKitDigitalInkRecognition: d352ba532b816e7a930b8fa2e180288352c4736c - MLKitEntityExtraction: 9f529552862823d493d999fe7920fb195cfba8e3 - MLKitFaceDetection: 32549f1e70e6e7731261bf9cea2b74095e2531cb - MLKitImageLabeling: 739591b1b69ebab735e97fa1b4ce37cea8361bb7 - MLKitImageLabelingCommon: 616b0c69b3e302720cfb65903da50dc2e697b915 - MLKitImageLabelingCustom: 43cf5d49fb762adb93e3108cb55c81a8ec005d57 - MLKitLanguageID: 7b7d115881e0e036627052e32d7771b9d7f67ee6 - MLKitMDD: 264ea38cb1eafc18ee1f37eb69e7f2596962df6c - MLKitNaturalLanguage: 498154f2461f97abb00eb161eb773670ffc46250 - MLKitObjectDetection: c6346f1e6fd945d9a1a993104184e886bda6e0f6 - MLKitObjectDetectionCommon: 0639341c7cbb44327dac9a23c473d2484d3169dd - MLKitObjectDetectionCustom: a210309c7d2a0c08b0ce7b9ef3e8d36027c96978 - MLKitPoseDetection: 037fc62d6ccdd5a63253d1f40f90a8120b0d9c61 - MLKitPoseDetectionAccurate: 4b25c5447244db7e1d97963c4d6e4ebe612c2d88 - MLKitPoseDetectionCommon: 4709eb53b8638f5bf87c1a0255ad8aa4db9ad460 - MLKitSegmentationCommon: f634fb3c20c1d8bace22a2c026bdaa0ca17613d9 - MLKitSegmentationSelfie: 62371f35c33d49c3e7474443677b6a0c7ac9c98b - MLKitSmartReply: 52977167d7d824207a784fa42ace19d6c6e698e8 - MLKitTextRecognition: c0ad24510481dfc8893ad15dfcb8a5f05ed9e826 - MLKitTextRecognitionChinese: b69210723ed8cc9bc7b2e7847001880b0dcbb7f8 - MLKitTextRecognitionCommon: 234ceb1cfdfb5fceb4fd664943046609a2961cc2 - MLKitTextRecognitionDevanagari: 326281ad520172466dd6dd6a784fa03fb8ab9e37 - MLKitTextRecognitionJapanese: 965f89fd8ad3a02eb466106522ed08ea19f30f61 - MLKitTextRecognitionKorean: 22c8d6a73f519fa9732873993a30b067f8deccb1 - MLKitTranslate: fcb283a2cbaaa595e1cf1d3fedffcea2e97a1168 - MLKitVision: 39a5a812db83c4a0794445088e567f3631c11961 - MLKitVisionKit: 316cd349468797ef4e7fd785bf658ca838984de3 - MLKitXenoCommon: 1a4268c1222a6043047af5bb9435028206c63287 - nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - SSZipArchive: 8a6ee5677c8e304bebc109e39cf0da91ccef22ea - -PODFILE CHECKSUM: df61d3916884bb4fa8c7ec2fdffdf0dd0d3cec36 - -COCOAPODS: 1.16.2 diff --git a/packages/example/ios/Runner.xcodeproj/project.pbxproj b/packages/example/ios/Runner.xcodeproj/project.pbxproj index 1e5adb77..4b5926b9 100644 --- a/packages/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 19BEEF351D804C298C54516F /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 185FF648C8C3525E10052A2C /* libPods-Runner.a */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; @@ -31,18 +30,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 07F1C45CAB683E2489041B44 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 185FF648C8C3525E10052A2C /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFA3C8F1D35360C0083082F /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Profile.xcconfig; path = Flutter/Profile.xcconfig; sourceTree = ""; }; - 8A014A7A90898F823F3D4B3D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 923A943AD1A8668FF24F5D59 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -58,7 +53,6 @@ buildActionMask = 2147483647; files = ( 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, - 19BEEF351D804C298C54516F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -68,21 +62,10 @@ 5AF1CF50FEB2DDD24045D4DC /* Pods */ = { isa = PBXGroup; children = ( - 8A014A7A90898F823F3D4B3D /* Pods-Runner.debug.xcconfig */, - 923A943AD1A8668FF24F5D59 /* Pods-Runner.release.xcconfig */, - 07F1C45CAB683E2489041B44 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; - 8B326F84B2536FFF50DFEFD3 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 185FF648C8C3525E10052A2C /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -103,7 +86,6 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 5AF1CF50FEB2DDD24045D4DC /* Pods */, - 8B326F84B2536FFF50DFEFD3 /* Frameworks */, ); sourceTree = ""; }; @@ -137,7 +119,6 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 80DABC9846A6DDC8F0D21E5C /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, A7B8C9D0E1F2A3B4C5D6E7F8 /* SwiftLint */, 97C146EA1CF9000F007C117D /* Sources */, @@ -145,7 +126,6 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 0B0823EF0E30F27C01C5808D /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -211,23 +191,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0B0823EF0E30F27C01C5808D /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -244,28 +207,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 80DABC9846A6DDC8F0D21E5C /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -400,7 +341,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.googleMlKitExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -541,7 +482,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.googleMlKitExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -564,7 +505,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.googleMlKitExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/packages/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..20a3c5f1 --- /dev/null +++ b/packages/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,59 @@ +{ + "pins" : [ + { + "identity" : "google-mlkit-swiftpm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/d-date/google-mlkit-swiftpm", + "state" : { + "revision" : "7aefa43c5105f28b9a8f9a48f7a115524a101186", + "version" : "9.0.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 2 +} diff --git a/packages/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..20a3c5f1 --- /dev/null +++ b/packages/example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,59 @@ +{ + "pins" : [ + { + "identity" : "google-mlkit-swiftpm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/d-date/google-mlkit-swiftpm", + "state" : { + "revision" : "7aefa43c5105f28b9a8f9a48f7a115524a101186", + "version" : "9.0.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 2 +} diff --git a/packages/example/pubspec.lock b/packages/example/pubspec.lock index e1df4131..f300482b 100644 --- a/packages/example/pubspec.lock +++ b/packages/example/pubspec.lock @@ -454,10 +454,10 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.19" material_color_utilities: dependency: transitive description: @@ -470,10 +470,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -611,10 +611,10 @@ packages: dependency: transitive description: name: test_api - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.9" + version: "0.7.11" typed_data: dependency: transitive description: @@ -656,5 +656,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.9.0 <4.0.0" + dart: ">=3.10.0-0 <4.0.0" flutter: ">=3.35.0" diff --git a/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning.podspec b/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning.podspec index c11671e8..1c30162f 100644 --- a/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning.podspec +++ b/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_barcode_scanning/Sources/google_mlkit_barcode_scanning/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/BarcodeScanning', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning/Package.swift b/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning/Package.swift new file mode 100644 index 00000000..a7bdf30c --- /dev/null +++ b/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-barcode-scanning", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-barcode-scanning", + targets: ["google_mlkit_barcode_scanning"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_barcode_scanning", + dependencies: [ + .product(name: "MLKitBarcodeScanning", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_barcode_scanning" + ) + ] +) diff --git a/packages/google_mlkit_barcode_scanning/ios/Classes/GoogleMlKitBarcodeScanningPlugin.swift b/packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning/Sources/google_mlkit_barcode_scanning/GoogleMlKitBarcodeScanningPlugin.swift similarity index 100% rename from packages/google_mlkit_barcode_scanning/ios/Classes/GoogleMlKitBarcodeScanningPlugin.swift rename to packages/google_mlkit_barcode_scanning/ios/google_mlkit_barcode_scanning/Sources/google_mlkit_barcode_scanning/GoogleMlKitBarcodeScanningPlugin.swift diff --git a/packages/google_mlkit_commons/ios/google_mlkit_commons.podspec b/packages/google_mlkit_commons/ios/google_mlkit_commons.podspec index 16f46bd3..fd03678d 100644 --- a/packages/google_mlkit_commons/ios/google_mlkit_commons.podspec +++ b/packages/google_mlkit_commons/ios/google_mlkit_commons.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_commons/Sources/google_mlkit_commons/**/*.swift' s.dependency 'Flutter' s.dependency 'MLKitVision', '~> 10.0.0' s.platform = :ios, '15.5' diff --git a/packages/google_mlkit_commons/ios/google_mlkit_commons/Package.resolved b/packages/google_mlkit_commons/ios/google_mlkit_commons/Package.resolved new file mode 100644 index 00000000..20a3c5f1 --- /dev/null +++ b/packages/google_mlkit_commons/ios/google_mlkit_commons/Package.resolved @@ -0,0 +1,59 @@ +{ + "pins" : [ + { + "identity" : "google-mlkit-swiftpm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/d-date/google-mlkit-swiftpm", + "state" : { + "revision" : "7aefa43c5105f28b9a8f9a48f7a115524a101186", + "version" : "9.0.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + } + ], + "version" : 2 +} diff --git a/packages/google_mlkit_commons/ios/google_mlkit_commons/Package.swift b/packages/google_mlkit_commons/ios/google_mlkit_commons/Package.swift new file mode 100644 index 00000000..60d1442c --- /dev/null +++ b/packages/google_mlkit_commons/ios/google_mlkit_commons/Package.swift @@ -0,0 +1,38 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-commons", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-commons", + targets: ["google_mlkit_commons"]) + ], + dependencies: [ + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ) + ], + targets: [ + .target( + name: "google_mlkit_commons", + dependencies: [ + // google-mlkit-swiftpm does not expose standalone MLKitVision or MLKitCommon + // library products — they are only available as binary targets bundled inside + // composite products (e.g. MLKitBarcodeScanning, MLKitFaceDetection, etc.). + // All vision-family products include MLKitVision + MLKitCommon (via the Common + // target), so we must pick one even though Commons itself does not use the + // specific module. MLKitObjectDetection is used here as a generic choice; the + // unused xcframework has no material impact because any real app using Google + // ML Kit will depend on at least one vision plugin that already brings it in. + .product(name: "MLKitObjectDetection", package: "google-mlkit-swiftpm") + ], + path: "Sources/google_mlkit_commons" + ) + ] +) diff --git a/packages/google_mlkit_commons/ios/Classes/GenericModelManager.swift b/packages/google_mlkit_commons/ios/google_mlkit_commons/Sources/google_mlkit_commons/GenericModelManager.swift similarity index 100% rename from packages/google_mlkit_commons/ios/Classes/GenericModelManager.swift rename to packages/google_mlkit_commons/ios/google_mlkit_commons/Sources/google_mlkit_commons/GenericModelManager.swift diff --git a/packages/google_mlkit_commons/ios/Classes/GoogleMlKitCommonsPlugin.swift b/packages/google_mlkit_commons/ios/google_mlkit_commons/Sources/google_mlkit_commons/GoogleMlKitCommonsPlugin.swift similarity index 100% rename from packages/google_mlkit_commons/ios/Classes/GoogleMlKitCommonsPlugin.swift rename to packages/google_mlkit_commons/ios/google_mlkit_commons/Sources/google_mlkit_commons/GoogleMlKitCommonsPlugin.swift diff --git a/packages/google_mlkit_commons/ios/Classes/MLKVisionImage+FlutterPlugin.swift b/packages/google_mlkit_commons/ios/google_mlkit_commons/Sources/google_mlkit_commons/MLKVisionImage+FlutterPlugin.swift similarity index 100% rename from packages/google_mlkit_commons/ios/Classes/MLKVisionImage+FlutterPlugin.swift rename to packages/google_mlkit_commons/ios/google_mlkit_commons/Sources/google_mlkit_commons/MLKVisionImage+FlutterPlugin.swift diff --git a/packages/google_mlkit_digital_ink_recognition/ios/Classes/GoogleMlKitDigitalInkRecognitionPlugin.swift b/packages/google_mlkit_digital_ink_recognition/ios/Classes/GoogleMlKitDigitalInkRecognitionPlugin.swift deleted file mode 100644 index b12328ed..00000000 --- a/packages/google_mlkit_digital_ink_recognition/ios/Classes/GoogleMlKitDigitalInkRecognitionPlugin.swift +++ /dev/null @@ -1,148 +0,0 @@ -import Flutter -import MLKitCommon -import MLKitDigitalInkRecognition -import google_mlkit_commons - -@objc -public class GoogleMlKitDigitalInkRecognitionPlugin: NSObject, FlutterPlugin { - private var instances: [String: DigitalInkRecognizer] = [:] - private var genericModelManager: GenericModelManager? - - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel( - name: "google_mlkit_digital_ink_recognizer", - binaryMessenger: registrar.messenger() - ) - let instance = GoogleMlKitDigitalInkRecognitionPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "vision#startDigitalInkRecognizer": - handleDetection(call: call, result: result) - case "vision#manageInkModels": - manageModel(call: call, result: result) - case "vision#closeDigitalInkRecognizer": - if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { - instances.removeValue(forKey: uid) - } - result(nil) - default: - result(FlutterMethodNotImplemented) - } - } - - private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let args = call.arguments as? [String: Any], - let modelTag = args["model"] as? String, - let uid = args["id"] as? String else { - result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) - return - } - - guard let identifier = DigitalInkRecognitionModelIdentifier(forLanguageTag: modelTag) else { - result(FlutterError(code: "invalid_model", message: "Invalid language tag: \(modelTag)", details: nil)) - return - } - let model = DigitalInkRecognitionModel(modelIdentifier: identifier) - guard ModelManager.modelManager().isModelDownloaded(model) else { - result(FlutterError( - code: "Error Model has not been downloaded yet", - message: "Model has not been downloaded yet", - details: "Model has not been downloaded yet" - )) - return - } - - let recognizer: DigitalInkRecognizer - if let existing = instances[uid] { - recognizer = existing - } else { - let options = DigitalInkRecognizerOptions(model: model) - recognizer = DigitalInkRecognizer.digitalInkRecognizer(options: options) - instances[uid] = recognizer - } - - guard let strokeList = args["ink"] as? [String: Any], - let strokesData = strokeList["strokes"] as? [[String: Any]] else { - result(FlutterError(code: "invalid_args", message: "Missing ink data", details: nil)) - return - } - let strokes = strokesData.map { strokeMap -> Stroke in - guard let pointsList = strokeMap["points"] as? [[String: Any]] else { - return Stroke(points: []) - } - let points = pointsList.map { pointMap -> StrokePoint in - let coordX = (pointMap["x"] as? NSNumber)?.floatValue ?? 0 - let coordY = (pointMap["y"] as? NSNumber)?.floatValue ?? 0 - let timeMs = (pointMap["t"] as? NSNumber)?.intValue ?? 0 - return StrokePoint(x: coordX, y: coordY, t: timeMs) - } - return Stroke(points: points) - } - let ink = Ink(strokes: strokes) - - let contextMap = args["context"] as? [String: Any] - var context: DigitalInkRecognitionContext? - if let ctx = contextMap { - let preContext = ctx["preContext"] as? String ?? "" - var writingArea: WritingArea? - if let writingAreaMap = ctx["writingArea"] as? [String: Any], - let width = writingAreaMap["width"] as? NSNumber, - let height = writingAreaMap["height"] as? NSNumber { - writingArea = WritingArea(width: width.floatValue, height: height.floatValue) - } - context = DigitalInkRecognitionContext(preContext: preContext, writingArea: writingArea) - } - - func process(recognitionResult: DigitalInkRecognitionResult?, error: Error?) { - if let error = error { - let nsError = error as NSError - result(FlutterError( - code: "Error \(nsError.code)", - message: nsError.domain, - details: nsError.localizedDescription - )) - return - } - guard let recognitionResult = recognitionResult else { - result(nil) - return - } - let candidates = recognitionResult.candidates.map { candidate in - [ - "text": candidate.text, - "score": candidate.score?.doubleValue ?? 0 - ] as [String: Any] - } - result(candidates) - } - - if let ctx = context { - recognizer.recognize(ink: ink, context: ctx) { recognitionResult, error in - process(recognitionResult: recognitionResult, error: error) - } - } else { - recognizer.recognize(ink: ink) { recognitionResult, error in - process(recognitionResult: recognitionResult, error: error) - } - } - } - - private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let args = call.arguments as? [String: Any], - let modelTag = args["model"] as? String else { - result(FlutterError(code: "invalid_args", message: "Missing model argument", details: nil)) - return - } - guard let identifier = DigitalInkRecognitionModelIdentifier(forLanguageTag: modelTag) else { - result(FlutterError(code: "invalid_model", message: "Invalid language tag: \(modelTag)", details: nil)) - return - } - let model = DigitalInkRecognitionModel(modelIdentifier: identifier) - let manager = GenericModelManager() - genericModelManager = manager - manager.manage(model: model, call: call, result: result) - } -} diff --git a/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition.podspec b/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition.podspec index 49807eed..52352e6d 100644 --- a/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition.podspec +++ b/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_digital_ink_recognition/Sources/google_mlkit_digital_ink_recognition/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/DigitalInkRecognition', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition/Package.swift b/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition/Package.swift new file mode 100644 index 00000000..0e7334f5 --- /dev/null +++ b/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-digital-ink-recognition", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-digital-ink-recognition", + targets: ["google_mlkit_digital_ink_recognition"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons") + ], + targets: [ + .target( + name: "google_mlkit_digital_ink_recognition", + dependencies: [ + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons") + ], + path: "Sources/google_mlkit_digital_ink_recognition" + ) + ] +) diff --git a/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition/Sources/google_mlkit_digital_ink_recognition/GoogleMlKitDigitalInkRecognitionPlugin.swift b/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition/Sources/google_mlkit_digital_ink_recognition/GoogleMlKitDigitalInkRecognitionPlugin.swift new file mode 100644 index 00000000..fd86f7d0 --- /dev/null +++ b/packages/google_mlkit_digital_ink_recognition/ios/google_mlkit_digital_ink_recognition/Sources/google_mlkit_digital_ink_recognition/GoogleMlKitDigitalInkRecognitionPlugin.swift @@ -0,0 +1,191 @@ +import Flutter +import google_mlkit_commons + +#if canImport(MLKitDigitalInkRecognition) + import MLKitCommon + import MLKitDigitalInkRecognition +#endif + +@objc +public class GoogleMlKitDigitalInkRecognitionPlugin: NSObject, FlutterPlugin { + private var instances: [String: Any] = [:] + private var genericModelManager: GenericModelManager? + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_digital_ink_recognizer", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitDigitalInkRecognitionPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + #if canImport(MLKitDigitalInkRecognition) + switch call.method { + case "vision#startDigitalInkRecognizer": + handleDetection(call: call, result: result) + case "vision#manageInkModels": + manageModel(call: call, result: result) + case "vision#closeDigitalInkRecognizer": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + #else + let unimplemented = FlutterError( + code: "UNIMPLEMENTED", + message: + "MLKitDigitalInkRecognition is not available via Swift Package Manager on iOS", + details: nil + ) + switch call.method { + case "vision#closeDigitalInkRecognizer": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(unimplemented) + } + #endif + } + + #if canImport(MLKitDigitalInkRecognition) + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let modelTag = args["model"] as? String, + let uid = args["id"] as? String + else { + result( + FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + + guard let identifier = DigitalInkRecognitionModelIdentifier(forLanguageTag: modelTag) + else { + result( + FlutterError( + code: "invalid_model", message: "Invalid language tag: \(modelTag)", + details: nil)) + return + } + let model = DigitalInkRecognitionModel(modelIdentifier: identifier) + guard ModelManager.modelManager().isModelDownloaded(model) else { + result( + FlutterError( + code: "Error Model has not been downloaded yet", + message: "Model has not been downloaded yet", + details: "Model has not been downloaded yet" + )) + return + } + + let recognizer: DigitalInkRecognizer + if let existing = instances[uid] as? DigitalInkRecognizer { + recognizer = existing + } else { + let options = DigitalInkRecognizerOptions(model: model) + recognizer = DigitalInkRecognizer.digitalInkRecognizer(options: options) + instances[uid] = recognizer + } + + guard let strokeList = args["ink"] as? [String: Any], + let strokesData = strokeList["strokes"] as? [[String: Any]] + else { + result( + FlutterError(code: "invalid_args", message: "Missing ink data", details: nil)) + return + } + let strokes = strokesData.map { strokeMap -> Stroke in + guard let pointsList = strokeMap["points"] as? [[String: Any]] else { + return Stroke(points: []) + } + let points = pointsList.map { pointMap -> StrokePoint in + let coordX = (pointMap["x"] as? NSNumber)?.floatValue ?? 0 + let coordY = (pointMap["y"] as? NSNumber)?.floatValue ?? 0 + let timeMs = (pointMap["t"] as? NSNumber)?.intValue ?? 0 + return StrokePoint(x: coordX, y: coordY, t: timeMs) + } + return Stroke(points: points) + } + let ink = Ink(strokes: strokes) + + let contextMap = args["context"] as? [String: Any] + var context: DigitalInkRecognitionContext? + if let ctx = contextMap { + let preContext = ctx["preContext"] as? String ?? "" + var writingArea: WritingArea? + if let writingAreaMap = ctx["writingArea"] as? [String: Any], + let width = writingAreaMap["width"] as? NSNumber, + let height = writingAreaMap["height"] as? NSNumber + { + writingArea = WritingArea(width: width.floatValue, height: height.floatValue) + } + context = DigitalInkRecognitionContext( + preContext: preContext, writingArea: writingArea) + } + + func process(recognitionResult: DigitalInkRecognitionResult?, error: Error?) { + if let error = error { + let nsError = error as NSError + result( + FlutterError( + code: "Error \(nsError.code)", + message: nsError.domain, + details: nsError.localizedDescription + )) + return + } + guard let recognitionResult = recognitionResult else { + result(nil) + return + } + let candidates = recognitionResult.candidates.map { candidate in + [ + "text": candidate.text, + "score": candidate.score?.doubleValue ?? 0, + ] as [String: Any] + } + result(candidates) + } + + if let ctx = context { + recognizer.recognize(ink: ink, context: ctx) { recognitionResult, error in + process(recognitionResult: recognitionResult, error: error) + } + } else { + recognizer.recognize(ink: ink) { recognitionResult, error in + process(recognitionResult: recognitionResult, error: error) + } + } + } + + private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let modelTag = args["model"] as? String + else { + result( + FlutterError( + code: "invalid_args", message: "Missing model argument", details: nil)) + return + } + guard let identifier = DigitalInkRecognitionModelIdentifier(forLanguageTag: modelTag) + else { + result( + FlutterError( + code: "invalid_model", message: "Invalid language tag: \(modelTag)", + details: nil)) + return + } + let model = DigitalInkRecognitionModel(modelIdentifier: identifier) + if genericModelManager == nil { + genericModelManager = GenericModelManager() + } + genericModelManager?.manage(model: model, call: call, result: result) + } + #endif +} diff --git a/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner.podspec b/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner.podspec index dac076d0..caee2f15 100644 --- a/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner.podspec +++ b/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.author = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_document_scanner/Sources/google_mlkit_document_scanner/**/*.swift' s.dependency 'Flutter' # s.dependency 'GoogleMLKit/DocumentScanner', '~> 5.0.0' s.platform = :ios, '15.5' diff --git a/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner/Package.swift b/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner/Package.swift new file mode 100644 index 00000000..01c31e9f --- /dev/null +++ b/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-document-scanner", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-document-scanner", + targets: ["google_mlkit_document_scanner"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_document_scanner", + path: "Sources/google_mlkit_document_scanner" + ) + ] +) diff --git a/packages/google_mlkit_document_scanner/ios/Classes/GoogleMlKitDocumentScannerPlugin.swift b/packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner/Sources/google_mlkit_document_scanner/GoogleMlKitDocumentScannerPlugin.swift similarity index 100% rename from packages/google_mlkit_document_scanner/ios/Classes/GoogleMlKitDocumentScannerPlugin.swift rename to packages/google_mlkit_document_scanner/ios/google_mlkit_document_scanner/Sources/google_mlkit_document_scanner/GoogleMlKitDocumentScannerPlugin.swift diff --git a/packages/google_mlkit_entity_extraction/ios/Classes/GoogleMlKitEntityExtractionPlugin.swift b/packages/google_mlkit_entity_extraction/ios/Classes/GoogleMlKitEntityExtractionPlugin.swift deleted file mode 100644 index f4b82c0f..00000000 --- a/packages/google_mlkit_entity_extraction/ios/Classes/GoogleMlKitEntityExtractionPlugin.swift +++ /dev/null @@ -1,198 +0,0 @@ -import Flutter -import MLKitEntityExtraction -import google_mlkit_commons - -@objc -public class GoogleMlKitEntityExtractionPlugin: NSObject, FlutterPlugin { - private var instances: [String: EntityExtractor] = [:] - private var genericModelManager: GenericModelManager? - - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel( - name: "google_mlkit_entity_extractor", - binaryMessenger: registrar.messenger() - ) - let instance = GoogleMlKitEntityExtractionPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "nlp#startEntityExtractor": - handleDetection(call: call, result: result) - case "nlp#manageEntityExtractionModels": - manageModel(call: call, result: result) - case "nlp#closeEntityExtractor": - if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { - instances.removeValue(forKey: uid) - } - result(nil) - default: - result(FlutterMethodNotImplemented) - } - } - - private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let args = call.arguments as? [String: Any], - let text = args["text"] as? String, - let uid = args["id"] as? String else { - result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) - return - } - - let entityExtractor: EntityExtractor - if let existing = instances[uid] { - entityExtractor = existing - } else { - guard let language = args["language"] as? String else { - result(FlutterError(code: "invalid_args", message: "Missing language", details: nil)) - return - } - // EntityExtractionModelIdentifier(rawValue:) is non-failable; invalid tags may fail at runtime. - let modelIdentifier = EntityExtractionModelIdentifier(rawValue: language) - let options = EntityExtractorOptions(modelIdentifier: modelIdentifier) - entityExtractor = EntityExtractor.entityExtractor(options: options) - instances[uid] = entityExtractor - } - - let params = EntityExtractionParams() - let parametersRaw = args["parameters"] - let parameters: [String: Any] = (parametersRaw is NSNull) ? [:] : (parametersRaw as? [String: Any]) ?? [:] - if let timezone = valueAsString(parameters["timezone"]), !timezone.isEmpty { - params.referenceTimeZone = TimeZone(abbreviation: timezone) - } - if let time = valueAsNumber(parameters["time"]) { - params.referenceTime = Date(timeIntervalSince1970: time.doubleValue / 1000) - } - if let locale = valueAsString(parameters["locale"]), !locale.isEmpty { - params.preferredLocale = Locale(identifier: locale) - } - if let filtersValues = valueAsNumberArray(parameters["filters"]), !filtersValues.isEmpty { - let filters: Set = Set(filtersValues.compactMap { numberToEntityType($0.intValue) }) - params.typesFilter = filters - } - - entityExtractor.downloadModelIfNeeded { error in - if let error = error as NSError? { - result(FlutterError(code: "Error \(error.code)", message: error.domain, details: error.localizedDescription)) - return - } - entityExtractor.annotateText(text, params: params) { annotations, error in - if let error = error as NSError? { - result(FlutterError(code: "Error \(error.code)", message: error.domain, details: error.localizedDescription)) - return - } - guard let annotations = annotations else { - result(nil) - return - } - let allAnnotations = annotations.map { annotation -> [String: Any] in - let range = annotation.range - let substring = (text as NSString).substring(with: range) - let entities = annotation.entities.map { entity in self.entityToDictionary(text: text, entity: entity) } - return [ - "text": substring, - "start": range.location, - "end": range.location + range.length, - "entities": entities - ] - } - result(allAnnotations) - } - } - } - - private func numberToEntityType(_ value: Int) -> EntityType? { - switch value { - case 1: return .address - case 2: return .dateTime - case 3: return .email - case 4: return .flightNumber - case 5: return .IBAN - case 6: return .ISBN - case 7: return .paymentCard - case 8: return .phone - case 9: return .trackingNumber - case 10: return .URL - case 11: return .money - default: return nil - } - } - - private func entityToDictionary(text: String, entity: Entity) -> [String: Any] { - var entityData: [String: Any] = [ - "type": entityTypeToNumber(entity.entityType), - "raw": String(describing: entity) - ] - if entity.entityType == .dateTime, let dateTimeEntity = entity.dateTimeEntity { - entityData["dateTimeGranularity"] = dateTimeEntity.dateTimeGranularity.rawValue - entityData["timestamp"] = dateTimeEntity.dateTime.timeIntervalSince1970 * 1000 - } else if entity.entityType == .flightNumber, let flightEntity = entity.flightNumberEntity { - entityData["code"] = flightEntity.airlineCode - entityData["number"] = flightEntity.flightNumber - } else if entity.entityType == .IBAN, let ibanEntity = entity.ibanEntity { - entityData["iban"] = ibanEntity.iban - entityData["code"] = ibanEntity.countryCode - } else if entity.entityType == .ISBN, let isbnEntity = entity.isbnEntity { - entityData["isbn"] = isbnEntity.isbn - } else if entity.entityType == .paymentCard, let cardEntity = entity.paymentCardEntity { - entityData["network"] = cardEntity.paymentCardNetwork.rawValue - entityData["number"] = cardEntity.paymentCardNumber - } else if entity.entityType == .trackingNumber, let trackingEntity = entity.trackingNumberEntity { - entityData["carrier"] = trackingEntity.parcelCarrier.rawValue - entityData["number"] = trackingEntity.parcelTrackingNumber - } else if entity.entityType == .money, let moneyEntity = entity.moneyEntity { - entityData["fraction"] = moneyEntity.fractionalPart - entityData["integer"] = moneyEntity.integerPart - entityData["unnormalized"] = moneyEntity.unnormalizedCurrency - } - return entityData - } - - private func entityTypeToNumber(_ type: EntityType) -> Int { - switch type { - case .address: return 1 - case .dateTime: return 2 - case .email: return 3 - case .flightNumber: return 4 - case .IBAN: return 5 - case .ISBN: return 6 - case .paymentCard: return 7 - case .phone: return 8 - case .trackingNumber: return 9 - case .URL: return 10 - case .money: return 11 - default: return 0 - } - } - - /// Treats NSNull and missing values as nil for method-channel backward compatibility (Dart may send null). - private func valueAsString(_ value: Any?) -> String? { - guard value != nil, value is NSNull == false else { return nil } - return value as? String - } - - private func valueAsNumber(_ value: Any?) -> NSNumber? { - guard value != nil, value is NSNull == false else { return nil } - return value as? NSNumber - } - - private func valueAsNumberArray(_ value: Any?) -> [NSNumber]? { - guard value != nil, value is NSNull == false else { return nil } - return value as? [NSNumber] - } - - private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let args = call.arguments as? [String: Any], - let modelTag = args["model"] as? String else { - result(FlutterError(code: "invalid_args", message: "Missing model argument", details: nil)) - return - } - // EntityExtractionModelIdentifier(rawValue:) is non-failable; invalid tags may fail when the model is used. - let modelIdentifier = EntityExtractionModelIdentifier(rawValue: modelTag) - let model = EntityExtractorRemoteModel.entityExtractorRemoteModel(identifier: modelIdentifier) - let manager = GenericModelManager() - genericModelManager = manager - manager.manage(model: model, call: call, result: result) - } -} diff --git a/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction.podspec b/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction.podspec index b33a4ff5..5b1ee4da 100644 --- a/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction.podspec +++ b/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_entity_extraction/Sources/google_mlkit_entity_extraction/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/EntityExtraction', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction/Package.swift b/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction/Package.swift new file mode 100644 index 00000000..99aad36c --- /dev/null +++ b/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-entity-extraction", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-entity-extraction", + targets: ["google_mlkit_entity_extraction"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons") + ], + targets: [ + .target( + name: "google_mlkit_entity_extraction", + dependencies: [ + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons") + ], + path: "Sources/google_mlkit_entity_extraction" + ) + ] +) diff --git a/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction/Sources/google_mlkit_entity_extraction/GoogleMlKitEntityExtractionPlugin.swift b/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction/Sources/google_mlkit_entity_extraction/GoogleMlKitEntityExtractionPlugin.swift new file mode 100644 index 00000000..9c12c66f --- /dev/null +++ b/packages/google_mlkit_entity_extraction/ios/google_mlkit_entity_extraction/Sources/google_mlkit_entity_extraction/GoogleMlKitEntityExtractionPlugin.swift @@ -0,0 +1,241 @@ +import Flutter +import google_mlkit_commons + +#if canImport(MLKitEntityExtraction) + import MLKitEntityExtraction +#endif + +@objc +public class GoogleMlKitEntityExtractionPlugin: NSObject, FlutterPlugin { + private var instances: [String: Any] = [:] + private var genericModelManager: GenericModelManager? + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_entity_extractor", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitEntityExtractionPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + #if canImport(MLKitEntityExtraction) + switch call.method { + case "nlp#startEntityExtractor": + handleDetection(call: call, result: result) + case "nlp#manageEntityExtractionModels": + manageModel(call: call, result: result) + case "nlp#closeEntityExtractor": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + #else + let unimplemented = FlutterError( + code: "UNIMPLEMENTED", + message: "MLKitEntityExtraction is not available via Swift Package Manager on iOS", + details: nil + ) + switch call.method { + case "nlp#closeEntityExtractor": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(unimplemented) + } + #endif + } + + #if canImport(MLKitEntityExtraction) + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let text = args["text"] as? String, + let uid = args["id"] as? String + else { + result( + FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + + let entityExtractor: EntityExtractor + if let existing = instances[uid] as? EntityExtractor { + entityExtractor = existing + } else { + guard let language = args["language"] as? String else { + result( + FlutterError( + code: "invalid_args", message: "Missing language", details: nil)) + return + } + let modelIdentifier = EntityExtractionModelIdentifier(rawValue: language) + let options = EntityExtractorOptions(modelIdentifier: modelIdentifier) + entityExtractor = EntityExtractor.entityExtractor(options: options) + instances[uid] = entityExtractor + } + + let params = EntityExtractionParams() + let parametersRaw = args["parameters"] + let parameters: [String: Any] = + (parametersRaw is NSNull) ? [:] : (parametersRaw as? [String: Any]) ?? [:] + if let timezone = valueAsString(parameters["timezone"]), !timezone.isEmpty { + params.referenceTimeZone = TimeZone(abbreviation: timezone) + } + if let time = valueAsNumber(parameters["time"]) { + params.referenceTime = Date(timeIntervalSince1970: time.doubleValue / 1000) + } + if let locale = valueAsString(parameters["locale"]), !locale.isEmpty { + params.preferredLocale = Locale(identifier: locale) + } + if let filtersValues = valueAsNumberArray(parameters["filters"]), !filtersValues.isEmpty + { + let filters: Set = Set( + filtersValues.compactMap { numberToEntityType($0.intValue) }) + params.typesFilter = filters + } + + entityExtractor.downloadModelIfNeeded { error in + if let error = error as NSError? { + result( + FlutterError( + code: "Error \(error.code)", message: error.domain, + details: error.localizedDescription)) + return + } + entityExtractor.annotateText(text, params: params) { annotations, error in + if let error = error as NSError? { + result( + FlutterError( + code: "Error \(error.code)", message: error.domain, + details: error.localizedDescription)) + return + } + guard let annotations = annotations else { + result(nil) + return + } + let allAnnotations = annotations.map { annotation -> [String: Any] in + let range = annotation.range + let substring = (text as NSString).substring(with: range) + let entities = annotation.entities.map { entity in + self.entityToDictionary(text: text, entity: entity) + } + return [ + "text": substring, + "start": range.location, + "end": range.location + range.length, + "entities": entities, + ] + } + result(allAnnotations) + } + } + } + + private func numberToEntityType(_ value: Int) -> EntityType? { + switch value { + case 1: return .address + case 2: return .dateTime + case 3: return .email + case 4: return .flightNumber + case 5: return .IBAN + case 6: return .ISBN + case 7: return .paymentCard + case 8: return .phone + case 9: return .trackingNumber + case 10: return .URL + case 11: return .money + default: return nil + } + } + + private func entityToDictionary(text: String, entity: Entity) -> [String: Any] { + var entityData: [String: Any] = [ + "type": entityTypeToNumber(entity.entityType), + "raw": String(describing: entity), + ] + if entity.entityType == .dateTime, let dateTimeEntity = entity.dateTimeEntity { + entityData["dateTimeGranularity"] = dateTimeEntity.dateTimeGranularity.rawValue + entityData["timestamp"] = dateTimeEntity.dateTime.timeIntervalSince1970 * 1000 + } else if entity.entityType == .flightNumber, + let flightEntity = entity.flightNumberEntity + { + entityData["code"] = flightEntity.airlineCode + entityData["number"] = flightEntity.flightNumber + } else if entity.entityType == .IBAN, let ibanEntity = entity.ibanEntity { + entityData["iban"] = ibanEntity.iban + entityData["code"] = ibanEntity.countryCode + } else if entity.entityType == .ISBN, let isbnEntity = entity.isbnEntity { + entityData["isbn"] = isbnEntity.isbn + } else if entity.entityType == .paymentCard, let cardEntity = entity.paymentCardEntity { + entityData["network"] = cardEntity.paymentCardNetwork.rawValue + entityData["number"] = cardEntity.paymentCardNumber + } else if entity.entityType == .trackingNumber, + let trackingEntity = entity.trackingNumberEntity + { + entityData["carrier"] = trackingEntity.parcelCarrier.rawValue + entityData["number"] = trackingEntity.parcelTrackingNumber + } else if entity.entityType == .money, let moneyEntity = entity.moneyEntity { + entityData["fraction"] = moneyEntity.fractionalPart + entityData["integer"] = moneyEntity.integerPart + entityData["unnormalized"] = moneyEntity.unnormalizedCurrency + } + return entityData + } + + private func entityTypeToNumber(_ type: EntityType) -> Int { + switch type { + case .address: return 1 + case .dateTime: return 2 + case .email: return 3 + case .flightNumber: return 4 + case .IBAN: return 5 + case .ISBN: return 6 + case .paymentCard: return 7 + case .phone: return 8 + case .trackingNumber: return 9 + case .URL: return 10 + case .money: return 11 + default: return 0 + } + } + + private func valueAsString(_ value: Any?) -> String? { + guard value != nil, value is NSNull == false else { return nil } + return value as? String + } + + private func valueAsNumber(_ value: Any?) -> NSNumber? { + guard value != nil, value is NSNull == false else { return nil } + return value as? NSNumber + } + + private func valueAsNumberArray(_ value: Any?) -> [NSNumber]? { + guard value != nil, value is NSNull == false else { return nil } + return value as? [NSNumber] + } + + private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let modelTag = args["model"] as? String + else { + result( + FlutterError( + code: "invalid_args", message: "Missing model argument", details: nil)) + return + } + let modelIdentifier = EntityExtractionModelIdentifier(rawValue: modelTag) + let model = EntityExtractorRemoteModel.entityExtractorRemoteModel( + identifier: modelIdentifier) + if genericModelManager == nil { + genericModelManager = GenericModelManager() + } + genericModelManager?.manage(model: model, call: call, result: result) + } + #endif +} diff --git a/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection.podspec b/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection.podspec index 1fe87008..656fc14f 100644 --- a/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection.podspec +++ b/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_face_detection/Sources/google_mlkit_face_detection/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/FaceDetection', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection/Package.swift b/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection/Package.swift new file mode 100644 index 00000000..18a4e205 --- /dev/null +++ b/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-face-detection", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-face-detection", + targets: ["google_mlkit_face_detection"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_face_detection", + dependencies: [ + .product(name: "MLKitFaceDetection", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_face_detection" + ) + ] +) diff --git a/packages/google_mlkit_face_detection/ios/Classes/GoogleMlKitFaceDetectionPlugin.swift b/packages/google_mlkit_face_detection/ios/google_mlkit_face_detection/Sources/google_mlkit_face_detection/GoogleMlKitFaceDetectionPlugin.swift similarity index 100% rename from packages/google_mlkit_face_detection/ios/Classes/GoogleMlKitFaceDetectionPlugin.swift rename to packages/google_mlkit_face_detection/ios/google_mlkit_face_detection/Sources/google_mlkit_face_detection/GoogleMlKitFaceDetectionPlugin.swift diff --git a/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection.podspec b/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection.podspec index 1a543fe6..53763115 100644 --- a/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection.podspec +++ b/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_face_mesh_detection/Sources/google_mlkit_face_mesh_detection/**/*.swift' s.dependency 'Flutter' # s.dependency 'GoogleMLKit/FaceMeshDetection', '~> 5.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection/Package.swift b/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection/Package.swift new file mode 100644 index 00000000..d3253781 --- /dev/null +++ b/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-face-mesh-detection", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-face-mesh-detection", + targets: ["google_mlkit_face_mesh_detection"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons") + ], + targets: [ + .target( + name: "google_mlkit_face_mesh_detection", + dependencies: [ + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons") + ], + path: "Sources/google_mlkit_face_mesh_detection" + ) + ] +) diff --git a/packages/google_mlkit_face_mesh_detection/ios/Classes/GoogleMlKitFaceMeshDetectionPlugin.swift b/packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection/Sources/google_mlkit_face_mesh_detection/GoogleMlKitFaceMeshDetectionPlugin.swift similarity index 100% rename from packages/google_mlkit_face_mesh_detection/ios/Classes/GoogleMlKitFaceMeshDetectionPlugin.swift rename to packages/google_mlkit_face_mesh_detection/ios/google_mlkit_face_mesh_detection/Sources/google_mlkit_face_mesh_detection/GoogleMlKitFaceMeshDetectionPlugin.swift diff --git a/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description.podspec b/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description.podspec index 4af76e93..fdaccad0 100644 --- a/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description.podspec +++ b/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_genai_image_description/Sources/google_mlkit_genai_image_description/**/*.swift' s.dependency 'Flutter' s.platform = :ios, '15.5' s.ios.deployment_target = '15.5' diff --git a/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description/Package.swift b/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description/Package.swift new file mode 100644 index 00000000..787d4e77 --- /dev/null +++ b/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-genai-image-description", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-genai-image-description", + targets: ["google_mlkit_genai_image_description"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_genai_image_description", + path: "Sources/google_mlkit_genai_image_description" + ) + ] +) diff --git a/packages/google_mlkit_genai_image_description/ios/Classes/GoogleMlKitGenaiImageDescriptionPlugin.swift b/packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description/Sources/google_mlkit_genai_image_description/GoogleMlKitGenaiImageDescriptionPlugin.swift similarity index 100% rename from packages/google_mlkit_genai_image_description/ios/Classes/GoogleMlKitGenaiImageDescriptionPlugin.swift rename to packages/google_mlkit_genai_image_description/ios/google_mlkit_genai_image_description/Sources/google_mlkit_genai_image_description/GoogleMlKitGenaiImageDescriptionPlugin.swift diff --git a/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt.podspec b/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt.podspec index 4af76e93..ceeec86b 100644 --- a/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt.podspec +++ b/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_genai_prompt/Sources/google_mlkit_genai_prompt/**/*.swift' s.dependency 'Flutter' s.platform = :ios, '15.5' s.ios.deployment_target = '15.5' diff --git a/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt/Package.swift b/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt/Package.swift new file mode 100644 index 00000000..d68d43d3 --- /dev/null +++ b/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-genai-prompt", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-genai-prompt", + targets: ["google_mlkit_genai_prompt"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_genai_prompt", + path: "Sources/google_mlkit_genai_prompt" + ) + ] +) diff --git a/packages/google_mlkit_genai_prompt/ios/Classes/GoogleMlKitGenaiPromptPlugin.swift b/packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt/Sources/google_mlkit_genai_prompt/GoogleMlKitGenaiPromptPlugin.swift similarity index 100% rename from packages/google_mlkit_genai_prompt/ios/Classes/GoogleMlKitGenaiPromptPlugin.swift rename to packages/google_mlkit_genai_prompt/ios/google_mlkit_genai_prompt/Sources/google_mlkit_genai_prompt/GoogleMlKitGenaiPromptPlugin.swift diff --git a/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading.podspec b/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading.podspec index 4af76e93..227967f1 100644 --- a/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading.podspec +++ b/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_genai_proofreading/Sources/google_mlkit_genai_proofreading/**/*.swift' s.dependency 'Flutter' s.platform = :ios, '15.5' s.ios.deployment_target = '15.5' diff --git a/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading/Package.swift b/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading/Package.swift new file mode 100644 index 00000000..c6172b00 --- /dev/null +++ b/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-genai-proofreading", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-genai-proofreading", + targets: ["google_mlkit_genai_proofreading"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_genai_proofreading", + path: "Sources/google_mlkit_genai_proofreading" + ) + ] +) diff --git a/packages/google_mlkit_genai_proofreading/ios/Classes/GoogleMlKitGenaiProofreadingPlugin.swift b/packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading/Sources/google_mlkit_genai_proofreading/GoogleMlKitGenaiProofreadingPlugin.swift similarity index 100% rename from packages/google_mlkit_genai_proofreading/ios/Classes/GoogleMlKitGenaiProofreadingPlugin.swift rename to packages/google_mlkit_genai_proofreading/ios/google_mlkit_genai_proofreading/Sources/google_mlkit_genai_proofreading/GoogleMlKitGenaiProofreadingPlugin.swift diff --git a/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting.podspec b/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting.podspec index 4af76e93..2cf9a873 100644 --- a/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting.podspec +++ b/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_genai_rewriting/Sources/google_mlkit_genai_rewriting/**/*.swift' s.dependency 'Flutter' s.platform = :ios, '15.5' s.ios.deployment_target = '15.5' diff --git a/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting/Package.swift b/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting/Package.swift new file mode 100644 index 00000000..c30e0d73 --- /dev/null +++ b/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-genai-rewriting", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-genai-rewriting", + targets: ["google_mlkit_genai_rewriting"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_genai_rewriting", + path: "Sources/google_mlkit_genai_rewriting" + ) + ] +) diff --git a/packages/google_mlkit_genai_rewriting/ios/Classes/GoogleMlKitGenaiRewritingPlugin.swift b/packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting/Sources/google_mlkit_genai_rewriting/GoogleMlKitGenaiRewritingPlugin.swift similarity index 100% rename from packages/google_mlkit_genai_rewriting/ios/Classes/GoogleMlKitGenaiRewritingPlugin.swift rename to packages/google_mlkit_genai_rewriting/ios/google_mlkit_genai_rewriting/Sources/google_mlkit_genai_rewriting/GoogleMlKitGenaiRewritingPlugin.swift diff --git a/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition.podspec b/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition.podspec index 4af76e93..71d711fe 100644 --- a/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition.podspec +++ b/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_genai_speech_recognition/Sources/google_mlkit_genai_speech_recognition/**/*.swift' s.dependency 'Flutter' s.platform = :ios, '15.5' s.ios.deployment_target = '15.5' diff --git a/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition/Package.swift b/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition/Package.swift new file mode 100644 index 00000000..33495403 --- /dev/null +++ b/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-genai-speech-recognition", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-genai-speech-recognition", + targets: ["google_mlkit_genai_speech_recognition"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_genai_speech_recognition", + path: "Sources/google_mlkit_genai_speech_recognition" + ) + ] +) diff --git a/packages/google_mlkit_genai_speech_recognition/ios/Classes/GoogleMlKitGenaiSpeechRecognitionPlugin.swift b/packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition/Sources/google_mlkit_genai_speech_recognition/GoogleMlKitGenaiSpeechRecognitionPlugin.swift similarity index 100% rename from packages/google_mlkit_genai_speech_recognition/ios/Classes/GoogleMlKitGenaiSpeechRecognitionPlugin.swift rename to packages/google_mlkit_genai_speech_recognition/ios/google_mlkit_genai_speech_recognition/Sources/google_mlkit_genai_speech_recognition/GoogleMlKitGenaiSpeechRecognitionPlugin.swift diff --git a/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization.podspec b/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization.podspec index 4af76e93..a5c5fe53 100644 --- a/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization.podspec +++ b/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_genai_summarization/Sources/google_mlkit_genai_summarization/**/*.swift' s.dependency 'Flutter' s.platform = :ios, '15.5' s.ios.deployment_target = '15.5' diff --git a/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization/Package.swift b/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization/Package.swift new file mode 100644 index 00000000..9cc40608 --- /dev/null +++ b/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-genai-summarization", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-genai-summarization", + targets: ["google_mlkit_genai_summarization"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_genai_summarization", + path: "Sources/google_mlkit_genai_summarization" + ) + ] +) diff --git a/packages/google_mlkit_genai_summarization/ios/Classes/GoogleMlKitGenaiSummarizationPlugin.swift b/packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization/Sources/google_mlkit_genai_summarization/GoogleMlKitGenaiSummarizationPlugin.swift similarity index 100% rename from packages/google_mlkit_genai_summarization/ios/Classes/GoogleMlKitGenaiSummarizationPlugin.swift rename to packages/google_mlkit_genai_summarization/ios/google_mlkit_genai_summarization/Sources/google_mlkit_genai_summarization/GoogleMlKitGenaiSummarizationPlugin.swift diff --git a/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling.podspec b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling.podspec index 8b3ef3bd..f5e6e11c 100644 --- a/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling.podspec +++ b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_image_labeling/Sources/google_mlkit_image_labeling/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/ImageLabeling', '~> 9.0.0' s.dependency 'GoogleMLKit/ImageLabelingCustom', '~> 9.0.0' diff --git a/packages/google_mlkit_image_labeling/ios/Classes/GoogleMlKitImageLabelingPlugin.swift b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Classes/GoogleMlKitImageLabelingPlugin.swift similarity index 100% rename from packages/google_mlkit_image_labeling/ios/Classes/GoogleMlKitImageLabelingPlugin.swift rename to packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Classes/GoogleMlKitImageLabelingPlugin.swift diff --git a/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Package.swift b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Package.swift new file mode 100644 index 00000000..bd2abac3 --- /dev/null +++ b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-image-labeling", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-image-labeling", + targets: ["google_mlkit_image_labeling"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_image_labeling", + dependencies: [ + .product(name: "MLKitImageLabeling", package: "google-mlkit-swiftpm"), + .product(name: "MLKitImageLabelingCustom", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_image_labeling" + ) + ] +) diff --git a/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Sources/google_mlkit_image_labeling/GoogleMlKitImageLabelingPlugin.swift b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Sources/google_mlkit_image_labeling/GoogleMlKitImageLabelingPlugin.swift new file mode 100644 index 00000000..128be726 --- /dev/null +++ b/packages/google_mlkit_image_labeling/ios/google_mlkit_image_labeling/Sources/google_mlkit_image_labeling/GoogleMlKitImageLabelingPlugin.swift @@ -0,0 +1,215 @@ +import Flutter +import MLKitCommon +import MLKitImageLabeling +import MLKitImageLabelingCommon +import MLKitImageLabelingCustom +import MLKitVision +import google_mlkit_commons + +#if canImport(MLKitLinkFirebase) + import MLKitLinkFirebase +#endif + +@objc +public class GoogleMlKitImageLabelingPlugin: NSObject, FlutterPlugin { + private var instances: [String: ImageLabeler] = [:] + #if canImport(MLKitLinkFirebase) + private var genericModelManager: GenericModelManager? + #endif + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_image_labeler", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitImageLabelingPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "vision#startImageLabelDetector": + handleDetection(call: call, result: result) + case "vision#closeImageLabelDetector": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + case "vision#manageFirebaseModels": + #if canImport(MLKitLinkFirebase) + manageModel(call: call, result: result) + #else + result( + FlutterError( + code: "ERROR_MISSING_MLKIT_FIREBASE_MODELS", + message: + "Add the GoogleMLKit/LinkFirebase pod to your Podfile to use Firebase-hosted models.", + details: nil + )) + #endif + default: + result(FlutterMethodNotImplemented) + } + } + + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let imageData = args["imageData"] as? [String: Any], + let uid = args["id"] as? String + else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + guard let image = VisionImage.visionImage(from: imageData) else { + result( + FlutterError( + code: "invalid_image", message: "Invalid or missing image data", details: nil)) + return + } + + let labeler: ImageLabeler + if let existing = instances[uid] { + labeler = existing + } else { + guard let dictionary = args["options"] as? [String: Any] else { + result(FlutterError(code: "invalid_args", message: "Missing options", details: nil)) + return + } + let type = dictionary["type"] as? String ?? "base" + switch type { + case "base": + labeler = ImageLabeler.imageLabeler(options: getDefaultOptions(dictionary)) + case "local": + guard let options = getLocalOptions(dictionary) else { + result( + FlutterError( + code: "invalid_args", message: "Missing path for local model", + details: nil)) + return + } + labeler = ImageLabeler.imageLabeler(options: options) + case "remote": + #if canImport(MLKitLinkFirebase) + guard let modelName = dictionary["modelName"] as? String, !modelName.isEmpty + else { + result( + FlutterError( + code: "invalid_args", + message: "Missing or invalid modelName for remote model", + details: nil + )) + return + } + if let options = getRemoteOptions(dictionary) { + labeler = ImageLabeler.imageLabeler(options: options) + } else { + result( + FlutterError( + code: "Error Model has not been downloaded yet", + message: "Model has not been downloaded yet", + details: "Model has not been downloaded yet" + )) + return + } + #else + result( + FlutterError( + code: "ERROR_MISSING_MLKIT_FIREBASE_MODELS", + message: + "Add the GoogleMLKit/LinkFirebase pod to your Podfile to use Firebase-hosted models.", + details: nil + )) + return + #endif + default: + result( + FlutterError( + code: type, + message: "Invalid model type: \(type)", + details: "Invalid model type: \(type)" + )) + return + } + instances[uid] = labeler + } + + labeler.process(image) { labels, error in + if let error = error as NSError? { + result( + FlutterError( + code: "Error \(error.code)", message: error.domain, + details: error.localizedDescription)) + return + } + guard let labels = labels else { + result([]) + return + } + let labelData = labels.map { label in + [ + "confidence": label.confidence, + "index": label.index, + "text": label.text, + ] as [String: Any] + } + result(labelData) + } + } + + private func getDefaultOptions(_ optionsData: [String: Any]) -> ImageLabelerOptions { + let options = ImageLabelerOptions() + if let conf = optionsData["confidenceThreshold"] as? NSNumber { + options.confidenceThreshold = conf + } + return options + } + + private func getLocalOptions(_ optionsData: [String: Any]) -> CustomImageLabelerOptions? { + guard let path = optionsData["path"] as? String else { return nil } + let localModel = LocalModel(path: path) + let options = CustomImageLabelerOptions(localModel: localModel) + if let conf = optionsData["confidenceThreshold"] as? NSNumber { + options.confidenceThreshold = conf + } + if let maxCount = optionsData["maxCount"] as? NSNumber { + options.maxResultCount = maxCount.intValue + } + return options + } + + #if canImport(MLKitLinkFirebase) + private func getRemoteOptions(_ optionsData: [String: Any]) -> CustomImageLabelerOptions? { + guard let modelName = optionsData["modelName"] as? String else { return nil } + let firebaseModelSource = FirebaseModelSource(name: modelName) + let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource) + guard ModelManager.modelManager().isModelDownloaded(remoteModel) else { + return nil + } + let options = CustomImageLabelerOptions(remoteModel: remoteModel) + if let conf = optionsData["confidenceThreshold"] as? NSNumber { + options.confidenceThreshold = conf + } + if let maxCount = optionsData["maxCount"] as? NSNumber { + options.maxResultCount = maxCount.intValue + } + return options + } + + private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let modelTag = args["model"] as? String + else { + result( + FlutterError( + code: "invalid_args", message: "Missing model argument", details: nil)) + return + } + let firebaseModelSource = FirebaseModelSource(name: modelTag) + let model = CustomRemoteModel(remoteModelSource: firebaseModelSource) + if genericModelManager == nil { + genericModelManager = GenericModelManager() + } + genericModelManager?.manage(model: model, call: call, result: result) + } + #endif +} diff --git a/packages/google_mlkit_language_id/ios/google_mlkit_language_id.podspec b/packages/google_mlkit_language_id/ios/google_mlkit_language_id.podspec index 330af428..867a63d4 100644 --- a/packages/google_mlkit_language_id/ios/google_mlkit_language_id.podspec +++ b/packages/google_mlkit_language_id/ios/google_mlkit_language_id.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_language_id/Sources/google_mlkit_language_id/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/LanguageID', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_language_id/ios/Classes/GoogleMlKitLanguageIdPlugin.swift b/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Classes/GoogleMlKitLanguageIdPlugin.swift similarity index 100% rename from packages/google_mlkit_language_id/ios/Classes/GoogleMlKitLanguageIdPlugin.swift rename to packages/google_mlkit_language_id/ios/google_mlkit_language_id/Classes/GoogleMlKitLanguageIdPlugin.swift diff --git a/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Package.swift b/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Package.swift new file mode 100644 index 00000000..6ac50b1e --- /dev/null +++ b/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-language-id", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-language-id", + targets: ["google_mlkit_language_id"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_language_id", + dependencies: [ + .product(name: "MLKitLanguageID", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_language_id" + ) + ] +) diff --git a/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Sources/google_mlkit_language_id/GoogleMlKitLanguageIdPlugin.swift b/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Sources/google_mlkit_language_id/GoogleMlKitLanguageIdPlugin.swift new file mode 100644 index 00000000..dd42779f --- /dev/null +++ b/packages/google_mlkit_language_id/ios/google_mlkit_language_id/Sources/google_mlkit_language_id/GoogleMlKitLanguageIdPlugin.swift @@ -0,0 +1,109 @@ +import Flutter +import MLKitLanguageID +import google_mlkit_commons + +@objc +public class GoogleMlKitLanguageIdPlugin: NSObject, FlutterPlugin { + private var instances: [String: LanguageIdentification] = [:] + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_language_identifier", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitLanguageIdPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "nlp#startLanguageIdentifier": + handleDetection(call: call, result: result) + case "nlp#closeLanguageIdentifier": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + + private func initialize(call: FlutterMethodCall) -> LanguageIdentification? { + guard let args = call.arguments as? [String: Any], + let confidence = args["confidence"] as? NSNumber else { return nil } + let options = LanguageIdentificationOptions(confidenceThreshold: confidence.floatValue) + return LanguageIdentification.languageIdentification(options: options) + } + + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let uid = args["id"] as? String, + let text = args["text"] as? String else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + + let languageId: LanguageIdentification + if let existing = instances[uid] { + languageId = existing + } else { + guard let newLanguageId = initialize(call: call) else { + result(FlutterError(code: "invalid_args", message: "Invalid options", details: nil)) + return + } + languageId = newLanguageId + instances[uid] = languageId + } + + let possibleLanguages = (args["possibleLanguages"] as? NSNumber)?.boolValue ?? false + if possibleLanguages { + identifyPossibleLanguages(in: text, languageId: languageId, result: result) + } else { + identifyLanguage(in: text, languageId: languageId, result: result) + } + } + + private func identifyPossibleLanguages( + in text: String, + languageId: LanguageIdentification, + result: @escaping FlutterResult + ) { + languageId.identifyPossibleLanguages(for: text) { identifiedLanguages, error in + if let error = error as NSError? { + result(FlutterError( + code: "Error \(error.code)", + message: error.domain, + details: error.localizedDescription + )) + return + } + guard let list = identifiedLanguages else { + result([]) + return + } + let resultArray = list.map { (lang: IdentifiedLanguage) -> [String: Any] in + ["language": lang.languageTag, "confidence": NSNumber(value: lang.confidence)] + } + result(resultArray) + } + } + + private func identifyLanguage( + in text: String, + languageId: LanguageIdentification, + result: @escaping FlutterResult + ) { + languageId.identifyLanguage(for: text) { languageTag, error in + if let error = error as NSError? { + result(FlutterError( + code: "Error \(error.code)", + message: error.domain, + details: error.localizedDescription + )) + return + } + result(languageTag) + } + } +} diff --git a/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection.podspec b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection.podspec index 6190c5f4..a8faae21 100644 --- a/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection.podspec +++ b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_object_detection/Sources/google_mlkit_object_detection/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/ObjectDetection', '~> 9.0.0' s.dependency 'GoogleMLKit/ObjectDetectionCustom', '~> 9.0.0' diff --git a/packages/google_mlkit_object_detection/ios/Classes/GoogleMlKitObjectDetectionPlugin.swift b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Classes/GoogleMlKitObjectDetectionPlugin.swift similarity index 100% rename from packages/google_mlkit_object_detection/ios/Classes/GoogleMlKitObjectDetectionPlugin.swift rename to packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Classes/GoogleMlKitObjectDetectionPlugin.swift diff --git a/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Package.swift b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Package.swift new file mode 100644 index 00000000..3b52e8d4 --- /dev/null +++ b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-object-detection", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-object-detection", + targets: ["google_mlkit_object_detection"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_object_detection", + dependencies: [ + .product(name: "MLKitObjectDetection", package: "google-mlkit-swiftpm"), + .product(name: "MLKitObjectDetectionCustom", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_object_detection" + ) + ] +) diff --git a/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Sources/google_mlkit_object_detection/GoogleMlKitObjectDetectionPlugin.swift b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Sources/google_mlkit_object_detection/GoogleMlKitObjectDetectionPlugin.swift new file mode 100644 index 00000000..1fae33f1 --- /dev/null +++ b/packages/google_mlkit_object_detection/ios/google_mlkit_object_detection/Sources/google_mlkit_object_detection/GoogleMlKitObjectDetectionPlugin.swift @@ -0,0 +1,253 @@ +import Flutter +import MLKitCommon +import MLKitObjectDetection +import MLKitObjectDetectionCommon +import MLKitObjectDetectionCustom +import MLKitVision +import google_mlkit_commons + +#if canImport(MLKitLinkFirebase) + import MLKitLinkFirebase +#endif + +@objc +public class GoogleMlKitObjectDetectionPlugin: NSObject, FlutterPlugin { + private var instances: [String: ObjectDetector] = [:] + #if canImport(MLKitLinkFirebase) + private var genericModelManager: GenericModelManager? + #endif + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_object_detector", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitObjectDetectionPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "vision#startObjectDetector": + handleDetection(call: call, result: result) + case "vision#closeObjectDetector": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + case "vision#manageFirebaseModels": + #if canImport(MLKitLinkFirebase) + manageModel(call: call, result: result) + #else + result( + FlutterError( + code: "ERROR_MISSING_MLKIT_FIREBASE_MODELS", + message: + "Add the GoogleMLKit/LinkFirebase pod to your Podfile to use Firebase-hosted models.", + details: nil + )) + #endif + default: + result(FlutterMethodNotImplemented) + } + } + + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let imageData = args["imageData"] as? [String: Any], + let uid = args["id"] as? String + else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + guard let image = VisionImage.visionImage(from: imageData) else { + result( + FlutterError( + code: "invalid_image", message: "Invalid or missing image data", details: nil)) + return + } + + let objectDetector: ObjectDetector + if let existing = instances[uid] { + objectDetector = existing + } else { + guard let dictionary = args["options"] as? [String: Any] else { + result(FlutterError(code: "invalid_args", message: "Missing options", details: nil)) + return + } + guard let detector = createDetector(from: dictionary, result: result) else { return } + objectDetector = detector + instances[uid] = objectDetector + } + + objectDetector.process(image) { objects, error in + if let error = error as NSError? { + result( + FlutterError( + code: "Error \(error.code)", message: error.domain, + details: error.localizedDescription)) + return + } + guard let objects = objects else { + result([]) + return + } + let objectsData = objects.map { object -> [String: Any] in + let labels = object.labels.map { label in + [ + "index": label.index, + "text": label.text, + "confidence": label.confidence, + ] as [String: Any] + } + var data: [String: Any] = [ + "rect": [ + "left": Double(object.frame.origin.x), + "top": Double(object.frame.origin.y), + "right": Double(object.frame.origin.x + object.frame.size.width), + "bottom": Double(object.frame.origin.y + object.frame.size.height), + ] as [String: Double], + "labels": labels, + ] + if let trackingID = object.trackingID { + data["trackingId"] = trackingID + } + return data + } + result(objectsData) + } + } + + private func createDetector(from dictionary: [String: Any], result: @escaping FlutterResult) + -> ObjectDetector? + { + let type = dictionary["type"] as? String ?? "base" + switch type { + case "base": + return ObjectDetector.objectDetector(options: getDefaultOptions(dictionary)) + case "local": + guard let options = getLocalOptions(dictionary) else { + result( + FlutterError( + code: "invalid_args", message: "Missing path for local model", details: nil) + ) + return nil + } + return ObjectDetector.objectDetector(options: options) + case "remote": + #if canImport(MLKitLinkFirebase) + guard let modelName = dictionary["modelName"] as? String, !modelName.isEmpty else { + result( + FlutterError( + code: "invalid_args", + message: "Missing or invalid modelName for remote model", + details: nil + )) + return nil + } + if let options = getRemoteOptions(dictionary) { + return ObjectDetector.objectDetector(options: options) + } + result( + FlutterError( + code: "Error Model has not been downloaded yet", + message: "Model has not been downloaded yet", + details: "Model has not been downloaded yet" + )) + return nil + #else + result( + FlutterError( + code: "ERROR_MISSING_MLKIT_FIREBASE_MODELS", + message: + "Add the GoogleMLKit/LinkFirebase pod to your Podfile to use Firebase-hosted models.", + details: nil + )) + return nil + #endif + default: + result( + FlutterError( + code: type, + message: "Invalid model type: \(type)", + details: "Invalid model type: \(type)" + )) + return nil + } + } + + private func getDefaultOptions(_ dictionary: [String: Any]) -> ObjectDetectorOptions { + let options = ObjectDetectorOptions() + let mode = (dictionary["mode"] as? NSNumber)?.intValue ?? 0 + options.detectorMode = + mode == 0 ? ObjectDetectorMode.stream : ObjectDetectorMode.singleImage + options.shouldEnableClassification = + (dictionary["classify"] as? NSNumber)?.boolValue ?? false + options.shouldEnableMultipleObjects = + (dictionary["multiple"] as? NSNumber)?.boolValue ?? false + return options + } + + private func getLocalOptions(_ dictionary: [String: Any]) -> CustomObjectDetectorOptions? { + guard let path = dictionary["path"] as? String else { return nil } + let localModel = LocalModel(path: path) + let options = CustomObjectDetectorOptions(localModel: localModel) + let mode = (dictionary["mode"] as? NSNumber)?.intValue ?? 0 + options.detectorMode = + mode == 0 ? ObjectDetectorMode.stream : ObjectDetectorMode.singleImage + options.shouldEnableClassification = + (dictionary["classify"] as? NSNumber)?.boolValue ?? false + options.shouldEnableMultipleObjects = + (dictionary["multiple"] as? NSNumber)?.boolValue ?? false + if let threshold = dictionary["threshold"] as? NSNumber { + options.classificationConfidenceThreshold = threshold + } + if let maxLabels = dictionary["maxLabels"] as? NSNumber { + options.maxPerObjectLabelCount = maxLabels.intValue + } + return options + } + + #if canImport(MLKitLinkFirebase) + private func getRemoteOptions(_ dictionary: [String: Any]) -> CustomObjectDetectorOptions? { + guard let modelName = dictionary["modelName"] as? String else { return nil } + let firebaseModelSource = FirebaseModelSource(name: modelName) + let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource) + guard ModelManager.modelManager().isModelDownloaded(remoteModel) else { + return nil + } + let options = CustomObjectDetectorOptions(remoteModel: remoteModel) + let mode = (dictionary["mode"] as? NSNumber)?.intValue ?? 0 + options.detectorMode = + mode == 0 ? ObjectDetectorMode.stream : ObjectDetectorMode.singleImage + options.shouldEnableClassification = + (dictionary["classify"] as? NSNumber)?.boolValue ?? false + options.shouldEnableMultipleObjects = + (dictionary["multiple"] as? NSNumber)?.boolValue ?? false + if let threshold = dictionary["threshold"] as? NSNumber { + options.classificationConfidenceThreshold = threshold + } + if let maxLabels = dictionary["maxLabels"] as? NSNumber { + options.maxPerObjectLabelCount = maxLabels.intValue + } + return options + } + + private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let modelTag = args["model"] as? String + else { + result( + FlutterError( + code: "invalid_args", message: "Missing model argument", details: nil)) + return + } + let firebaseModelSource = FirebaseModelSource(name: modelTag) + let model = CustomRemoteModel(remoteModelSource: firebaseModelSource) + if genericModelManager == nil { + genericModelManager = GenericModelManager() + } + genericModelManager?.manage(model: model, call: call, result: result) + } + #endif +} diff --git a/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection.podspec b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection.podspec index e71db861..79900c36 100644 --- a/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection.podspec +++ b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_pose_detection/Sources/google_mlkit_pose_detection/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/PoseDetection', '~> 9.0.0' s.dependency 'GoogleMLKit/PoseDetectionAccurate', '~> 9.0.0' diff --git a/packages/google_mlkit_pose_detection/ios/Classes/GoogleMlKitPoseDetectionPlugin.swift b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Classes/GoogleMlKitPoseDetectionPlugin.swift similarity index 100% rename from packages/google_mlkit_pose_detection/ios/Classes/GoogleMlKitPoseDetectionPlugin.swift rename to packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Classes/GoogleMlKitPoseDetectionPlugin.swift diff --git a/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Package.swift b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Package.swift new file mode 100644 index 00000000..f73d3f91 --- /dev/null +++ b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-pose-detection", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-pose-detection", + targets: ["google_mlkit_pose_detection"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_pose_detection", + dependencies: [ + .product(name: "MLKitPoseDetection", package: "google-mlkit-swiftpm"), + .product(name: "MLKitPoseDetectionAccurate", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_pose_detection" + ) + ] +) diff --git a/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Sources/google_mlkit_pose_detection/GoogleMlKitPoseDetectionPlugin.swift b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Sources/google_mlkit_pose_detection/GoogleMlKitPoseDetectionPlugin.swift new file mode 100644 index 00000000..955b9366 --- /dev/null +++ b/packages/google_mlkit_pose_detection/ios/google_mlkit_pose_detection/Sources/google_mlkit_pose_detection/GoogleMlKitPoseDetectionPlugin.swift @@ -0,0 +1,136 @@ +import Flutter +import MLKitVision +import MLKitPoseDetection +import MLKitPoseDetectionCommon +import MLKitPoseDetectionAccurate +import google_mlkit_commons + +@objc +public class GoogleMlKitPoseDetectionPlugin: NSObject, FlutterPlugin { + private var instances: [String: PoseDetector] = [:] + + private static let landmarkTypeToNumber: [PoseLandmarkType: Int] = [ + .nose: 0, + .leftEyeInner: 1, + .leftEye: 2, + .leftEyeOuter: 3, + .rightEyeInner: 4, + .rightEye: 5, + .rightEyeOuter: 6, + .leftEar: 7, + .rightEar: 8, + .mouthLeft: 9, + .mouthRight: 10, + .leftShoulder: 11, + .rightShoulder: 12, + .leftElbow: 13, + .rightElbow: 14, + .leftWrist: 15, + .rightWrist: 16, + .leftPinkyFinger: 17, + .rightPinkyFinger: 18, + .leftIndexFinger: 19, + .rightIndexFinger: 20, + .leftThumb: 21, + .rightThumb: 22, + .leftHip: 23, + .rightHip: 24, + .leftKnee: 25, + .rightKnee: 26, + .leftAnkle: 27, + .rightAnkle: 28, + .leftHeel: 29, + .rightHeel: 30, + .leftToe: 31, + .rightToe: 32 + ] + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_pose_detector", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitPoseDetectionPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "vision#startPoseDetector": + handleDetection(call: call, result: result) + case "vision#closePoseDetector": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + + private func initialize(call: FlutterMethodCall) -> PoseDetector? { + guard let args = call.arguments as? [String: Any], + let optionsDict = args["options"] as? [String: Any] else { return nil } + let mode = optionsDict["mode"] as? String ?? "stream" + let detectorMode: PoseDetectorMode = mode == "single" ? .singleImage : .stream + let model = optionsDict["model"] as? String ?? "base" + + if model == "base" { + let options = PoseDetectorOptions() + options.detectorMode = detectorMode + return PoseDetector.poseDetector(options: options) + } else { + let options = AccuratePoseDetectorOptions() + options.detectorMode = detectorMode + return PoseDetector.poseDetector(options: options) + } + } + + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let imageData = args["imageData"] as? [String: Any], + let uid = args["id"] as? String else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + guard let image = VisionImage.visionImage(from: imageData) else { + result(FlutterError(code: "invalid_image", message: "Invalid or missing image data", details: nil)) + return + } + + let detector: PoseDetector + if let existing = instances[uid] { + detector = existing + } else { + guard let newDetector = initialize(call: call) else { + result(FlutterError(code: "invalid_args", message: "Invalid options", details: nil)) + return + } + detector = newDetector + instances[uid] = detector + } + + detector.process(image) { poses, error in + if let error = error as NSError? { + result(FlutterError(code: "Error \(error.code)", message: error.domain, details: error.localizedDescription)) + return + } + guard let poses = poses, !poses.isEmpty else { + result([]) + return + } + let array = poses.map { pose -> [[String: Any]] in + pose.landmarks.map { landmark in + [ + "type": Self.landmarkTypeToNumber[landmark.type] ?? -1, + "x": landmark.position.x, + "y": landmark.position.y, + "z": landmark.position.z, + "likelihood": landmark.inFrameLikelihood + ] as [String: Any] + } + } + result(array) + } + } +} diff --git a/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation.podspec b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation.podspec index 17c323d0..5df6c65a 100644 --- a/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation.podspec +++ b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_selfie_segmentation/Sources/google_mlkit_selfie_segmentation/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/SegmentationSelfie', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_selfie_segmentation/ios/Classes/GoogleMlKitSelfieSegmentationPlugin.swift b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Classes/GoogleMlKitSelfieSegmentationPlugin.swift similarity index 100% rename from packages/google_mlkit_selfie_segmentation/ios/Classes/GoogleMlKitSelfieSegmentationPlugin.swift rename to packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Classes/GoogleMlKitSelfieSegmentationPlugin.swift diff --git a/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Package.swift b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Package.swift new file mode 100644 index 00000000..252c79fa --- /dev/null +++ b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-selfie-segmentation", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-selfie-segmentation", + targets: ["google_mlkit_selfie_segmentation"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_selfie_segmentation", + dependencies: [ + .product(name: "MLKitSegmentationSelfie", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_selfie_segmentation" + ) + ] +) diff --git a/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Sources/google_mlkit_selfie_segmentation/GoogleMlKitSelfieSegmentationPlugin.swift b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Sources/google_mlkit_selfie_segmentation/GoogleMlKitSelfieSegmentationPlugin.swift new file mode 100644 index 00000000..5b2ee4c5 --- /dev/null +++ b/packages/google_mlkit_selfie_segmentation/ios/google_mlkit_selfie_segmentation/Sources/google_mlkit_selfie_segmentation/GoogleMlKitSelfieSegmentationPlugin.swift @@ -0,0 +1,103 @@ +import Flutter +import MLKitVision +import MLKitSegmentationSelfie +import MLKitSegmentationCommon +import google_mlkit_commons + +@objc +public class GoogleMlKitSelfieSegmentationPlugin: NSObject, FlutterPlugin { + private var instances: [String: Segmenter] = [:] + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_selfie_segmenter", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitSelfieSegmentationPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "vision#startSelfieSegmenter": + handleDetection(call: call, result: result) + case "vision#closeSelfieSegmenter": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + + private func initialize(call: FlutterMethodCall) -> Segmenter? { + guard let args = call.arguments as? [String: Any] else { return nil } + let isStream = (args["isStream"] as? NSNumber)?.boolValue ?? false + let enableRawSizeMask = (args["enableRawSizeMask"] as? NSNumber)?.boolValue ?? false + let options = SelfieSegmenterOptions() + options.segmenterMode = isStream ? .stream : .singleImage + options.shouldEnableRawSizeMask = enableRawSizeMask + return Segmenter.segmenter(options: options) + } + + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let imageData = args["imageData"] as? [String: Any], + let uid = args["id"] as? String else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + guard let image = VisionImage.visionImage(from: imageData) else { + result(FlutterError(code: "invalid_image", message: "Invalid or missing image data", details: nil)) + return + } + + let segmenter: Segmenter + if let existing = instances[uid] { + segmenter = existing + } else { + guard let newSegmenter = initialize(call: call) else { + result(FlutterError(code: "invalid_args", message: "Invalid options", details: nil)) + return + } + segmenter = newSegmenter + instances[uid] = segmenter + } + + segmenter.process(image) { mask, error in + if let error = error as NSError? { + result(FlutterError(code: "Error \(error.code)", message: error.domain, details: error.localizedDescription)) + return + } + guard let mask = mask else { + result(nil) + return + } + let width = CVPixelBufferGetWidth(mask.buffer) + let height = CVPixelBufferGetHeight(mask.buffer) + CVPixelBufferLockBaseAddress(mask.buffer, .readOnly) + let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) + guard let baseAddress = CVPixelBufferGetBaseAddress(mask.buffer) else { + CVPixelBufferUnlockBaseAddress(mask.buffer, .readOnly) + result(FlutterError(code: "error", message: "Failed to get pixel buffer base address", details: nil)) + return + } + let maskAddress = baseAddress.assumingMemoryBound(to: Float.self) + var confidences: [NSNumber] = [] + var rowAddress = maskAddress + for _ in 0...size) + } + CVPixelBufferUnlockBaseAddress(mask.buffer, .readOnly) + result([ + "width": width, + "height": height, + "confidences": confidences + ]) + } + } +} diff --git a/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply.podspec b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply.podspec index 39e96a34..43bf7889 100644 --- a/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply.podspec +++ b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_smart_reply/Sources/google_mlkit_smart_reply/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/SmartReply', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_smart_reply/ios/Classes/GoogleMlKitSmartReplyPlugin.swift b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Classes/GoogleMlKitSmartReplyPlugin.swift similarity index 100% rename from packages/google_mlkit_smart_reply/ios/Classes/GoogleMlKitSmartReplyPlugin.swift rename to packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Classes/GoogleMlKitSmartReplyPlugin.swift diff --git a/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Package.swift b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Package.swift new file mode 100644 index 00000000..feafad8b --- /dev/null +++ b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-smart-reply", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-smart-reply", + targets: ["google_mlkit_smart_reply"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_smart_reply", + dependencies: [ + .product(name: "MLKitSmartReply", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_smart_reply" + ) + ] +) diff --git a/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Sources/google_mlkit_smart_reply/GoogleMlKitSmartReplyPlugin.swift b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Sources/google_mlkit_smart_reply/GoogleMlKitSmartReplyPlugin.swift new file mode 100644 index 00000000..90c9d46c --- /dev/null +++ b/packages/google_mlkit_smart_reply/ios/google_mlkit_smart_reply/Sources/google_mlkit_smart_reply/GoogleMlKitSmartReplyPlugin.swift @@ -0,0 +1,83 @@ +import Flutter +import MLKitSmartReply +import google_mlkit_commons + +@objc +public class GoogleMlKitSmartReplyPlugin: NSObject, FlutterPlugin { + private var instances: [String: SmartReply] = [:] + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_smart_reply", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitSmartReplyPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "nlp#startSmartReply": + handleStartSmartReply(call: call, result: result) + case "nlp#closeSmartReply": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + + private func handleStartSmartReply(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let json = args["conversation"] as? [[String: Any]], + let uid = args["id"] as? String else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + var conversation: [TextMessage] = [] + for object in json { + guard let text = object["message"] as? String, + let timestampNum = object["timestamp"] as? NSNumber, + let userId = object["userId"] as? String else { continue } + let timestamp = timestampNum.doubleValue + let isLocalUser = userId == "local" + let message = TextMessage( + text: text, + timestamp: timestamp, + userID: userId, + isLocalUser: isLocalUser + ) + conversation.append(message) + } + + let smartReply: SmartReply + if let existing = instances[uid] { + smartReply = existing + } else { + smartReply = SmartReply.smartReply() + instances[uid] = smartReply + } + + smartReply.suggestReplies(for: conversation) { suggestionResult, error in + if let error = error as NSError? { + result(FlutterError( + code: "Error \(error.code)", + message: error.domain, + details: error.localizedDescription + )) + return + } + guard let suggestionResult = suggestionResult else { + result(nil) + return + } + var dict: [String: Any] = ["status": suggestionResult.status.rawValue] + if suggestionResult.status.rawValue == 0 { // MLKSmartReplyResultStatusSuccess + dict["suggestions"] = suggestionResult.suggestions.map { $0.text } + } + result(dict) + } + } +} diff --git a/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation.podspec b/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation.podspec index 04c00a39..694524a7 100644 --- a/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation.podspec +++ b/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.author = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_subject_segmentation/Sources/google_mlkit_subject_segmentation/**/*.swift' s.dependency 'Flutter' # s.dependency 'GoogleMLKit/SubjectSegmentation', '~> 6.0.0' s.platform = :ios, '15.5' diff --git a/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation/Package.swift b/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation/Package.swift new file mode 100644 index 00000000..4f5af4d9 --- /dev/null +++ b/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-subject-segmentation", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-subject-segmentation", + targets: ["google_mlkit_subject_segmentation"]) + ], + dependencies: [ + + ], + targets: [ + .target( + name: "google_mlkit_subject_segmentation", + path: "Sources/google_mlkit_subject_segmentation" + ) + ] +) diff --git a/packages/google_mlkit_subject_segmentation/ios/Classes/GoogleMlKitSubjectSegmentationPlugin.swift b/packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation/Sources/google_mlkit_subject_segmentation/GoogleMlKitSubjectSegmentationPlugin.swift similarity index 100% rename from packages/google_mlkit_subject_segmentation/ios/Classes/GoogleMlKitSubjectSegmentationPlugin.swift rename to packages/google_mlkit_subject_segmentation/ios/google_mlkit_subject_segmentation/Sources/google_mlkit_subject_segmentation/GoogleMlKitSubjectSegmentationPlugin.swift diff --git a/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition.podspec b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition.podspec index c98523b0..d29bb474 100644 --- a/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition.podspec +++ b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_text_recognition/Sources/google_mlkit_text_recognition/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/TextRecognition', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_text_recognition/ios/Classes/GoogleMlKitTextRecognitionPlugin.swift b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Classes/GoogleMlKitTextRecognitionPlugin.swift similarity index 100% rename from packages/google_mlkit_text_recognition/ios/Classes/GoogleMlKitTextRecognitionPlugin.swift rename to packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Classes/GoogleMlKitTextRecognitionPlugin.swift diff --git a/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Package.swift b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Package.swift new file mode 100644 index 00000000..72151ef0 --- /dev/null +++ b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-text-recognition", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-text-recognition", + targets: ["google_mlkit_text_recognition"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_text_recognition", + dependencies: [ + .product(name: "MLKitTextRecognition", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_text_recognition" + ) + ] +) diff --git a/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Sources/google_mlkit_text_recognition/GoogleMlKitTextRecognitionPlugin.swift b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Sources/google_mlkit_text_recognition/GoogleMlKitTextRecognitionPlugin.swift new file mode 100644 index 00000000..fb7e74f6 --- /dev/null +++ b/packages/google_mlkit_text_recognition/ios/google_mlkit_text_recognition/Sources/google_mlkit_text_recognition/GoogleMlKitTextRecognitionPlugin.swift @@ -0,0 +1,179 @@ +import Flutter +import MLKitVision +import MLKitTextRecognition +import MLKitTextRecognitionCommon +import google_mlkit_commons +#if canImport(MLKitTextRecognitionChinese) +import MLKitTextRecognitionChinese +#endif +#if canImport(MLKitTextRecognitionDevanagari) +import MLKitTextRecognitionDevanagari +#endif +#if canImport(MLKitTextRecognitionJapanese) +import MLKitTextRecognitionJapanese +#endif +#if canImport(MLKitTextRecognitionKorean) +import MLKitTextRecognitionKorean +#endif + +@objc +public class GoogleMlKitTextRecognitionPlugin: NSObject, FlutterPlugin { + private var instances: [String: TextRecognizer] = [:] + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_text_recognizer", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitTextRecognitionPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "vision#startTextRecognizer": + handleDetection(call: call, result: result) + case "vision#closeTextRecognizer": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + + private func initialize(call: FlutterMethodCall) -> TextRecognizer { + let scriptIndex = (call.arguments as? [String: Any])?["script"] as? Int ?? 0 + switch scriptIndex { + case 0: + return TextRecognizer.textRecognizer(options: TextRecognizerOptions()) + case 1: + #if canImport(MLKitTextRecognitionChinese) + return TextRecognizer.textRecognizer(options: ChineseTextRecognizerOptions()) + #else + return TextRecognizer.textRecognizer(options: TextRecognizerOptions()) + #endif + case 2: + #if canImport(MLKitTextRecognitionDevanagari) + return TextRecognizer.textRecognizer(options: DevanagariTextRecognizerOptions()) + #else + return TextRecognizer.textRecognizer(options: TextRecognizerOptions()) + #endif + case 3: + #if canImport(MLKitTextRecognitionJapanese) + return TextRecognizer.textRecognizer(options: JapaneseTextRecognizerOptions()) + #else + return TextRecognizer.textRecognizer(options: TextRecognizerOptions()) + #endif + case 4: + #if canImport(MLKitTextRecognitionKorean) + return TextRecognizer.textRecognizer(options: KoreanTextRecognizerOptions()) + #else + return TextRecognizer.textRecognizer(options: TextRecognizerOptions()) + #endif + default: + return TextRecognizer.textRecognizer(options: TextRecognizerOptions()) + } + } + + private func handleDetection(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let imageData = args["imageData"] as? [String: Any], + let uid = args["id"] as? String else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + guard let image = VisionImage.visionImage(from: imageData) else { + result(FlutterError(code: "invalid_image", message: "Invalid or missing image data", details: nil)) + return + } + + let recognizer: TextRecognizer + if let existing = instances[uid] { + recognizer = existing + } else { + recognizer = initialize(call: call) + instances[uid] = recognizer + } + + recognizer.process(image) { visionText, error in + if let error = error as NSError? { + result(FlutterError(code: "Error \(error.code)", message: error.domain, details: error.localizedDescription)) + return + } + guard let visionText = visionText else { + result(["text": "", "blocks": [] as [[String: Any]]]) + return + } + var textBlocks: [[String: Any]] = [] + for block in visionText.blocks { + var blockData = self.addData( + cornerPoints: block.cornerPoints, + frame: block.frame, + languages: block.recognizedLanguages, + text: block.text, + confidence: nil, + angle: nil + ) + var textLines: [[String: Any]] = [] + for line in block.lines { + var lineData = self.addData( + cornerPoints: line.cornerPoints, + frame: line.frame, + languages: line.recognizedLanguages, + text: line.text, + confidence: nil, + angle: nil + ) + var elementsData: [[String: Any]] = [] + for element in line.elements { + var elementData = self.addData( + cornerPoints: element.cornerPoints, + frame: element.frame, + languages: element.recognizedLanguages, + text: element.text, + confidence: nil, + angle: nil + ) + elementData["symbols"] = [] as [[String: Any]] + elementsData.append(elementData) + } + lineData["elements"] = elementsData + textLines.append(lineData) + } + blockData["lines"] = textLines + textBlocks.append(blockData) + } + result(["text": visionText.text, "blocks": textBlocks]) + } + } + + private func addData( + cornerPoints: [NSValue], + frame: CGRect, + languages: [TextRecognizedLanguage], + text: String, + confidence: NSNumber?, + angle: NSNumber? + ) -> [String: Any] { + let points = cornerPoints.map { (point: NSValue) -> [String: Double] in + let cgPoint = point.cgPointValue + return ["x": Double(cgPoint.x), "y": Double(cgPoint.y)] + } + let allLanguageData = languages.compactMap { $0.languageCode } + return [ + "points": points, + "rect": [ + "left": Double(frame.origin.x), + "top": Double(frame.origin.y), + "right": Double(frame.origin.x + frame.size.width), + "bottom": Double(frame.origin.y + frame.size.height) + ] as [String: Double], + "recognizedLanguages": allLanguageData, + "text": text, + "confidence": confidence ?? NSNull(), + "angle": angle ?? NSNull() + ] + } +} diff --git a/packages/google_mlkit_translation/ios/google_mlkit_translation.podspec b/packages/google_mlkit_translation/ios/google_mlkit_translation.podspec index 98ed5b49..ea514947 100644 --- a/packages/google_mlkit_translation/ios/google_mlkit_translation.podspec +++ b/packages/google_mlkit_translation/ios/google_mlkit_translation.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'flutter-ml.dev' s.source = { :path => '.' } - s.source_files = 'Classes/**/*.swift' + s.source_files = 'google_mlkit_translation/Sources/google_mlkit_translation/**/*.swift' s.dependency 'Flutter' s.dependency 'GoogleMLKit/Translate', '~> 9.0.0' s.dependency 'google_mlkit_commons' diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.swift b/packages/google_mlkit_translation/ios/google_mlkit_translation/Classes/GoogleMlKitTranslationPlugin.swift similarity index 100% rename from packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.swift rename to packages/google_mlkit_translation/ios/google_mlkit_translation/Classes/GoogleMlKitTranslationPlugin.swift diff --git a/packages/google_mlkit_translation/ios/google_mlkit_translation/Package.swift b/packages/google_mlkit_translation/ios/google_mlkit_translation/Package.swift new file mode 100644 index 00000000..a1ec0c42 --- /dev/null +++ b/packages/google_mlkit_translation/ios/google_mlkit_translation/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.9 + +import PackageDescription + +let package = Package( + name: "google-mlkit-translation", + platforms: [ + .iOS("15.5") + ], + products: [ + .library( + name: "google-mlkit-translation", + targets: ["google_mlkit_translation"]) + ], + dependencies: [ + .package(path: "../../../google_mlkit_commons/ios/google_mlkit_commons"), + .package( + url: "https://github.com/d-date/google-mlkit-swiftpm", + from: "9.0.0" + ), + ], + targets: [ + .target( + name: "google_mlkit_translation", + dependencies: [ + .product(name: "MLKitTranslate", package: "google-mlkit-swiftpm"), + // Note: The `package` value uses the DIRECTORY name ("google_mlkit_commons"), not the + // Package.swift `name` field ("google-mlkit-commons"). For local path dependencies, SPM + // derives the package identity from the directory name, not the `name` field. + .product(name: "google-mlkit-commons", package: "google_mlkit_commons"), + ], + path: "Sources/google_mlkit_translation" + ) + ] +) diff --git a/packages/google_mlkit_translation/ios/google_mlkit_translation/Sources/google_mlkit_translation/GoogleMlKitTranslationPlugin.swift b/packages/google_mlkit_translation/ios/google_mlkit_translation/Sources/google_mlkit_translation/GoogleMlKitTranslationPlugin.swift new file mode 100644 index 00000000..d0d22fe8 --- /dev/null +++ b/packages/google_mlkit_translation/ios/google_mlkit_translation/Sources/google_mlkit_translation/GoogleMlKitTranslationPlugin.swift @@ -0,0 +1,112 @@ +import Flutter +import MLKitTranslate +import google_mlkit_commons + +@objc +public class GoogleMlKitTranslationPlugin: NSObject, FlutterPlugin { + private var instances: [String: Translator] = [:] + private var genericModelManager: GenericModelManager? + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "google_mlkit_on_device_translator", + binaryMessenger: registrar.messenger() + ) + let instance = GoogleMlKitTranslationPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "nlp#startLanguageTranslator": + handleTranslation(call: call, result: result) + case "nlp#manageLanguageModelModels": + manageModel(call: call, result: result) + case "nlp#closeLanguageTranslator": + if let args = call.arguments as? [String: Any], let uid = args["id"] as? String { + instances.removeValue(forKey: uid) + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + + private func initialize(call: FlutterMethodCall) -> Translator? { + guard let args = call.arguments as? [String: Any], + let sourceTag = args["source"] as? String, + let targetTag = args["target"] as? String + else { + return nil + } + // TranslateLanguage(rawValue:) is non-failable; invalid tags may fail at runtime when the translator is used. + let sourceLang = TranslateLanguage(rawValue: sourceTag) + let targetLang = TranslateLanguage(rawValue: targetTag) + let options = TranslatorOptions(sourceLanguage: sourceLang, targetLanguage: targetLang) + return Translator.translator(options: options) + } + + private func handleTranslation(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let text = args["text"] as? String, + let uid = args["id"] as? String + else { + result(FlutterError(code: "invalid_args", message: "Missing arguments", details: nil)) + return + } + + let translator: Translator + if let existing = instances[uid] { + translator = existing + } else { + guard let newTranslator = initialize(call: call) else { + result( + FlutterError( + code: "invalid_args", + message: "Missing or invalid source/target language", + details: nil + )) + return + } + translator = newTranslator + instances[uid] = translator + } + + translator.downloadModelIfNeeded { error in + if let error = error as NSError? { + result( + FlutterError( + code: "Error \(error.code)", message: error.domain, + details: error.localizedDescription)) + return + } + translator.translate(text) { translatedText, error in + if let error = error as NSError? { + result( + FlutterError( + code: "Error \(error.code)", message: error.domain, + details: error.localizedDescription)) + return + } + result(translatedText ?? "") + } + } + } + + private func manageModel(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String: Any], + let modelTag = args["model"] as? String + else { + result( + FlutterError(code: "invalid_args", message: "Missing model argument", details: nil)) + return + } + // TranslateLanguage(rawValue:) is non-failable; invalid tags may fail when the model is used. + let lang = TranslateLanguage(rawValue: modelTag) + let model = TranslateRemoteModel.translateRemoteModel(language: lang) + if genericModelManager == nil { + genericModelManager = GenericModelManager() + } + genericModelManager?.manage(model: model, call: call, result: result) + } +}