Ver código fonte

feat: support building on iOS platform (#3033)

Lucas.Xu 1 ano atrás
pai
commit
d32c2082b6

+ 12 - 35
frontend/.vscode/launch.json

@@ -44,46 +44,33 @@
             "cwd": "${workspaceRoot}/appflowy_flutter"
         },
         {
-            "name": "AF-desktop: Debug Rust",
-            "request": "attach",
-            "type": "lldb",
-            "pid": "${command:pickMyProcess}"
-        },
-        // {
-        //     "name": "AF-desktop: profile mode",
-        //     "request": "launch",
-        //     "program": "./lib/main.dart",
-        //     "type": "dart",
-        //     "flutterMode": "profile",
-        //     "cwd": "${workspaceRoot}/appflowy_flutter"
-        // },
-        {
-            // This task builds the Rust and Dart code of AppFlowy for android.
-            "name": "AF-android: Build All",
+            "name": "AF-iOS: Clean + Rebuild All",
             "request": "launch",
             "program": "./lib/main.dart",
             "type": "dart",
-            "preLaunchTask": "AF: build_mobile_sdk",
+            "preLaunchTask": "AF: Clean + Rebuild All (iOS)",
             "env": {
-                "RUST_LOG": "info"
+                "RUST_LOG": "trace"
             },
             "cwd": "${workspaceRoot}/appflowy_flutter"
         },
         {
-            // This task builds will:
-            // - call the clean task,
-            // - rebuild all the generated Files (including freeze and language files)
-            // - rebuild the the Rust and Dart code of AppFlowy.
-            "name": "AF-android: Clean + Rebuild All",
+            "name": "AF-iOS-Simulator: Clean + Rebuild All",
             "request": "launch",
             "program": "./lib/main.dart",
             "type": "dart",
-            "preLaunchTask": "AF: Clean + Rebuild All (Android)",
+            "preLaunchTask": "AF: Clean + Rebuild All (iOS Simulator)",
             "env": {
-                "RUST_LOG": "info"
+                "RUST_LOG": "trace"
             },
             "cwd": "${workspaceRoot}/appflowy_flutter"
         },
+        {
+            "name": "AF-desktop: Debug Rust",
+            "request": "attach",
+            "type": "lldb",
+            "pid": "${command:pickMyProcess}"
+        },
         {
             // https://tauri.app/v1/guides/debugging/vs-code
             "type": "lldb",
@@ -99,15 +86,5 @@
             "preLaunchTask": "AF: Tauri UI Dev",
             "cwd": "${workspaceRoot}/appflowy_tauri/"
         },
-        // {
-        //     "type": "lldb",
-        //     "request": "launch",
-        //     "name": "AF-tauri: Production Debug",
-        //     "cargo": {
-        //         "args": ["build", "--release", "--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml"]
-        //     },
-        //     "preLaunchTask": "AF: Tauri UI Build",
-        //     "cwd": "${workspaceRoot}/appflowy_tauri/"
-        // },
     ]
 }

+ 31 - 4
frontend/.vscode/tasks.json

@@ -28,13 +28,13 @@
       }
     },
     {
-      "label": "AF: Clean + Rebuild All (Android)",
+      "label": "AF: Clean + Rebuild All (iOS)",
       "type": "shell",
       "dependsOrder": "sequence",
       "dependsOn": [
         "AF: Dart Clean",
         "AF: Flutter Clean",
-        "AF: Build Appflowy Core_for_android",
+        "AF: Build Appflowy Core For iOS",
         "AF: Flutter Pub Get",
         "AF: Flutter Package Get",
         "AF: Generate Language Files",
@@ -46,9 +46,36 @@
       }
     },
     {
-      "label": "AF: Build Appflowy Core_for_android",
+      "label": "AF: Clean + Rebuild All (iOS Simulator)",
       "type": "shell",
-      "command": "cargo make --profile development-android appflowy-core-dev-android",
+      "dependsOrder": "sequence",
+      "dependsOn": [
+        "AF: Dart Clean",
+        "AF: Flutter Clean",
+        "AF: Build Appflowy Core For iOS Simulator",
+        "AF: Flutter Pub Get",
+        "AF: Flutter Package Get",
+        "AF: Generate Language Files",
+        "AF: Generate Freezed Files"
+      ],
+      "presentation": {
+        "reveal": "always",
+        "panel": "new"
+      }
+    },
+    {
+      "label": "AF: Build Appflowy Core For iOS",
+      "type": "shell",
+      "command": "cargo make --profile development-ios-arm64 appflowy-core-dev-ios",
+      "group": "build",
+      "options": {
+        "cwd": "${workspaceFolder}"
+      }
+    },
+    {
+      "label": "AF: Build Appflowy Core For iOS Simulator",
+      "type": "shell",
+      "command": "cargo make --profile development-ios-arm64-sim appflowy-core-dev-ios",
       "group": "build",
       "options": {
         "cwd": "${workspaceFolder}"

+ 17 - 6
frontend/Makefile.toml

@@ -2,6 +2,7 @@
 
 extend = [
   { path = "scripts/makefile/desktop.toml" },
+  { path = "scripts/makefile/mobile.toml" },
   { path = "scripts/makefile/protobuf.toml" },
   { path = "scripts/makefile/tests.toml" },
   { path = "scripts/makefile/docker.toml" },
@@ -150,6 +151,22 @@ LINUX_ARCH = "arm64"
 APP_ENVIRONMENT = "production"
 FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
 
+[env.development-ios-arm64-sim]
+BUILD_FLAG = "debug"
+TARGET_OS = "ios"
+FLUTTER_OUTPUT_DIR = "Debug"
+RUST_COMPILE_TARGET = "aarch64-apple-ios-sim"
+BUILD_ARCHS = "arm64"
+CRATE_TYPE = "staticlib"
+
+[env.development-ios-arm64]
+BUILD_FLAG = "debug"
+TARGET_OS = "ios"
+FLUTTER_OUTPUT_DIR = "Debug"
+RUST_COMPILE_TARGET = "aarch64-apple-ios"
+BUILD_ARCHS = "arm64"
+CRATE_TYPE = "staticlib"
+
 [tasks.echo_env]
 script = ['''
     echo "-------- Env Parameters --------"
@@ -165,12 +182,6 @@ script = ['''
     ''']
 script_runner = "@shell"
 
-[env.production-ios]
-BUILD_FLAG = "release"
-TARGET_OS = "ios"
-FLUTTER_OUTPUT_DIR = "Release"
-PRODUCT_EXT = "ipa"
-
 [env.development-android]
 BUILD_FLAG = "debug"
 TARGET_OS = "android"

+ 0 - 7
frontend/appflowy_flutter/integration_test/util/mock/mock_file_picker.dart

@@ -77,13 +77,6 @@ Future<String> mockSaveFilePath(
 Future<List<String>> mockPickFilePaths({
   required List<String> paths,
 }) async {
-  // late final Directory dir;
-  // if (customPath != null) {
-  //   dir = Directory(customPath);
-  // } else {
-  //   dir = await TestFolder.testLocation(applicationDataPath, name);
-  // }
-  // final paths = fileNames.map((e) => p.join(dir.path, e)).toList();
   getIt.unregister<FilePickerService>();
   getIt.registerFactory<FilePickerService>(
     () => MockFilePicker(

+ 1 - 1
frontend/appflowy_flutter/ios/Flutter/AppFrameworkInfo.plist

@@ -21,6 +21,6 @@
   <key>CFBundleVersion</key>
   <string>1.0</string>
   <key>MinimumOSVersion</key>
-  <string>9.0</string>
+  <string>11.0</string>
 </dict>
 </plist>

+ 1 - 1
frontend/appflowy_flutter/ios/Podfile

@@ -1,5 +1,5 @@
 # Uncomment this line to define a global platform for your project
-# platform :ios, '9.0'
+# platform :ios, '11.0'
 
 # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
 ENV['COCOAPODS_DISABLE_STATS'] = 'true'

+ 134 - 52
frontend/appflowy_flutter/ios/Podfile.lock

@@ -1,81 +1,163 @@
 PODS:
-  - flowy_editor (0.0.1):
-    - Flutter
-  - flowy_infra_ui (0.0.1):
+  - app_links (0.0.1):
     - Flutter
   - appflowy_backend (0.0.1):
     - Flutter
+  - connectivity_plus (0.0.1):
+    - Flutter
+    - ReachabilitySwift
+  - device_info_plus (0.0.1):
+    - Flutter
+  - DKImagePickerController/Core (4.3.4):
+    - DKImagePickerController/ImageDataManager
+    - DKImagePickerController/Resource
+  - DKImagePickerController/ImageDataManager (4.3.4)
+  - DKImagePickerController/PhotoGallery (4.3.4):
+    - DKImagePickerController/Core
+    - DKPhotoGallery
+  - DKImagePickerController/Resource (4.3.4)
+  - DKPhotoGallery (0.0.17):
+    - DKPhotoGallery/Core (= 0.0.17)
+    - DKPhotoGallery/Model (= 0.0.17)
+    - DKPhotoGallery/Preview (= 0.0.17)
+    - DKPhotoGallery/Resource (= 0.0.17)
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Core (0.0.17):
+    - DKPhotoGallery/Model
+    - DKPhotoGallery/Preview
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Model (0.0.17):
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Preview (0.0.17):
+    - DKPhotoGallery/Model
+    - DKPhotoGallery/Resource
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Resource (0.0.17):
+    - SDWebImage
+    - SwiftyGif
+  - file_picker (0.0.1):
+    - DKImagePickerController/PhotoGallery
+    - Flutter
+  - flowy_infra_ui (0.0.1):
+    - Flutter
   - Flutter (1.0.0)
-  - flutter_inappwebview (0.0.1):
+  - fluttertoast (0.0.2):
+    - Flutter
+    - Toast
+  - integration_test (0.0.1):
     - Flutter
-    - flutter_inappwebview/Core (= 0.0.1)
-    - OrderedSet (~> 5.0)
-  - flutter_inappwebview/Core (0.0.1):
+  - package_info_plus (0.4.5):
     - Flutter
-    - OrderedSet (~> 5.0)
-  - flutter_keyboard_visibility (0.0.1):
+  - path_provider_foundation (0.0.1):
     - Flutter
-  - image_picker (0.0.1):
+    - FlutterMacOS
+  - ReachabilitySwift (5.0.0)
+  - rich_clipboard_ios (0.0.1):
     - Flutter
-  - OrderedSet (5.0.0)
-  - path_provider (0.0.1):
+  - SDWebImage (5.14.2):
+    - SDWebImage/Core (= 5.14.2)
+  - SDWebImage/Core (5.14.2)
+  - shared_preferences_foundation (0.0.1):
     - Flutter
-  - url_launcher (0.0.1):
+    - FlutterMacOS
+  - sign_in_with_apple (0.0.1):
     - Flutter
-  - video_player (0.0.1):
+  - SwiftyGif (5.4.3)
+  - Toast (4.0.0)
+  - url_launcher_ios (0.0.1):
+    - Flutter
+  - webview_flutter_wkwebview (0.0.1):
     - Flutter
 
 DEPENDENCIES:
-  - flowy_editor (from `.symlinks/plugins/flowy_editor/ios`)
-  - flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
+  - app_links (from `.symlinks/plugins/app_links/ios`)
   - appflowy_backend (from `.symlinks/plugins/appflowy_backend/ios`)
+  - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
+  - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
+  - file_picker (from `.symlinks/plugins/file_picker/ios`)
+  - flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
   - Flutter (from `Flutter`)
-  - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
-  - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
-  - image_picker (from `.symlinks/plugins/image_picker/ios`)
-  - path_provider (from `.symlinks/plugins/path_provider/ios`)
-  - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
-  - video_player (from `.symlinks/plugins/video_player/ios`)
+  - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
+  - integration_test (from `.symlinks/plugins/integration_test/ios`)
+  - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
+  - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
+  - rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
+  - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+  - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
+  - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
+  - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
 
 SPEC REPOS:
   trunk:
-    - OrderedSet
+    - DKImagePickerController
+    - DKPhotoGallery
+    - ReachabilitySwift
+    - SDWebImage
+    - SwiftyGif
+    - Toast
 
 EXTERNAL SOURCES:
-  flowy_editor:
-    :path: ".symlinks/plugins/flowy_editor/ios"
-  flowy_infra_ui:
-    :path: ".symlinks/plugins/flowy_infra_ui/ios"
+  app_links:
+    :path: ".symlinks/plugins/app_links/ios"
   appflowy_backend:
     :path: ".symlinks/plugins/appflowy_backend/ios"
+  connectivity_plus:
+    :path: ".symlinks/plugins/connectivity_plus/ios"
+  device_info_plus:
+    :path: ".symlinks/plugins/device_info_plus/ios"
+  file_picker:
+    :path: ".symlinks/plugins/file_picker/ios"
+  flowy_infra_ui:
+    :path: ".symlinks/plugins/flowy_infra_ui/ios"
   Flutter:
     :path: Flutter
-  flutter_inappwebview:
-    :path: ".symlinks/plugins/flutter_inappwebview/ios"
-  flutter_keyboard_visibility:
-    :path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
-  image_picker:
-    :path: ".symlinks/plugins/image_picker/ios"
-  path_provider:
-    :path: ".symlinks/plugins/path_provider/ios"
-  url_launcher:
-    :path: ".symlinks/plugins/url_launcher/ios"
-  video_player:
-    :path: ".symlinks/plugins/video_player/ios"
+  fluttertoast:
+    :path: ".symlinks/plugins/fluttertoast/ios"
+  integration_test:
+    :path: ".symlinks/plugins/integration_test/ios"
+  package_info_plus:
+    :path: ".symlinks/plugins/package_info_plus/ios"
+  path_provider_foundation:
+    :path: ".symlinks/plugins/path_provider_foundation/darwin"
+  rich_clipboard_ios:
+    :path: ".symlinks/plugins/rich_clipboard_ios/ios"
+  shared_preferences_foundation:
+    :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+  sign_in_with_apple:
+    :path: ".symlinks/plugins/sign_in_with_apple/ios"
+  url_launcher_ios:
+    :path: ".symlinks/plugins/url_launcher_ios/ios"
+  webview_flutter_wkwebview:
+    :path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
 
 SPEC CHECKSUMS:
-  flowy_editor: bf8d58894ddb03453bd4d8521c57267ad638b837
-  flowy_infra_ui: 146c88346fd55d2ee6a41ae35059a5bf095cfbb3
-  appflowy_backend: c416222c639e678828776789bf0c1a1d0d59df3c
-  Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
-  flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
-  flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
-  image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6
-  OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
-  path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
-  url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
-  video_player: ecd305f42e9044793efd34846e1ce64c31ea6fcb
+  app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
+  appflowy_backend: 652da8c7a089ffe165cb119a68eb168a6ed2c9d0
+  connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
+  device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
+  DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
+  DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
+  file_picker: ce3938a0df3cc1ef404671531facef740d03f920
+  flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
+  Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
+  fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
+  integration_test: 13825b8a9334a850581300559b8839134b124670
+  package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
+  path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
+  ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
+  rich_clipboard_ios: 7588abe18f881a6d0e9ec0b12e51cae2761e8942
+  SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
+  shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
+  sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
+  SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
+  Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
+  url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
+  webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
 
-PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
 
-COCOAPODS: 1.10.1
+COCOAPODS: 1.11.3

+ 7 - 4
frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj

@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 50;
+	objectVersion = 54;
 	objects = {
 
 /* Begin PBXBuildFile section */
@@ -217,10 +217,12 @@
 		};
 		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
 			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
+				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
 			);
 			name = "Thin Binary";
 			outputPaths = (
@@ -231,6 +233,7 @@
 		};
 		9740EEB61CF901F6004384FC /* Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
 			buildActionMask = 2147483647;
 			files = (
 			);
@@ -340,7 +343,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
@@ -414,7 +417,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
@@ -463,7 +466,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;

+ 2 - 0
frontend/appflowy_flutter/ios/Runner/Info.plist

@@ -47,5 +47,7 @@
 	</array>
 	<key>CADisableMinimumFrameDurationOnPhone</key>
 	<true/>
+	<key>UIApplicationSupportsIndirectInputEvents</key>
+	<true/>
 </dict>
 </plist>

+ 5 - 0
frontend/appflowy_flutter/lib/startup/tasks/hot_key.dart

@@ -1,3 +1,4 @@
+import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:hotkey_manager/hotkey_manager.dart';
 
 import '../startup.dart';
@@ -7,6 +8,10 @@ class HotKeyTask extends LaunchTask {
 
   @override
   Future<void> initialize(LaunchContext context) async {
+    // the hotkey manager is not supported on mobile
+    if (PlatformExtension.isMobile) {
+      return;
+    }
     await hotKeyManager.unregisterAll();
   }
 }

+ 18 - 0
frontend/appflowy_flutter/packages/appflowy_backend/ios/Classes/binding.h

@@ -0,0 +1,18 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+int64_t init_sdk(char *path);
+
+void async_event(int64_t port, const uint8_t *input, uintptr_t len);
+
+const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
+
+int32_t set_stream_port(int64_t port);
+
+void link_me_please(void);
+
+void backend_log(int64_t level, const char *data);
+
+void set_env(const char *data);

+ 2 - 2
frontend/appflowy_flutter/packages/flowy_infra_ui/android/src/main/kotlin/com/example/flowy_infra_ui/FlowyInfraUiPlugin.kt

@@ -8,8 +8,8 @@ import io.flutter.plugin.common.MethodChannel
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler
 import io.flutter.plugin.common.MethodChannel.Result
 
-/** FlowyInfraUiPlugin */
-class FlowyInfraUiPlugin: FlutterPlugin, MethodCallHandler {
+/** FlowyInfraUIPlugin */
+class FlowyInfraUIPlugin: FlutterPlugin, MethodCallHandler {
   /// The MethodChannel that will the communication between Flutter and native Android
   ///
   /// This local reference serves to register the plugin with the Flutter Engine and unregister it

+ 1 - 1
frontend/appflowy_flutter/packages/flowy_infra_ui/ios/Classes/FlowyInfraUiPlugin.h

@@ -1,4 +1,4 @@
 #import <Flutter/Flutter.h>
 
-@interface FlowyInfraUiPlugin : NSObject<FlutterPlugin>
+@interface FlowyInfraUIPlugin : NSObject <FlutterPlugin>
 @end

+ 3 - 3
frontend/appflowy_flutter/packages/flowy_infra_ui/ios/Classes/FlowyInfraUiPlugin.m

@@ -1,4 +1,4 @@
-#import "FlowyInfraUiPlugin.h"
+#import "FlowyInfraUIPlugin.h"
 #if __has_include(<flowy_infra_ui/flowy_infra_ui-Swift.h>)
 #import <flowy_infra_ui/flowy_infra_ui-Swift.h>
 #else
@@ -8,8 +8,8 @@
 #import "flowy_infra_ui-Swift.h"
 #endif
 
-@implementation FlowyInfraUiPlugin
+@implementation FlowyInfraUIPlugin
 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
-  [SwiftFlowyInfraUiPlugin registerWithRegistrar:registrar];
+  [SwiftFlowyInfraUIPlugin registerWithRegistrar:registrar];
 }
 @end

+ 2 - 2
frontend/appflowy_flutter/packages/flowy_infra_ui/ios/Classes/SwiftFlowyInfraUiPlugin.swift

@@ -1,10 +1,10 @@
 import Flutter
 import UIKit
 
-public class SwiftFlowyInfraUiPlugin: NSObject, FlutterPlugin {
+public class SwiftFlowyInfraUIPlugin: NSObject, FlutterPlugin {
   public static func register(with registrar: FlutterPluginRegistrar) {
     let channel = FlutterMethodChannel(name: "flowy_infra_ui", binaryMessenger: registrar.messenger())
-    let instance = SwiftFlowyInfraUiPlugin()
+    let instance = SwiftFlowyInfraUIPlugin()
     registrar.addMethodCallDelegate(instance, channel: channel)
   }
 

+ 96 - 0
frontend/scripts/install_dev_env/install_ios.sh

@@ -0,0 +1,96 @@
+#!/bin/bash
+
+YELLOW="\e[93m"
+GREEN="\e[32m"
+RED="\e[31m"
+ENDCOLOR="\e[0m"
+
+printMessage() {
+    printf "${YELLOW}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printSuccess() {
+    printf "${GREEN}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printError() {
+    printf "${RED}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+# Install Rust
+printMessage "The Rust programming language is required to compile AppFlowy."
+printMessage "We can install it now if you don't already have it on your system."
+
+read -p "$(printSuccess "Do you want to install Rust? [y/N]") " installrust
+
+if [[ "${installrust:-N}" == [Yy] ]]; then
+    printMessage "Installing Rust."
+    brew install rustup-init
+    rustup-init -y --default-toolchain=stable
+
+    source "$HOME/.cargo/env"
+else
+    printMessage "Skipping Rust installation."
+fi
+
+rustup target add aarch64-apple-ios
+rustup target add aarch64-apple-ios-sim
+
+# Install sqllite
+printMessage "Installing sqlLite3."
+brew install sqlite3
+
+printMessage "Setting up Flutter"
+
+# Get the current Flutter version
+FLUTTER_VERSION=$(flutter --version | grep -oE 'Flutter [^ ]+' | grep -oE '[^ ]+$')
+# Check if the current version is 3.10.1
+if [ "$FLUTTER_VERSION" = "3.10.1" ]; then
+    echo "Flutter version is already 3.10.1"
+else
+    # Get the path to the Flutter SDK
+    FLUTTER_PATH=$(which flutter)
+    FLUTTER_PATH=${FLUTTER_PATH%/bin/flutter}
+
+    current_dir=$(pwd)
+
+    cd $FLUTTER_PATH
+    # Use git to checkout version 3.10.1 of Flutter
+    git checkout 3.10.1
+    # Get back to current working directory
+    cd "$current_dir"
+
+    echo "Switched to Flutter version 3.10.1"
+fi
+
+# Enable linux desktop
+flutter config --enable-macos-desktop
+
+# Fix any problems reported by flutter doctor
+flutter doctor
+
+# Add the githooks directory to your git configuration
+printMessage "Setting up githooks."
+git config core.hooksPath .githooks
+
+# Install go-gitlint
+printMessage "Installing go-gitlint."
+GOLINT_FILENAME="go-gitlint_1.1.0_osx_x86_64.tar.gz"
+curl -L https://github.com/llorllale/go-gitlint/releases/download/1.1.0/${GOLINT_FILENAME} --output ${GOLINT_FILENAME}
+tar -zxv --directory .githooks/. -f ${GOLINT_FILENAME} gitlint
+rm ${GOLINT_FILENAME}
+
+# Change to the frontend directory
+cd frontend || exit 1
+
+# Install cargo make
+printMessage "Installing cargo-make."
+cargo install --force cargo-make
+
+# Install duckscript
+printMessage "Installing duckscript."
+cargo install --force duckscript_cli
+
+# Check prerequisites
+printMessage "Checking prerequisites."
+cargo make appflowy-flutter-deps-tools

+ 9 - 0
frontend/scripts/makefile/flutter.toml

@@ -38,6 +38,15 @@ mac_alias = "appflowy-macos-dev"
 windows_alias = "appflowy-windows-dev"
 linux_alias = "appflowy-linux-dev"
 
+[tasks.appflowy-ios-dev]
+dependencies = ["appflowy-core-dev-ios"]
+run_task = { name = [
+  "code_generation",
+  "set-app-version",
+  "flutter-build",
+] }
+script_runner = "@shell"
+
 [tasks.appflowy-macos-dev]
 dependencies = ["appflowy-core-dev"]
 run_task = { name = [

+ 55 - 0
frontend/scripts/makefile/mobile.toml

@@ -0,0 +1,55 @@
+
+[tasks.env_check]
+dependencies = ["echo_env", "install_flutter_protobuf"]
+condition = { env_set = [
+  "BUILD_FLAG",
+  "RUST_COMPILE_TARGET",
+  "CRATE_TYPE",
+  "TARGET_OS",
+], channels = [
+  "stable",
+] }
+
+[tasks.appflowy-core-dev-ios]
+category = "Build"
+dependencies = ["env_check"]
+run_task = { name = [
+  "setup-crate-type",
+  "sdk-build-ios",
+  "post-mobile-ios",
+  "restore-crate-type",
+] }
+
+[tasks.sdk-build-ios]
+private = true
+script = [
+  """
+    cd rust-lib/
+    rustup show
+    echo cargo lipo --targets ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
+    cargo lipo --targets ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
+    cd ../
+  """,
+]
+script_runner = "@shell"
+
+[tasks.post-mobile-ios]
+private = true
+script = [
+  """
+    echo "🚀 🚀 🚀  AppFlowy-Core for iOS platform build success"
+    dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/packages/appflowy_backend/${TARGET_OS}
+    lib = set lib${LIB_NAME}.${LIB_EXT}
+
+    echo "💻 💻 💻  Copying ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/target/${RUST_COMPILE_TARGET}/${BUILD_FLAG}/${lib} to ${dart_ffi_dir}/${lib}"
+    rm -f ${dart_ffi_dir}/${lib}
+    cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/target/${RUST_COMPILE_TARGET}/${BUILD_FLAG}/${lib} \
+    ${dart_ffi_dir}/${lib}
+
+    echo "💻 💻 💻  Copying ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/binding.h to ${dart_ffi_dir}/Classes/binding.h"
+    rm -f ${dart_ffi_dir}/Classes/binding.h
+    cp ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/binding.h \
+    ${dart_ffi_dir}/Classes/binding.h
+  """,
+]
+script_runner = "@duckscript"