also looking at this
fix(ios): no gated dispatcher arm can hang the page any more (58 → 0)
#120
1 file
+298
-106
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -561,7 +561,11 @@ struct CraftWebView: UIViewRepresentable { | ||
| 561 | 561 | func dispatch(action: String, body: [String: Any], callbackId: String?) { |
| 562 | 562 | switch action { |
| 563 | 563 | case "startListening": |
| 564 | if config.enableSpeechRecognition { startSpeechRecognition() } | |
| 564 | if config.enableSpeechRecognition { | |
| 565 | startSpeechRecognition() | |
| 566 | } else { | |
| 567 | rejectCallback(callbackId, error: "Speech recognition is disabled", code: "CAPABILITY_DISABLED") | |
| 568 | } | |
| 565 | 569 | case "stopListening": |
| 566 | 570 | stopSpeechRecognition() |
| 567 | 571 | case "haptic": |
| @@ -611,23 +615,38 @@ struct CraftWebView: UIViewRepresentable { | ||
| 611 | 615 | rejectCallback(callbackId, error: "Push notifications are disabled", code: "CAPABILITY_DISABLED") |
| 612 | 616 | } |
| 613 | 617 | case "secureSet": |
| 614 | if config.enableSecureStorage, | |
| 615 | let key = body["key"] as? String, | |
| 616 | let value = body["value"] as? String { | |
| 617 | let success = secureStore(key: key, value: value) | |
| 618 | resolveCallback(callbackId, result: success) | |
| 618 | if config.enableSecureStorage { | |
| 619 | if let key = body["key"] as? String, | |
| 620 | let value = body["value"] as? String { | |
| 621 | let success = secureStore(key: key, value: value) | |
| 622 | resolveCallback(callbackId, result: success) | |
| 623 | } else { | |
| 624 | rejectCallback(callbackId, error: "secureSet was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 625 | } | |
| 626 | } else { | |
| 627 | rejectCallback(callbackId, error: "Secure storage is disabled", code: "CAPABILITY_DISABLED") | |
| 619 | 628 | } |
| 620 | 629 | case "secureGet": |
| 621 | if config.enableSecureStorage, | |
| 622 | let key = body["key"] as? String { | |
| 623 | let value = secureRetrieve(key: key) | |
| 624 | resolveCallback(callbackId, result: value as Any) | |
| 630 | if config.enableSecureStorage { | |
| 631 | if let key = body["key"] as? String { | |
| 632 | let value = secureRetrieve(key: key) | |
| 633 | resolveCallback(callbackId, result: value as Any) | |
| 634 | } else { | |
| 635 | rejectCallback(callbackId, error: "secureGet was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 636 | } | |
| 637 | } else { | |
| 638 | rejectCallback(callbackId, error: "Secure storage is disabled", code: "CAPABILITY_DISABLED") | |
| 625 | 639 | } |
| 626 | 640 | case "secureRemove": |
| 627 | if config.enableSecureStorage, | |
| 628 | let key = body["key"] as? String { | |
| 629 | let success = secureRemove(key: key) | |
| 630 | resolveCallback(callbackId, result: success) | |
| 641 | if config.enableSecureStorage { | |
| 642 | if let key = body["key"] as? String { | |
| 643 | let success = secureRemove(key: key) | |
| 644 | resolveCallback(callbackId, result: success) | |
| 645 | } else { | |
| 646 | rejectCallback(callbackId, error: "secureRemove was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 647 | } | |
| 648 | } else { | |
| 649 | rejectCallback(callbackId, error: "Secure storage is disabled", code: "CAPABILITY_DISABLED") | |
| 631 | 650 | } |
| 632 | 651 | case "secureClear": |
| 633 | 652 | if config.enableSecureStorage { |
| @@ -690,10 +709,16 @@ struct CraftWebView: UIViewRepresentable { | ||
| 690 | 709 | readLocationRecording(callbackId: callbackId) |
| 691 | 710 | // Clipboard |
| 692 | 711 | case "clipboardWrite": |
| 693 | if config.enableClipboard, let text = body["text"] as? String { | |
| 694 | UIPasteboard.general.string = text | |
| 695 | resolveCallback(callbackId, result: true) | |
| 696 | } | |
| 712 | if config.enableClipboard { | |
| 713 | if let text = body["text"] as? String { | |
| 714 | UIPasteboard.general.string = text | |
| 715 | resolveCallback(callbackId, result: true) | |
| 716 | } else { | |
| 717 | rejectCallback(callbackId, error: "clipboardWrite was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 718 | } | |
| 719 | } else { | |
| 720 | rejectCallback(callbackId, error: "Clipboard is disabled", code: "CAPABILITY_DISABLED") | |
| 721 | } | |
| 697 | 722 | case "clipboardRead": |
| 698 | 723 | if config.enableClipboard { |
| 699 | 724 | let text = UIPasteboard.general.string ?? "" |
| @@ -751,11 +776,15 @@ struct CraftWebView: UIViewRepresentable { | ||
| 751 | 776 | rejectCallback(callbackId, error: "Contacts access is disabled", code: "CAPABILITY_DISABLED") |
| 752 | 777 | } |
| 753 | 778 | case "addContact": |
| 754 | if config.enableContacts, | |
| 755 | let contactData = body["contact"] as? [String: Any] { | |
| 756 | addContact(contactData, callbackId: callbackId) | |
| 779 | if config.enableContacts { | |
| 780 | if let contactData = body["contact"] as? [String: Any] { | |
| 781 | addContact(contactData, callbackId: callbackId) | |
| 782 | } else { | |
| 783 | rejectCallback(callbackId, error: "addContact was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 784 | } | |
| 785 | } else { | |
| 786 | rejectCallback(callbackId, error: "Contacts access is disabled", code: "CAPABILITY_DISABLED") | |
| 757 | 787 | } |
| 758 | ||
| 759 | 788 | // MARK: - Calendar |
| 760 | 789 | case "getCalendarEvents": |
| 761 | 790 | if config.enableCalendar { |
| @@ -766,26 +795,45 @@ struct CraftWebView: UIViewRepresentable { | ||
| 766 | 795 | rejectCallback(callbackId, error: "Calendar access is disabled", code: "CAPABILITY_DISABLED") |
| 767 | 796 | } |
| 768 | 797 | case "createCalendarEvent": |
| 769 | if config.enableCalendar, | |
| 770 | let eventData = body["event"] as? [String: Any] { | |
| 771 | createCalendarEvent(eventData, callbackId: callbackId) | |
| 798 | if config.enableCalendar { | |
| 799 | if let eventData = body["event"] as? [String: Any] { | |
| 800 | createCalendarEvent(eventData, callbackId: callbackId) | |
| 801 | } else { | |
| 802 | rejectCallback(callbackId, error: "createCalendarEvent was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 803 | } | |
| 804 | } else { | |
| 805 | rejectCallback(callbackId, error: "Calendar access is disabled", code: "CAPABILITY_DISABLED") | |
| 772 | 806 | } |
| 773 | 807 | case "deleteCalendarEvent": |
| 774 | if config.enableCalendar, | |
| 775 | let eventId = body["eventId"] as? String { | |
| 776 | deleteCalendarEvent(eventId, callbackId: callbackId) | |
| 808 | if config.enableCalendar { | |
| 809 | if let eventId = body["eventId"] as? String { | |
| 810 | deleteCalendarEvent(eventId, callbackId: callbackId) | |
| 811 | } else { | |
| 812 | rejectCallback(callbackId, error: "deleteCalendarEvent was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 813 | } | |
| 814 | } else { | |
| 815 | rejectCallback(callbackId, error: "Calendar access is disabled", code: "CAPABILITY_DISABLED") | |
| 777 | 816 | } |
| 778 | ||
| 779 | 817 | // MARK: - Local Notifications |
| 780 | 818 | case "scheduleNotification": |
| 781 | if config.enableLocalNotifications, | |
| 782 | let notifData = body["notification"] as? [String: Any] { | |
| 783 | scheduleLocalNotification(notifData, callbackId: callbackId) | |
| 819 | if config.enableLocalNotifications { | |
| 820 | if let notifData = body["notification"] as? [String: Any] { | |
| 821 | scheduleLocalNotification(notifData, callbackId: callbackId) | |
| 822 | } else { | |
| 823 | rejectCallback(callbackId, error: "scheduleNotification was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 824 | } | |
| 825 | } else { | |
| 826 | rejectCallback(callbackId, error: "Local notifications are disabled", code: "CAPABILITY_DISABLED") | |
| 784 | 827 | } |
| 785 | 828 | case "cancelNotification": |
| 786 | if config.enableLocalNotifications, | |
| 787 | let notifId = body["id"] as? String { | |
| 788 | cancelLocalNotification(notifId, callbackId: callbackId) | |
| 829 | if config.enableLocalNotifications { | |
| 830 | if let notifId = body["id"] as? String { | |
| 831 | cancelLocalNotification(notifId, callbackId: callbackId) | |
| 832 | } else { | |
| 833 | rejectCallback(callbackId, error: "cancelNotification was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 834 | } | |
| 835 | } else { | |
| 836 | rejectCallback(callbackId, error: "Local notifications are disabled", code: "CAPABILITY_DISABLED") | |
| 789 | 837 | } |
| 790 | 838 | case "cancelAllNotifications": |
| 791 | 839 | if config.enableLocalNotifications { |
| @@ -827,14 +875,24 @@ struct CraftWebView: UIViewRepresentable { | ||
| 827 | 875 | |
| 828 | 876 | // MARK: - In-App Purchase |
| 829 | 877 | case "getProducts": |
| 830 | if config.enableInAppPurchase, | |
| 831 | let productIds = body["productIds"] as? [String] { | |
| 832 | getProducts(productIds, callbackId: callbackId) | |
| 878 | if config.enableInAppPurchase { | |
| 879 | if let productIds = body["productIds"] as? [String] { | |
| 880 | getProducts(productIds, callbackId: callbackId) | |
| 881 | } else { | |
| 882 | rejectCallback(callbackId, error: "getProducts was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 883 | } | |
| 884 | } else { | |
| 885 | rejectCallback(callbackId, error: "In-app purchase is disabled", code: "CAPABILITY_DISABLED") | |
| 833 | 886 | } |
| 834 | 887 | case "purchase": |
| 835 | if config.enableInAppPurchase, | |
| 836 | let productId = body["productId"] as? String { | |
| 837 | purchaseProduct(productId, callbackId: callbackId) | |
| 888 | if config.enableInAppPurchase { | |
| 889 | if let productId = body["productId"] as? String { | |
| 890 | purchaseProduct(productId, callbackId: callbackId) | |
| 891 | } else { | |
| 892 | rejectCallback(callbackId, error: "purchase was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 893 | } | |
| 894 | } else { | |
| 895 | rejectCallback(callbackId, error: "In-app purchase is disabled", code: "CAPABILITY_DISABLED") | |
| 838 | 896 | } |
| 839 | 897 | case "restorePurchases": |
| 840 | 898 | if config.enableInAppPurchase { |
| @@ -845,16 +903,25 @@ struct CraftWebView: UIViewRepresentable { | ||
| 845 | 903 | |
| 846 | 904 | // MARK: - Keep Awake |
| 847 | 905 | case "setKeepAwake": |
| 848 | if config.enableKeepAwake, | |
| 849 | let enabled = body["enabled"] as? Bool { | |
| 850 | setKeepAwake(enabled, callbackId: callbackId) | |
| 906 | if config.enableKeepAwake { | |
| 907 | if let enabled = body["enabled"] as? Bool { | |
| 908 | setKeepAwake(enabled, callbackId: callbackId) | |
| 909 | } else { | |
| 910 | rejectCallback(callbackId, error: "setKeepAwake was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 911 | } | |
| 912 | } else { | |
| 913 | rejectCallback(callbackId, error: "Keep awake is disabled", code: "CAPABILITY_DISABLED") | |
| 851 | 914 | } |
| 852 | ||
| 853 | 915 | // MARK: - Orientation Lock |
| 854 | 916 | case "lockOrientation": |
| 855 | if config.enableOrientationLock, | |
| 856 | let orientation = body["orientation"] as? String { | |
| 857 | lockOrientation(orientation, callbackId: callbackId) | |
| 917 | if config.enableOrientationLock { | |
| 918 | if let orientation = body["orientation"] as? String { | |
| 919 | lockOrientation(orientation, callbackId: callbackId) | |
| 920 | } else { | |
| 921 | rejectCallback(callbackId, error: "lockOrientation was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 922 | } | |
| 923 | } else { | |
| 924 | rejectCallback(callbackId, error: "Orientation lock is disabled", code: "CAPABILITY_DISABLED") | |
| 858 | 925 | } |
| 859 | 926 | case "unlockOrientation": |
| 860 | 927 | if config.enableOrientationLock { |
| @@ -882,18 +949,25 @@ struct CraftWebView: UIViewRepresentable { | ||
| 882 | 949 | |
| 883 | 950 | // MARK: - File Download |
| 884 | 951 | case "downloadFile": |
| 885 | if config.enableFileDownload, | |
| 886 | let url = body["url"] as? String, | |
| 887 | let filename = body["filename"] as? String { | |
| 888 | downloadFile(url: url, filename: filename, callbackId: callbackId) | |
| 889 | } | |
| 952 | if config.enableFileDownload { | |
| 953 | if let url = body["url"] as? String, let filename = body["filename"] as? String { | |
| 954 | downloadFile(url: url, filename: filename, callbackId: callbackId) | |
| 955 | } else { | |
| 956 | rejectCallback(callbackId, error: "downloadFile was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 957 | } | |
| 958 | } else { | |
| 959 | rejectCallback(callbackId, error: "File download is disabled", code: "CAPABILITY_DISABLED") | |
| 960 | } | |
| 890 | 961 | case "saveFile": |
| 891 | if config.enableFileDownload, | |
| 892 | let data = body["data"] as? String, | |
| 893 | let filename = body["filename"] as? String { | |
| 894 | saveFile(data: data, filename: filename, callbackId: callbackId) | |
| 895 | } | |
| 896 | ||
| 962 | if config.enableFileDownload { | |
| 963 | if let data = body["data"] as? String, let filename = body["filename"] as? String { | |
| 964 | saveFile(data: data, filename: filename, callbackId: callbackId) | |
| 965 | } else { | |
| 966 | rejectCallback(callbackId, error: "saveFile was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 967 | } | |
| 968 | } else { | |
| 969 | rejectCallback(callbackId, error: "File download is disabled", code: "CAPABILITY_DISABLED") | |
| 970 | } | |
| 897 | 971 | // MARK: - Social Auth |
| 898 | 972 | case "signInWithApple": |
| 899 | 973 | if config.enableSocialAuth { |
| @@ -938,18 +1012,27 @@ struct CraftWebView: UIViewRepresentable { | ||
| 938 | 1012 | |
| 939 | 1013 | // MARK: - Local Database |
| 940 | 1014 | case "dbExecute": |
| 941 | if config.enableLocalDatabase, | |
| 942 | let sql = body["sql"] as? String { | |
| 943 | let params = body["params"] as? [Any] | |
| 944 | dbExecute(sql: sql, params: params, callbackId: callbackId) | |
| 1015 | if config.enableLocalDatabase { | |
| 1016 | if let sql = body["sql"] as? String { | |
| 1017 | let params = body["params"] as? [Any] | |
| 1018 | dbExecute(sql: sql, params: params, callbackId: callbackId) | |
| 1019 | } else { | |
| 1020 | rejectCallback(callbackId, error: "dbExecute was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1021 | } | |
| 1022 | } else { | |
| 1023 | rejectCallback(callbackId, error: "Local database is disabled", code: "CAPABILITY_DISABLED") | |
| 945 | 1024 | } |
| 946 | 1025 | case "dbQuery": |
| 947 | if config.enableLocalDatabase, | |
| 948 | let sql = body["sql"] as? String { | |
| 949 | let params = body["params"] as? [Any] | |
| 950 | dbQuery(sql: sql, params: params, callbackId: callbackId) | |
| 1026 | if config.enableLocalDatabase { | |
| 1027 | if let sql = body["sql"] as? String { | |
| 1028 | let params = body["params"] as? [Any] | |
| 1029 | dbQuery(sql: sql, params: params, callbackId: callbackId) | |
| 1030 | } else { | |
| 1031 | rejectCallback(callbackId, error: "dbQuery was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1032 | } | |
| 1033 | } else { | |
| 1034 | rejectCallback(callbackId, error: "Local database is disabled", code: "CAPABILITY_DISABLED") | |
| 951 | 1035 | } |
| 952 | ||
| 953 | 1036 | // MARK: - Bluetooth |
| 954 | 1037 | case "startBluetoothScan": |
| 955 | 1038 | if config.enableBluetooth { |
| @@ -978,11 +1061,16 @@ struct CraftWebView: UIViewRepresentable { | ||
| 978 | 1061 | rejectCallback(callbackId, error: "HealthKit is disabled", code: "CAPABILITY_DISABLED") |
| 979 | 1062 | } |
| 980 | 1063 | case "getHealthData": |
| 981 | if config.enableHealthKit, | |
| 982 | let dataType = body["type"] as? String { | |
| 983 | let startDate = body["startDate"] as? Double | |
| 984 | let endDate = body["endDate"] as? Double | |
| 985 | getHealthData(type: dataType, startDate: startDate, endDate: endDate, callbackId: callbackId) | |
| 1064 | if config.enableHealthKit { | |
| 1065 | if let dataType = body["type"] as? String { | |
| 1066 | let startDate = body["startDate"] as? Double | |
| 1067 | let endDate = body["endDate"] as? Double | |
| 1068 | getHealthData(type: dataType, startDate: startDate, endDate: endDate, callbackId: callbackId) | |
| 1069 | } else { | |
| 1070 | rejectCallback(callbackId, error: "getHealthData was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1071 | } | |
| 1072 | } else { | |
| 1073 | rejectCallback(callbackId, error: "HealthKit is disabled", code: "CAPABILITY_DISABLED") | |
| 986 | 1074 | } |
| 987 | 1075 | case "saveHealthWorkout": |
| 988 | 1076 | if config.enableHealthKit { |
| @@ -1013,22 +1101,37 @@ struct CraftWebView: UIViewRepresentable { | ||
| 1013 | 1101 | |
| 1014 | 1102 | // MARK: - Background Tasks |
| 1015 | 1103 | case "registerBackgroundTask": |
| 1016 | if config.enableBackgroundTasks, | |
| 1017 | let taskId = body["taskId"] as? String { | |
| 1018 | registerBackgroundTask(taskId: taskId, callbackId: callbackId) | |
| 1104 | if config.enableBackgroundTasks { | |
| 1105 | if let taskId = body["taskId"] as? String { | |
| 1106 | registerBackgroundTask(taskId: taskId, callbackId: callbackId) | |
| 1107 | } else { | |
| 1108 | rejectCallback(callbackId, error: "registerBackgroundTask was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1109 | } | |
| 1110 | } else { | |
| 1111 | rejectCallback(callbackId, error: "Background tasks are disabled", code: "CAPABILITY_DISABLED") | |
| 1019 | 1112 | } |
| 1020 | 1113 | case "scheduleBackgroundTask": |
| 1021 | if config.enableBackgroundTasks, | |
| 1022 | let taskId = body["taskId"] as? String { | |
| 1023 | let delay = body["delay"] as? Double ?? 900 // 15 minutes default | |
| 1024 | let requiresNetwork = body["requiresNetwork"] as? Bool ?? false | |
| 1025 | let requiresCharging = body["requiresCharging"] as? Bool ?? false | |
| 1026 | scheduleBackgroundTask(taskId: taskId, delay: delay, requiresNetwork: requiresNetwork, requiresCharging: requiresCharging, callbackId: callbackId) | |
| 1114 | if config.enableBackgroundTasks { | |
| 1115 | if let taskId = body["taskId"] as? String { | |
| 1116 | let delay = body["delay"] as? Double ?? 900 // 15 minutes default | |
| 1117 | let requiresNetwork = body["requiresNetwork"] as? Bool ?? false | |
| 1118 | let requiresCharging = body["requiresCharging"] as? Bool ?? false | |
| 1119 | scheduleBackgroundTask(taskId: taskId, delay: delay, requiresNetwork: requiresNetwork, requiresCharging: requiresCharging, callbackId: callbackId) | |
| 1120 | } else { | |
| 1121 | rejectCallback(callbackId, error: "scheduleBackgroundTask was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1122 | } | |
| 1123 | } else { | |
| 1124 | rejectCallback(callbackId, error: "Background tasks are disabled", code: "CAPABILITY_DISABLED") | |
| 1027 | 1125 | } |
| 1028 | 1126 | case "cancelBackgroundTask": |
| 1029 | if config.enableBackgroundTasks, | |
| 1030 | let taskId = body["taskId"] as? String { | |
| 1031 | cancelBackgroundTask(taskId: taskId, callbackId: callbackId) | |
| 1127 | if config.enableBackgroundTasks { | |
| 1128 | if let taskId = body["taskId"] as? String { | |
| 1129 | cancelBackgroundTask(taskId: taskId, callbackId: callbackId) | |
| 1130 | } else { | |
| 1131 | rejectCallback(callbackId, error: "cancelBackgroundTask was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1132 | } | |
| 1133 | } else { | |
| 1134 | rejectCallback(callbackId, error: "Background tasks are disabled", code: "CAPABILITY_DISABLED") | |
| 1032 | 1135 | } |
| 1033 | 1136 | case "cancelAllBackgroundTasks": |
| 1034 | 1137 | if config.enableBackgroundTasks { |
| @@ -1039,10 +1142,15 @@ struct CraftWebView: UIViewRepresentable { | ||
| 1039 | 1142 | |
| 1040 | 1143 | // MARK: - PDF Viewer |
| 1041 | 1144 | case "openPDF": |
| 1042 | if config.enablePDFViewer, | |
| 1043 | let source = body["source"] as? String { | |
| 1044 | let page = body["page"] as? Int ?? 0 | |
| 1045 | openPDF(source: source, page: page, callbackId: callbackId) | |
| 1145 | if config.enablePDFViewer { | |
| 1146 | if let source = body["source"] as? String { | |
| 1147 | let page = body["page"] as? Int ?? 0 | |
| 1148 | openPDF(source: source, page: page, callbackId: callbackId) | |
| 1149 | } else { | |
| 1150 | rejectCallback(callbackId, error: "openPDF was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1151 | } | |
| 1152 | } else { | |
| 1153 | rejectCallback(callbackId, error: "PDF viewing is disabled", code: "CAPABILITY_DISABLED") | |
| 1046 | 1154 | } |
| 1047 | 1155 | case "closePDF": |
| 1048 | 1156 | closePDF(callbackId: callbackId) |
| @@ -1108,15 +1216,25 @@ struct CraftWebView: UIViewRepresentable { | ||
| 1108 | 1216 | rejectCallback(callbackId, error: "AR is disabled", code: "CAPABILITY_DISABLED") |
| 1109 | 1217 | } |
| 1110 | 1218 | case "placeARObject": |
| 1111 | if config.enableAR, | |
| 1112 | let model = body["model"] as? String { | |
| 1113 | let position = body["position"] as? [String: Double] | |
| 1114 | placeARObject(model: model, position: position, callbackId: callbackId) | |
| 1219 | if config.enableAR { | |
| 1220 | if let model = body["model"] as? String { | |
| 1221 | let position = body["position"] as? [String: Double] | |
| 1222 | placeARObject(model: model, position: position, callbackId: callbackId) | |
| 1223 | } else { | |
| 1224 | rejectCallback(callbackId, error: "placeARObject was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1225 | } | |
| 1226 | } else { | |
| 1227 | rejectCallback(callbackId, error: "AR is disabled", code: "CAPABILITY_DISABLED") | |
| 1115 | 1228 | } |
| 1116 | 1229 | case "removeARObject": |
| 1117 | if config.enableAR, | |
| 1118 | let objectId = body["objectId"] as? String { | |
| 1119 | removeARObject(objectId: objectId, callbackId: callbackId) | |
| 1230 | if config.enableAR { | |
| 1231 | if let objectId = body["objectId"] as? String { | |
| 1232 | removeARObject(objectId: objectId, callbackId: callbackId) | |
| 1233 | } else { | |
| 1234 | rejectCallback(callbackId, error: "removeARObject was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1235 | } | |
| 1236 | } else { | |
| 1237 | rejectCallback(callbackId, error: "AR is disabled", code: "CAPABILITY_DISABLED") | |
| 1120 | 1238 | } |
| 1121 | 1239 | case "getARPlanes": |
| 1122 | 1240 | if config.enableAR { |
| @@ -1127,21 +1245,35 @@ struct CraftWebView: UIViewRepresentable { | ||
| 1127 | 1245 | |
| 1128 | 1246 | // MARK: - ML (Core ML / Vision) |
| 1129 | 1247 | case "classifyImage": |
| 1130 | if config.enableMLKit, | |
| 1131 | let imageBase64 = body["image"] as? String { | |
| 1132 | classifyImage(imageBase64: imageBase64, callbackId: callbackId) | |
| 1248 | if config.enableMLKit { | |
| 1249 | if let imageBase64 = body["image"] as? String { | |
| 1250 | classifyImage(imageBase64: imageBase64, callbackId: callbackId) | |
| 1251 | } else { | |
| 1252 | rejectCallback(callbackId, error: "classifyImage was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1253 | } | |
| 1254 | } else { | |
| 1255 | rejectCallback(callbackId, error: "Vision is disabled", code: "CAPABILITY_DISABLED") | |
| 1133 | 1256 | } |
| 1134 | 1257 | case "detectObjects": |
| 1135 | if config.enableMLKit, | |
| 1136 | let imageBase64 = body["image"] as? String { | |
| 1137 | detectObjects(imageBase64: imageBase64, callbackId: callbackId) | |
| 1258 | if config.enableMLKit { | |
| 1259 | if let imageBase64 = body["image"] as? String { | |
| 1260 | detectObjects(imageBase64: imageBase64, callbackId: callbackId) | |
| 1261 | } else { | |
| 1262 | rejectCallback(callbackId, error: "detectObjects was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1263 | } | |
| 1264 | } else { | |
| 1265 | rejectCallback(callbackId, error: "Vision is disabled", code: "CAPABILITY_DISABLED") | |
| 1138 | 1266 | } |
| 1139 | 1267 | case "recognizeText": |
| 1140 | if config.enableMLKit, | |
| 1141 | let imageBase64 = body["image"] as? String { | |
| 1142 | recognizeText(imageBase64: imageBase64, callbackId: callbackId) | |
| 1268 | if config.enableMLKit { | |
| 1269 | if let imageBase64 = body["image"] as? String { | |
| 1270 | recognizeText(imageBase64: imageBase64, callbackId: callbackId) | |
| 1271 | } else { | |
| 1272 | rejectCallback(callbackId, error: "recognizeText was called without the values it needs", code: "INVALID_ARGUMENT") | |
| 1273 | } | |
| 1274 | } else { | |
| 1275 | rejectCallback(callbackId, error: "Vision is disabled", code: "CAPABILITY_DISABLED") | |
| 1143 | 1276 | } |
| 1144 | ||
| 1145 | 1277 | // MARK: - Widget |
| 1146 | 1278 | case "updateWidget": |
| 1147 | 1279 | if let data = body["data"] as? [String: Any] { |