diff --git a/.github/workflows/ios_build_tests.yml b/.github/workflows/ios_build_tests.yml index 6c28967abf7..3c3b8420ae1 100644 --- a/.github/workflows/ios_build_tests.yml +++ b/.github/workflows/ios_build_tests.yml @@ -38,11 +38,6 @@ jobs: uses: actions/checkout@v3 with: lfs: true - - name: Xcodegen - uses: xavierLowmiller/xcodegen-action@1.1.2 - with: - spec: framework/examples/ios-demo/project.yml - version: '2.32.0' - name: Demo working-directory: framework/examples/ios-demo run: | diff --git a/.github/workflows/project_artifact_compare.yml b/.github/workflows/project_artifact_compare.yml index 2c0980704e5..bdb37c8c999 100644 --- a/.github/workflows/project_artifact_compare.yml +++ b/.github/workflows/project_artifact_compare.yml @@ -89,16 +89,10 @@ jobs: with: ref: ${{ matrix.ref }} lfs: true - - name: Xcodegen - uses: xavierLowmiller/xcodegen-action@1.1.2 - with: - spec: framework/examples/ios-demo/project.yml - version: '2.32.0' - name: Build if: ${{ matrix.ref }} run: | pushd framework/examples/ios-demo - xcodegen pod install xcodebuild build \ -destination 'generic/platform=iOS' \ diff --git a/.gitignore b/.gitignore index 01bdcb1af9d..33cc2630ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ framework/examples/android-demo/src/main/assets/ framework/examples/android-demo/libs/* framework/examples/android-demo/maven-auth.properties framework/examples/android-demo/.cxx/ -framework/examples/ios-demo/HippyDemo.xcodeproj framework/examples/ios-demo/HippyDemo.xcworkspace framework/examples/ios-demo/Pods/* framework/examples/ios-demo/Podfile.lock diff --git a/.husky/post-checkout b/.husky/post-checkout index c37815e2b56..ca7fcb40088 100755 --- a/.husky/post-checkout +++ b/.husky/post-checkout @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-checkout'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-checkout' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-checkout "$@" diff --git a/.husky/post-commit b/.husky/post-commit index e5230c305f9..52b339cb3f4 100755 --- a/.husky/post-commit +++ b/.husky/post-commit @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-commit'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-commit' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-commit "$@" diff --git a/.husky/post-merge b/.husky/post-merge index c99b752a527..a912e667aa3 100755 --- a/.husky/post-merge +++ b/.husky/post-merge @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-merge'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-merge' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-merge "$@" diff --git a/.husky/pre-push b/.husky/pre-push index 216e91527e6..0f0089bc25d 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs pre-push "$@" diff --git a/README.md b/README.md index 9e7e17ac22b..bbd6ae55a27 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ For iOS, we recommend to use iOS simulator when first try. However, you can chan 3. Choose a demo to build with `npm run buildexample [hippy-react-demo|hippy-vue-demo|hippy-vue-next-demo]`. -4. Install Xcodegen with `brew install xcodegen`, install CocoaPods with `brew install cocoapods`, install cmake with `brew install cmake`, then execute `xcodegen` command at `framework/examples/ios-demo` directory, which will create `HippyDemo.xcodeproj` and `HippyDemo.xcworkspace` files and install Cocoapods dependencies. +4. Install CocoaPods with `brew install cocoapods`, install cmake with `brew install cmake`, then execute `pod install` command at `framework/examples/ios-demo` directory, which will create `HippyDemo.xcworkspace` files and install Cocoapods dependencies. 5. Start the Xcode and build the iOS app with opening `framework/examples/ios-demo/HippyDemo.xcworkspace`. diff --git a/docs/api/hippy-react/components.md b/docs/api/hippy-react/components.md index 5a4954c9641..a7985c317cd 100644 --- a/docs/api/hippy-react/components.md +++ b/docs/api/hippy-react/components.md @@ -182,7 +182,7 @@ import icon from './qb_icon_new.png'; | autoHideStatusBar | 是否在`Modal`显示时自动隐藏状态栏。Android 中仅 api28 以上生效。 `default: false` | `boolean` | `Android` | | autoHideNavigationBar | 是否在`Modal`显示时自动隐藏导航栏。 `default: false` | `boolean` | `Android` | | onShow | 在`Modal`显示时会执行此回调函数。 | `Function` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | -| onOrientationChange | 屏幕旋转方向改变时执行会回调 | `Function` | `Android、iOS` | +| onOrientationChange | 屏幕旋转方向改变时执行会回调,返回当前屏幕显示方向 `{ orientation: portrait|landscape }` | `Function` | `Android、iOS` | | onRequestClose | 在 `Modal` 请求关闭时会执行此回调函数,一般时在 Android 系统里按下硬件返回按钮时触发,一般要在里面处理关闭弹窗。 | `Function` | `Android、hippy-react-web、Voltron` | | transparent | 背景是否是透明的。`default: true` | `boolean` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | visible | 是否显示。`default: true` | `boolean` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | diff --git a/docs/development/android-3.0-integration-guidelines.md b/docs/development/android-3.0-integration-guidelines.md index b2613b69ac1..781103152d5 100644 --- a/docs/development/android-3.0-integration-guidelines.md +++ b/docs/development/android-3.0-integration-guidelines.md @@ -22,7 +22,7 @@ 2. Maven 集成 - - 查询 [Maven Central Hippy 版本](https://search.maven.org/search?q=com.tencent.hippy),其中 `hippy-release` 为 `release` 版本(不携带 `inspector`),`hippy-debug` 为 `debug` 版本 + - 查询 [Maven Central Hippy 版本](https://search.maven.org/search?q=com.tencent.hippy),其中 `hippy-release` 为 `release` 版本,`hippy-debug` 为 `debug` 版本,`hippy-snapshot` 为 `beta` 版本 - 配置 build.gradle diff --git a/docs/development/demo.md b/docs/development/demo.md index a8c0c4de6b8..bb970335588 100644 --- a/docs/development/demo.md +++ b/docs/development/demo.md @@ -22,7 +22,7 @@ Demo的Native工程代码位于framework/examples目录,前端工程代码位 首先,通过Homebrew包管理工具安装git, git-lfs, node(v16) and npm(v7) ```shell -brew install git git-lfs node@16 xcodegen cmake +brew install git git-lfs node@16 cmake ``` #### 编译iOS Demo环境准备 @@ -82,9 +82,9 @@ git clone https://github.com/Tencent/Hippy.git ```shell # 进入Hippy源码目录 cd ./framework/examples/ios-demo -# 执行xcodegen生成工程 -xcodegen -# 打开workspace,编译运行即可 +# 使用 Cocoapods 生成工程 +pod install +# 打开 workspace,编译运行即可 open HippyDemo.xcworkspace ``` diff --git a/dom/src/dom/taitank_layout_node.cc b/dom/src/dom/taitank_layout_node.cc index b4b5bba339f..a21024d6c73 100644 --- a/dom/src/dom/taitank_layout_node.cc +++ b/dom/src/dom/taitank_layout_node.cc @@ -157,7 +157,7 @@ TAITANK_GET_STYLE_DECL(Direction, TaitankDirection, TaitankDirection::DIRECTION_ } static void CheckValueType(footstone::value::HippyValue::Type type) { - if (type == footstone::value::HippyValue::Type::kNumber || type == footstone::value::HippyValue::Type::kObject) + if (type == footstone::value::HippyValue::Type::kString || type == footstone::value::HippyValue::Type::kObject) FOOTSTONE_DLOG(WARNING) << "Taitank Layout Node Value Type Error"; } diff --git a/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx b/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx index 5bf6a3fb443..7b2f772c5d3 100644 --- a/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx +++ b/driver/js/examples/hippy-react-demo/src/components/Modal/index.jsx @@ -154,7 +154,8 @@ export default class ModalExpo extends React.Component { transparent={true} animationType={this.state.animationType} visible={visible} - onRequestClose={() => { /* Trigger when hardware back pressed */ }} + requestClose={() => { /* Trigger when hardware back pressed */ }} + orientationChange={(evt) => { console.log('orientation changed', evt.orientation); }} supportedOrientations={['portrait']} immersionStatusBar={this.state.immerseStatusBar} autoHideStatusBar={this.state.hideStatusBar} diff --git a/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue b/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue index 62861904c5e..864c1edee6a 100644 --- a/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue +++ b/driver/js/examples/hippy-vue-demo/src/components/native-demos/demo-dialog.vue @@ -51,6 +51,7 @@ :autoHideNavigationBar="autoHideNavigationBar" @show="onShow" @requestClose="onClose" + @orientationChange="onOrientationChange" >
@@ -80,6 +81,7 @@ :autoHideStatusBar="autoHideStatusBar" :autoHideNavigationBar="autoHideNavigationBar" @requestClose="onClose" + @orientationChange="onOrientationChange" >
@@ -78,6 +79,7 @@ :animationType="dialogAnimationType" :transparent="true" @requestClose="onClose" + @orientationChange="onOrientationChange" >
{ console.log('Dialog is opening'); }; - + const onOrientationChange = (evt) => { + console.log('orientation changed', evt.nativeParams); + }; const onClose = (evt) => { evt.stopPropagation(); /** diff --git a/driver/js/include/driver/js_driver_utils.h b/driver/js/include/driver/js_driver_utils.h index 7a9a36cad04..188889c5a83 100644 --- a/driver/js/include/driver/js_driver_utils.h +++ b/driver/js/include/driver/js_driver_utils.h @@ -46,7 +46,8 @@ class JsDriverUtils { static std::shared_ptr CreateEngineAndAsyncInitialize(const std::shared_ptr& task_runner, const std::shared_ptr& param, - int64_t group_id); + int64_t group_id, + bool is_reload); static void InitInstance(const std::shared_ptr& engine, const std::shared_ptr& param, diff --git a/driver/js/src/js_driver_utils.cc b/driver/js/src/js_driver_utils.cc index f293886274e..e3b91d4d746 100644 --- a/driver/js/src/js_driver_utils.cc +++ b/driver/js/src/js_driver_utils.cc @@ -133,13 +133,17 @@ void AsyncInitializeEngine(const std::shared_ptr& engine, std::shared_ptr JsDriverUtils::CreateEngineAndAsyncInitialize(const std::shared_ptr& task_runner, const std::shared_ptr& param, - int64_t group_id) { + int64_t group_id, + bool is_reload) { FOOTSTONE_DCHECK(group_id >= -1) << "group_id must be greater than or equal to -1"; std::shared_ptr engine = nullptr; auto group = group_id; if (param->is_debug) { group = VM::kDebuggerGroupId; } + // 1. 调试模式启动新实例不复用V8 context, 调试模式 reload 场景复用 V8 context + // 2. Demo 场景, group_id 为 -1,不复用 V8 context + if ((group == VM::kDebuggerGroupId && is_reload) || (group != VM::kDebuggerGroupId && group != VM::kDefaultGroupId)) { std::lock_guard lock(engine_mutex); auto it = reuse_engine_map.find(group); diff --git a/driver/js/src/napi/jsc/jsc_ctx.cc b/driver/js/src/napi/jsc/jsc_ctx.cc index c7d1be501ce..25f1d9f8a83 100644 --- a/driver/js/src/napi/jsc/jsc_ctx.cc +++ b/driver/js/src/napi/jsc/jsc_ctx.cc @@ -1149,6 +1149,7 @@ std::shared_ptr JSCCtx::RunScript(const string_view& data, if (exception) { SetException(std::make_shared(context_, exception)); + FOOTSTONE_LOG(ERROR) << GetExceptionMessage(exception_); return nullptr; } diff --git a/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h b/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h index 97cb14f0644..56a3d914d87 100644 --- a/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h +++ b/framework/android/connector/driver/js/src/main/cpp/include/connector/js_driver_jni.h @@ -40,7 +40,8 @@ jint CreateJsDriver(JNIEnv* j_env, jint j_dom_manager_id, jobject j_vm_init_param, jint j_vfs_id, - jint j_devtools_id); + jint j_devtools_id, + jboolean j_is_reload); void DestroyJsDriver(JNIEnv* j_env, jobject j_object, diff --git a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc index b3f03d58564..a6891e59f47 100644 --- a/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc +++ b/framework/android/connector/driver/js/src/main/cpp/src/js_driver_jni.cc @@ -71,7 +71,7 @@ inline namespace driver { REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) "onCreate", "([BZZZLcom/openhippy/connector/NativeCallback;" - "JILcom/openhippy/connector/JsDriver$V8InitParams;II)I", + "JILcom/openhippy/connector/JsDriver$V8InitParams;IIZ)I", CreateJsDriver) REGISTER_JNI("com/openhippy/connector/JsDriver", // NOLINT(cert-err58-cpp) @@ -266,7 +266,8 @@ jint CreateJsDriver(JNIEnv* j_env, jint j_dom_manager_id, jobject j_vm_init_param, jint j_vfs_id, - jint j_devtools_id) { + jint j_devtools_id, + jboolean j_is_reload) { FOOTSTONE_LOG(INFO) << "CreateJsDriver begin, j_single_thread_mode = " << static_cast(j_single_thread_mode) << ", j_bridge_param_json = " @@ -343,7 +344,7 @@ jint CreateJsDriver(JNIEnv* j_env, } }; auto engine = JsDriverUtils::CreateEngineAndAsyncInitialize( - dom_task_runner, param, static_cast(j_group_id)); + dom_task_runner, param, static_cast(j_group_id), static_cast(j_is_reload)); { std::lock_guard lock(holder_mutex); engine_holder[engine.get()] = engine; diff --git a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java index d61b10eb0c8..6e277584bcb 100644 --- a/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java +++ b/framework/android/connector/driver/js/src/main/java/com/openhippy/connector/JsDriver.java @@ -92,9 +92,9 @@ public void onResourceReady(ByteBuffer output, long resId) { public void initialize(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, - long groupId, int domManagerId, V8InitParams v8InitParams, int vfsId, int devtoolsId) { + long groupId, int domManagerId, V8InitParams v8InitParams, int vfsId, int devtoolsId, boolean isReload) { mInstanceId = onCreate(globalConfig, useLowMemoryMode, enableV8Serialization, - isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId); + isDevModule, callback, groupId, domManagerId, v8InitParams, vfsId, devtoolsId, isReload); } public void onDestroy(boolean useLowMemoryMode, boolean isReload, @@ -147,7 +147,7 @@ public void attachToRoot(@NonNull View root) { private native int onCreate(byte[] globalConfig, boolean useLowMemoryMode, boolean enableV8Serialization, boolean isDevModule, NativeCallback callback, - long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId); + long groupId, int domManagerId, V8InitParams v8InitParams, int vfs_id, int devtoolsId, boolean isReload); private native void onDestroy(int instanceId, boolean useLowMemoryMode, boolean isReload, NativeCallback callback); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java index 5543e0957c1..f78739eaaf0 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/HippyEngineManagerImpl.java @@ -646,7 +646,7 @@ public void callback(Boolean result, Throwable e) { notifyEngineInitialized(EngineInitStatus.STATUS_WRONG_STATE, e); } } - }); + }, onReLoad); } /** diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java index 47cef1a1258..bacc8bcb1b8 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridge.java @@ -27,7 +27,7 @@ public interface HippyBridge { String URI_SCHEME_ASSETS = "asset:"; String URI_SCHEME_FILE = "file:"; - void initJSBridge(String gobalConfig, NativeCallback callback, int groupId); + void initJSBridge(String gobalConfig, NativeCallback callback, int groupId, boolean isReload); boolean runScriptFromUri(String uri, AssetManager assetManager, boolean canUseCodeCache, String codeCacheTag, NativeCallback callback); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java index be142d2d3f6..64e0be25713 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeImpl.java @@ -91,12 +91,12 @@ public HippyBridgeImpl(HippyEngineContext engineContext, BridgeCallback callback } @Override - public void initJSBridge(String globalConfig, NativeCallback callback, final int groupId) { + public void initJSBridge(String globalConfig, NativeCallback callback, final int groupId, boolean isReload) { mDebugGlobalConfig = globalConfig; - initJSEngine(groupId, callback); + initJSEngine(groupId, callback, isReload); } - private void initJSEngine(int groupId, NativeCallback callback) { + private void initJSEngine(int groupId, NativeCallback callback, boolean isReload) { synchronized (HippyBridgeImpl.class) { try { String localCachePath = mContext.getGlobalConfigs().getContext().getCacheDir() @@ -112,7 +112,8 @@ private void initJSEngine(int groupId, NativeCallback callback) { mContext.getDomManagerId(), mV8InitParams, mContext.getVfsId(), - mContext.getDevtoolsId() + mContext.getDevtoolsId(), + isReload ); mInit = true; } catch (Throwable e) { diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java index 4a586f22c1d..ef10a281273 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManager.java @@ -26,7 +26,7 @@ @SuppressWarnings({"deprecation", "unused"}) public interface HippyBridgeManager { - void initBridge(Callback callback); + void initBridge(Callback callback, boolean isReload); void runBundle(int id, HippyBundleLoader loader); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java index b1b854b1829..72a0af5297d 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/bridge/HippyBridgeManagerImpl.java @@ -51,6 +51,7 @@ import com.tencent.mtt.hippy.utils.TimeMonitor; import java.lang.ref.WeakReference; +import java.util.HashMap; import org.json.JSONObject; import java.nio.ByteBuffer; @@ -87,6 +88,9 @@ public enum BridgeState { public static final int DESTROY_CLOSE = 0; public static final int DESTROY_RELOAD = 1; + public static final int CREATE_NORMAL = 0; + public static final int CREATE_RELOAD = 1; + final HippyEngineContext mContext; final HippyBundleLoader mCoreBundleLoader; private final HippyBridge mHippyBridge; @@ -229,6 +233,7 @@ public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_CODE_INIT_BRIDGE: { @SuppressWarnings("unchecked") final com.tencent.mtt.hippy.common.Callback callback = (com.tencent.mtt.hippy.common.Callback) msg.obj; + final int code = msg.arg1; try { mHippyBridge.initJSBridge(getGlobalConfigs(), new NativeCallback(mHandler) { @Override @@ -280,7 +285,7 @@ public void Call(long result, Message message, callback.callback(true, null); } } - }, mGroupId); + }, mGroupId, code == CREATE_RELOAD); } catch (Throwable e) { mBridgeState = BridgeState.UNINITIALIZED; callback.callback(false, e); @@ -361,9 +366,11 @@ public void Call(long result, Message message, String action, } @Override - public void initBridge(Callback callback) { + public void initBridge(Callback callback, boolean isReload) { mHandler = new Handler(mContext.getThreadExecutor().getBridgeThread().getLooper(), this); - Message message = mHandler.obtainMessage(MSG_CODE_INIT_BRIDGE, callback); + Message message = mHandler.obtainMessage(MSG_CODE_INIT_BRIDGE); + message.arg1 = isReload ? CREATE_RELOAD : CREATE_NORMAL; + message.obj = callback; mHandler.sendMessage(message); } diff --git a/framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..b5bf8471335 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.pbxproj @@ -0,0 +1,576 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 16D159EB8DCA407DFE4008C8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D2EE3455BE6DD7D2ADA20093 /* main.m */; }; + 291382C2783D93A2C7BD4ACB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6094F1F80FD70F9F9853EE15 /* AppDelegate.m */; }; + 36842E3B36BB6E52267BED0F /* HomePageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC53FC9D9560D6AD989F8540 /* HomePageView.xib */; }; + 369577C613161936EE28F52E /* DebugCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BD54126F3E80B297E2109B40 /* DebugCell.m */; }; + 380469787621147052854E8F /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE0C5F943248577555BFB26 /* SettingsViewController.m */; }; + 4CBD515D378CB81C4C164353 /* DebugCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01F6A42F861E597779C10D09 /* DebugCell.xib */; }; + 5A5A9B087B27BD97E132C49E /* DemoBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4708C41E8571123282E2B806 /* DemoBaseViewController.m */; }; + 5A60628334CE16EA4F8B1329 /* DemoNavigationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 16348E2DEBFF10ADD6FCF7A9 /* DemoNavigationViewController.m */; }; + 72328C3389BA8DD5E54D7D5F /* TurboConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 46034630F6D4A25199673157 /* TurboConfig.m */; }; + 74C51526435B1AB788CBF991 /* TurboBaseModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC8AEAE8BF116954FB41BDF9 /* TurboBaseModule.mm */; }; + 8C009766BC90CAF781042636 /* PageCreationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E734E5A93F5B92EA4149B8D /* PageCreationViewController.m */; }; + 8D5E861571620B4DA80254C2 /* TTTGB-Medium.otf in CopyFiles */ = {isa = PBXBuildFile; fileRef = B2FA2A97C78628634C6AFC9A /* TTTGB-Medium.otf */; }; + 994E84462AD481034728A29D /* HippyPageCacheView.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BFD15025A6217FEE93CC45 /* HippyPageCacheView.m */; }; + 9C99A05896359E49AE9A8352 /* MyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 90F34E176169143334EDFC28 /* MyView.m */; }; + 9CF888F990C83ECD4F6C78AA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EF877A8CB4B2AD68A780A12 /* Assets.xcassets */; }; + A3F629CA4C94D65F42BB4B33 /* HippyDemoViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9EB0CD5FCC3F38E3C7DC5E00 /* HippyDemoViewController.mm */; }; + B4CB9EEA2C501CCD508731F6 /* Pods_HippyDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25D66C0C95F43BF303761823 /* Pods_HippyDemo.framework */; }; + B5E0049F9941EA38E80EC858 /* HippyDemoLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9D15F65FD369654F00146AD7 /* HippyDemoLoader.mm */; }; + B8AA2B793BE43710EFC9D4DC /* TestModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = D2241406CEF00D2FF5938BB2 /* TestModule.mm */; }; + BD5196B65EE0B712FDA2EB9B /* UIViewController+Title.m in Sources */ = {isa = PBXBuildFile; fileRef = C412B5D210F1E3359523628C /* UIViewController+Title.m */; }; + C1A78F8868963E6C790472AA /* PageCreationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 25B65684EC9C9AF27BB04D21 /* PageCreationCell.xib */; }; + C94B7BB8A618F36A1F58C270 /* HomePageViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 90B5552FD45612EC5EC4CB45 /* HomePageViewController.mm */; }; + D026B185E903AE8F958F8588 /* HippyPageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E7AA75BF1236A4328285E7 /* HippyPageCache.m */; }; + DCCEB3FED1802C0D9C21AB11 /* PageManagerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CC425F89818837D6DA1053A /* PageManagerViewController.m */; }; + DE19BAD300150C5D3D76D7F7 /* PageCreationCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 263EFA7461EF8C6CCDF67C66 /* PageCreationCell.m */; }; + DF04004944D28CE1569D68EC /* HippyPageCacheContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A066953E3614D26125F617EF /* HippyPageCacheContainerView.m */; }; + EBC6C5A3C36A88BF4DAD00BE /* res in Resources */ = {isa = PBXBuildFile; fileRef = 1E1EECB72C2ACDCB192E6628 /* res */; }; + F3F6D205C6FBA36CB262A28E /* SettingsInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C6BAC7BC0021E9E21B04E496 /* SettingsInfo.m */; }; + F67849BC0665859B70497D0E /* IconUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 17AB7FB6346FE861C9D953F5 /* IconUtils.m */; }; + FD3BE72F8A086DC9A9484195 /* MyViewManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 834EC46509EC221C787A9F23 /* MyViewManager.mm */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 171164B8638E25416B38386C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + 8D5E861571620B4DA80254C2 /* TTTGB-Medium.otf in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 01F6A42F861E597779C10D09 /* DebugCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DebugCell.xib; sourceTree = ""; }; + 041D598BDAC3735B5A06ECE7 /* TurboConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TurboConfig.h; sourceTree = ""; }; + 078054D939523BD635AFFB94 /* SettingsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; + 16348E2DEBFF10ADD6FCF7A9 /* DemoNavigationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoNavigationViewController.m; sourceTree = ""; }; + 16E50FF2E97E0CF6CD4B1EE1 /* MyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyView.h; sourceTree = ""; }; + 177A9E8373F2682A9A831991 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 17AB7FB6346FE861C9D953F5 /* IconUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IconUtils.m; sourceTree = ""; }; + 1E1EECB72C2ACDCB192E6628 /* res */ = {isa = PBXFileReference; lastKnownFileType = folder; path = res; sourceTree = SOURCE_ROOT; }; + 1E734E5A93F5B92EA4149B8D /* PageCreationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PageCreationViewController.m; sourceTree = ""; }; + 25B65684EC9C9AF27BB04D21 /* PageCreationCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageCreationCell.xib; sourceTree = ""; }; + 25D66C0C95F43BF303761823 /* Pods_HippyDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HippyDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 263EFA7461EF8C6CCDF67C66 /* PageCreationCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PageCreationCell.m; sourceTree = ""; }; + 27E7AA75BF1236A4328285E7 /* HippyPageCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyPageCache.m; sourceTree = ""; }; + 28C2DA6A96B596294158B364 /* HippyDemoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyDemoViewController.h; sourceTree = ""; }; + 331F0D867F0DF450A84E4587 /* Pods-HippyDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HippyDemo.debug.xcconfig"; path = "Target Support Files/Pods-HippyDemo/Pods-HippyDemo.debug.xcconfig"; sourceTree = ""; }; + 3A2AA203AF38A01360BEFB2A /* HippyDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HippyDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 427F60D1B98DA953328F4E71 /* DemoBaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoBaseViewController.h; sourceTree = ""; }; + 46034630F6D4A25199673157 /* TurboConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TurboConfig.m; sourceTree = ""; }; + 4708C41E8571123282E2B806 /* DemoBaseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoBaseViewController.m; sourceTree = ""; }; + 4BCCCB3005CE328BD310C417 /* HippyDemoLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyDemoLoader.h; sourceTree = ""; }; + 53A0957F7CC6967C8F70403A /* PageManagerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageManagerViewController.h; sourceTree = ""; }; + 5EF877A8CB4B2AD68A780A12 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6045C301C6CFDC6C2E87AA66 /* IconUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IconUtils.h; sourceTree = ""; }; + 6094F1F80FD70F9F9853EE15 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 6C482239432657812B25A59A /* SettingsInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsInfo.h; sourceTree = ""; }; + 73A859BDC9C3189E38FD082F /* MyViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyViewManager.h; sourceTree = ""; }; + 7CC425F89818837D6DA1053A /* PageManagerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PageManagerViewController.m; sourceTree = ""; }; + 7DB34DDF95B8E9C9AD87FFE4 /* Pods-HippyDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HippyDemo.release.xcconfig"; path = "Target Support Files/Pods-HippyDemo/Pods-HippyDemo.release.xcconfig"; sourceTree = ""; }; + 80D715362B1A0B0DCEAA813D /* DemoNavigationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoNavigationViewController.h; sourceTree = ""; }; + 82BFD15025A6217FEE93CC45 /* HippyPageCacheView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyPageCacheView.m; sourceTree = ""; }; + 834EC46509EC221C787A9F23 /* MyViewManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MyViewManager.mm; sourceTree = ""; }; + 8C70B069BA7C4A351FD26D60 /* HippyPageCacheView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyPageCacheView.h; sourceTree = ""; }; + 90B5552FD45612EC5EC4CB45 /* HomePageViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HomePageViewController.mm; sourceTree = ""; }; + 90F34E176169143334EDFC28 /* MyView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyView.m; sourceTree = ""; }; + 91861A8DD9D692FEDF657D34 /* HomePageViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomePageViewController.h; sourceTree = ""; }; + 9ABAD8FCDB669A1A6F327CDB /* DebugCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DebugCell.h; sourceTree = ""; }; + 9D15F65FD369654F00146AD7 /* HippyDemoLoader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HippyDemoLoader.mm; sourceTree = ""; }; + 9EB0CD5FCC3F38E3C7DC5E00 /* HippyDemoViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HippyDemoViewController.mm; sourceTree = ""; }; + A066953E3614D26125F617EF /* HippyPageCacheContainerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HippyPageCacheContainerView.m; sourceTree = ""; }; + B2FA2A97C78628634C6AFC9A /* TTTGB-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "TTTGB-Medium.otf"; sourceTree = ""; }; + B5A22F93EAA399DE400CED88 /* TestModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestModule.h; sourceTree = ""; }; + BD54126F3E80B297E2109B40 /* DebugCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DebugCell.m; sourceTree = ""; }; + BF84819C1E7CCD88BCBEA4D7 /* HippyPageCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyPageCache.h; sourceTree = ""; }; + C412B5D210F1E3359523628C /* UIViewController+Title.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Title.m"; sourceTree = ""; }; + C629FDE26F7B6DBC976FB63A /* TurboBaseModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TurboBaseModule.h; sourceTree = ""; }; + C6BAC7BC0021E9E21B04E496 /* SettingsInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsInfo.m; sourceTree = ""; }; + C7504BC18486120B29F3D9FA /* PageCreationCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageCreationCell.h; sourceTree = ""; }; + CC8AEAE8BF116954FB41BDF9 /* TurboBaseModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TurboBaseModule.mm; sourceTree = ""; }; + D2241406CEF00D2FF5938BB2 /* TestModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestModule.mm; sourceTree = ""; }; + D2EE3455BE6DD7D2ADA20093 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DC53FC9D9560D6AD989F8540 /* HomePageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomePageView.xib; sourceTree = ""; }; + DDD052D2760E830B1E0095EA /* UIViewController+Title.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Title.h"; sourceTree = ""; }; + E54E76502FD6AF687683BA8A /* HippyPageCacheContainerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HippyPageCacheContainerView.h; sourceTree = ""; }; + EEE0C5F943248577555BFB26 /* SettingsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; + EF558AABDC113B410B8ED69C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + F3C3E83580FEFC6EEE4CF186 /* DemoConfigs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoConfigs.h; sourceTree = ""; }; + FBB045A62B4E511674BF590C /* PageCreationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PageCreationViewController.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 188D3F897881826DDEBE243C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B4CB9EEA2C501CCD508731F6 /* Pods_HippyDemo.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0632A691275B124491E38EA7 /* HippyDemo */ = { + isa = PBXGroup; + children = ( + EF558AABDC113B410B8ED69C /* AppDelegate.h */, + 6094F1F80FD70F9F9853EE15 /* AppDelegate.m */, + 427F60D1B98DA953328F4E71 /* DemoBaseViewController.h */, + 4708C41E8571123282E2B806 /* DemoBaseViewController.m */, + F3C3E83580FEFC6EEE4CF186 /* DemoConfigs.h */, + 80D715362B1A0B0DCEAA813D /* DemoNavigationViewController.h */, + 16348E2DEBFF10ADD6FCF7A9 /* DemoNavigationViewController.m */, + 6045C301C6CFDC6C2E87AA66 /* IconUtils.h */, + 17AB7FB6346FE861C9D953F5 /* IconUtils.m */, + 177A9E8373F2682A9A831991 /* Info.plist */, + D2EE3455BE6DD7D2ADA20093 /* main.m */, + B5A22F93EAA399DE400CED88 /* TestModule.h */, + D2241406CEF00D2FF5938BB2 /* TestModule.mm */, + DDD052D2760E830B1E0095EA /* UIViewController+Title.h */, + C412B5D210F1E3359523628C /* UIViewController+Title.m */, + 4CF6648308C60D57DD274D30 /* HomePage */, + DE44248AB3BFFAAE044EAB9E /* myview */, + 73EF815F203E15B86205DFC3 /* PageManager */, + 57D9249C5EA211061816F73B /* RenderPage */, + 09FA8898DDD429EC19584446 /* SettingsPage */, + 529ECDFF8342B9C9D950B1FA /* turbomodule */, + 260EEB3518979BB11CBFC5C8 /* vfsloader */, + ); + path = HippyDemo; + sourceTree = ""; + }; + 09FA8898DDD429EC19584446 /* SettingsPage */ = { + isa = PBXGroup; + children = ( + 6C482239432657812B25A59A /* SettingsInfo.h */, + C6BAC7BC0021E9E21B04E496 /* SettingsInfo.m */, + 078054D939523BD635AFFB94 /* SettingsViewController.h */, + EEE0C5F943248577555BFB26 /* SettingsViewController.m */, + ); + path = SettingsPage; + sourceTree = ""; + }; + 260EEB3518979BB11CBFC5C8 /* vfsloader */ = { + isa = PBXGroup; + children = ( + 4BCCCB3005CE328BD310C417 /* HippyDemoLoader.h */, + 9D15F65FD369654F00146AD7 /* HippyDemoLoader.mm */, + ); + path = vfsloader; + sourceTree = ""; + }; + 37AC1BAC994661BA846D4236 /* Products */ = { + isa = PBXGroup; + children = ( + 3A2AA203AF38A01360BEFB2A /* HippyDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 3F4F18D890009F63DB0F22D2 = { + isa = PBXGroup; + children = ( + 5EF877A8CB4B2AD68A780A12 /* Assets.xcassets */, + 1E1EECB72C2ACDCB192E6628 /* res */, + F3C07E1B41FBB8BA0834C6BC /* fonts */, + 0632A691275B124491E38EA7 /* HippyDemo */, + 37AC1BAC994661BA846D4236 /* Products */, + 7D0241712C232049EC5C8DC8 /* Pods */, + C24B18F307439A018EC32537 /* Frameworks */, + ); + sourceTree = ""; + }; + 4CF6648308C60D57DD274D30 /* HomePage */ = { + isa = PBXGroup; + children = ( + DC53FC9D9560D6AD989F8540 /* HomePageView.xib */, + 91861A8DD9D692FEDF657D34 /* HomePageViewController.h */, + 90B5552FD45612EC5EC4CB45 /* HomePageViewController.mm */, + ); + path = HomePage; + sourceTree = ""; + }; + 529ECDFF8342B9C9D950B1FA /* turbomodule */ = { + isa = PBXGroup; + children = ( + C629FDE26F7B6DBC976FB63A /* TurboBaseModule.h */, + CC8AEAE8BF116954FB41BDF9 /* TurboBaseModule.mm */, + 041D598BDAC3735B5A06ECE7 /* TurboConfig.h */, + 46034630F6D4A25199673157 /* TurboConfig.m */, + ); + path = turbomodule; + sourceTree = ""; + }; + 57D9249C5EA211061816F73B /* RenderPage */ = { + isa = PBXGroup; + children = ( + 28C2DA6A96B596294158B364 /* HippyDemoViewController.h */, + 9EB0CD5FCC3F38E3C7DC5E00 /* HippyDemoViewController.mm */, + ); + path = RenderPage; + sourceTree = ""; + }; + 73EF815F203E15B86205DFC3 /* PageManager */ = { + isa = PBXGroup; + children = ( + 9ABAD8FCDB669A1A6F327CDB /* DebugCell.h */, + BD54126F3E80B297E2109B40 /* DebugCell.m */, + 01F6A42F861E597779C10D09 /* DebugCell.xib */, + BF84819C1E7CCD88BCBEA4D7 /* HippyPageCache.h */, + 27E7AA75BF1236A4328285E7 /* HippyPageCache.m */, + E54E76502FD6AF687683BA8A /* HippyPageCacheContainerView.h */, + A066953E3614D26125F617EF /* HippyPageCacheContainerView.m */, + 8C70B069BA7C4A351FD26D60 /* HippyPageCacheView.h */, + 82BFD15025A6217FEE93CC45 /* HippyPageCacheView.m */, + C7504BC18486120B29F3D9FA /* PageCreationCell.h */, + 263EFA7461EF8C6CCDF67C66 /* PageCreationCell.m */, + 25B65684EC9C9AF27BB04D21 /* PageCreationCell.xib */, + FBB045A62B4E511674BF590C /* PageCreationViewController.h */, + 1E734E5A93F5B92EA4149B8D /* PageCreationViewController.m */, + 53A0957F7CC6967C8F70403A /* PageManagerViewController.h */, + 7CC425F89818837D6DA1053A /* PageManagerViewController.m */, + ); + path = PageManager; + sourceTree = ""; + }; + 7D0241712C232049EC5C8DC8 /* Pods */ = { + isa = PBXGroup; + children = ( + 331F0D867F0DF450A84E4587 /* Pods-HippyDemo.debug.xcconfig */, + 7DB34DDF95B8E9C9AD87FFE4 /* Pods-HippyDemo.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + C24B18F307439A018EC32537 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 25D66C0C95F43BF303761823 /* Pods_HippyDemo.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + DE44248AB3BFFAAE044EAB9E /* myview */ = { + isa = PBXGroup; + children = ( + 16E50FF2E97E0CF6CD4B1EE1 /* MyView.h */, + 90F34E176169143334EDFC28 /* MyView.m */, + 73A859BDC9C3189E38FD082F /* MyViewManager.h */, + 834EC46509EC221C787A9F23 /* MyViewManager.mm */, + ); + path = myview; + sourceTree = ""; + }; + F3C07E1B41FBB8BA0834C6BC /* fonts */ = { + isa = PBXGroup; + children = ( + B2FA2A97C78628634C6AFC9A /* TTTGB-Medium.otf */, + ); + path = fonts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F7DF50CA19E5E79F64D3D4E6 /* HippyDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 20BB0794C4C1CBC0A34760F4 /* Build configuration list for PBXNativeTarget "HippyDemo" */; + buildPhases = ( + 261C188D2073B9C801310C71 /* [CP] Check Pods Manifest.lock */, + 8C5357857E521ECD2E143CFD /* Sources */, + 81E836ED7DAB70A2CBF5FAFE /* Resources */, + 171164B8638E25416B38386C /* CopyFiles */, + 188D3F897881826DDEBE243C /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HippyDemo; + productName = HippyDemo; + productReference = 3A2AA203AF38A01360BEFB2A /* HippyDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 215A0FDFFBD7838975355873 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1530; + }; + buildConfigurationList = BD5ED1CB5E91D88413261758 /* Build configuration list for PBXProject "HippyDemo" */; + compatibilityVersion = "Xcode 11.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = 3F4F18D890009F63DB0F22D2; + productRefGroup = 37AC1BAC994661BA846D4236 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F7DF50CA19E5E79F64D3D4E6 /* HippyDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 81E836ED7DAB70A2CBF5FAFE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9CF888F990C83ECD4F6C78AA /* Assets.xcassets in Resources */, + 4CBD515D378CB81C4C164353 /* DebugCell.xib in Resources */, + 36842E3B36BB6E52267BED0F /* HomePageView.xib in Resources */, + C1A78F8868963E6C790472AA /* PageCreationCell.xib in Resources */, + EBC6C5A3C36A88BF4DAD00BE /* res in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 261C188D2073B9C801310C71 /* [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-HippyDemo-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; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8C5357857E521ECD2E143CFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 291382C2783D93A2C7BD4ACB /* AppDelegate.m in Sources */, + 369577C613161936EE28F52E /* DebugCell.m in Sources */, + 5A5A9B087B27BD97E132C49E /* DemoBaseViewController.m in Sources */, + 5A60628334CE16EA4F8B1329 /* DemoNavigationViewController.m in Sources */, + B5E0049F9941EA38E80EC858 /* HippyDemoLoader.mm in Sources */, + A3F629CA4C94D65F42BB4B33 /* HippyDemoViewController.mm in Sources */, + D026B185E903AE8F958F8588 /* HippyPageCache.m in Sources */, + DF04004944D28CE1569D68EC /* HippyPageCacheContainerView.m in Sources */, + 994E84462AD481034728A29D /* HippyPageCacheView.m in Sources */, + C94B7BB8A618F36A1F58C270 /* HomePageViewController.mm in Sources */, + F67849BC0665859B70497D0E /* IconUtils.m in Sources */, + 9C99A05896359E49AE9A8352 /* MyView.m in Sources */, + FD3BE72F8A086DC9A9484195 /* MyViewManager.mm in Sources */, + DE19BAD300150C5D3D76D7F7 /* PageCreationCell.m in Sources */, + 8C009766BC90CAF781042636 /* PageCreationViewController.m in Sources */, + DCCEB3FED1802C0D9C21AB11 /* PageManagerViewController.m in Sources */, + F3F6D205C6FBA36CB262A28E /* SettingsInfo.m in Sources */, + 380469787621147052854E8F /* SettingsViewController.m in Sources */, + B8AA2B793BE43710EFC9D4DC /* TestModule.mm in Sources */, + 74C51526435B1AB788CBF991 /* TurboBaseModule.mm in Sources */, + 72328C3389BA8DD5E54D7D5F /* TurboConfig.m in Sources */, + BD5196B65EE0B712FDA2EB9B /* UIViewController+Title.m in Sources */, + 16D159EB8DCA407DFE4008C8 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A19596015FBB918148A61FF4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DB34DDF95B8E9C9AD87FFE4 /* Pods-HippyDemo.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = HippyDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "${inherited}"; + PRODUCT_BUNDLE_IDENTIFIER = com.tencent.HippyDemo2.db; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BDE0917765AAB8F06FED1216 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + EDA31BB6FAF111A6FD9A3EC9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F2EFFA7A058FDD9A908F1C9C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 331F0D867F0DF450A84E4587 /* Pods-HippyDemo.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = HippyDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "${inherited}"; + PRODUCT_BUNDLE_IDENTIFIER = com.tencent.HippyDemo2.db; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 20BB0794C4C1CBC0A34760F4 /* Build configuration list for PBXNativeTarget "HippyDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2EFFA7A058FDD9A908F1C9C /* Debug */, + A19596015FBB918148A61FF4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + BD5ED1CB5E91D88413261758 /* Build configuration list for PBXProject "HippyDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDE0917765AAB8F06FED1216 /* Debug */, + EDA31BB6FAF111A6FD9A3EC9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 215A0FDFFBD7838975355873 /* Project object */; +} diff --git a/framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..919434a6254 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme b/framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme new file mode 100644 index 00000000000..d063a92dfa1 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xcodeproj/xcshareddata/xcschemes/HippyDemo.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/examples/ios-demo/HippyDemo.xctestplan b/framework/examples/ios-demo/HippyDemo.xctestplan new file mode 100644 index 00000000000..9ab53771413 --- /dev/null +++ b/framework/examples/ios-demo/HippyDemo.xctestplan @@ -0,0 +1,22 @@ +{ + "configurations" : [ + { + "id" : "C642C4E4-3340-4058-BCDB-F8D3BE31926A", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "targetForVariableExpansion" : { + "containerPath" : "container:HippyDemo.xcodeproj", + "identifier" : "F7DF50CA19E5E79F64D3D4E6", + "name" : "HippyDemo" + } + }, + "testTargets" : [ + + ], + "version" : 1 +} diff --git a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm index 43481a05867..e4bb6087b5e 100644 --- a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm +++ b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm @@ -198,7 +198,9 @@ - (void)mountConnector:(HippyBridge *)hippyBridge { } else { NSURL *vendorBundleURL = [self vendorBundleURL]; NSURL *indexBundleURL = [self indexBundleURL]; - [hippyBridge loadBundleURL:vendorBundleURL completion:^(NSURL * _Nullable, NSError * _Nullable) { + [hippyBridge loadBundleURL:vendorBundleURL + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable, NSError * _Nullable) { NSLog(@"url %@ load finish", vendorBundleURL); }]; hippyBridge.sandboxDirectory = [indexBundleURL URLByDeletingLastPathComponent]; @@ -261,10 +263,6 @@ - (BOOL)isDebugMode { return _isDebugMode; } -- (void)reload:(HippyBridge *)bridge { - [self mountConnector:_hippyBridge]; -} - - (void)removeRootView:(NSNumber *)rootTag bridge:(HippyBridge *)bridge { [[[self.contentAreaView subviews] firstObject] removeFromSuperview]; } diff --git a/framework/examples/ios-demo/HippyDemo/TestModule.mm b/framework/examples/ios-demo/HippyDemo/TestModule.mm index 04879102526..69a0e711364 100644 --- a/framework/examples/ios-demo/HippyDemo/TestModule.mm +++ b/framework/examples/ios-demo/HippyDemo/TestModule.mm @@ -95,7 +95,9 @@ - (void)mountConnector:(HippyBridge *)connector onView:(UIView *)view { rootView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; [_connector setRootView:rootView]; NSNumber *rootTag = [rootView hippyTag]; - [connector loadBundleURL:bundleUrl completion:^(NSURL * _Nullable, NSError * _Nullable) { + [connector loadBundleURL:bundleUrl + bundleType:HippyBridgeBundleTypeBusiness + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) { NSLog(@"url %@ load finish", bundleStr); [connector loadInstanceForRootView:rootTag withProperties:@{@"isSimulator": @(isSimulator)}]; }]; diff --git a/framework/examples/ios-demo/README.md b/framework/examples/ios-demo/README.md index 136ba04ba62..a6f68621c51 100644 --- a/framework/examples/ios-demo/README.md +++ b/framework/examples/ios-demo/README.md @@ -1,9 +1,11 @@ -> You need to install cocoapods, cmake and xcodegen before starting HippyDemo +# iOS Demo Running Guide + +> You need to install cocoapods and cmake before starting HippyDemo 1.open your Terminal 2.change the current working folder to framework/examples/ios-demo -3.execute 'xcodegen' command to install dependencies +3.execute 'pod install' command to install dependencies and generate 'HippyDemo.xcworkspace' -4.open 'HippyDemo.xcworkspace' \ No newline at end of file +4.open 'HippyDemo.xcworkspace', run demo! diff --git a/framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan b/framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan new file mode 100644 index 00000000000..a32bdc00674 --- /dev/null +++ b/framework/examples/ios-demo/hippy-Unit-UnitTests.xctestplan @@ -0,0 +1,32 @@ +{ + "configurations" : [ + { + "id" : "EA4E4CF7-9024-4B4A-B911-A0056EF7BAD7", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : { + "targets" : [ + { + "containerPath" : "container:Pods.xcodeproj", + "identifier" : "927329EF8F33B34F7F9A0655F6ABA7C3", + "name" : "hippy" + } + ] + } + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:Pods.xcodeproj", + "identifier" : "C1E172B01D45B4ADDB866126B685CA58", + "name" : "hippy-Unit-UnitTests" + } + } + ], + "version" : 1 +} diff --git a/framework/examples/ios-demo/project.yml b/framework/examples/ios-demo/project.yml deleted file mode 100644 index 5cce0a47c01..00000000000 --- a/framework/examples/ios-demo/project.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: HippyDemo -options: - createIntermediateGroups: true - bundleIdPrefix: "com.tencent" - xcodeVersion: "10.0" - postGenCommand: pod update -targets: - HippyDemo: - type: application - platform: iOS - deploymentTarget: "11.0" - settings: - OTHER_LDFLAGS: "${inherited}" - TARGETED_DEVICE_FAMILY: "1" - CLANG_CXX_LANGUAGE_STANDARD: "c++17" - PRODUCT_BUNDLE_IDENTIFIER: "com.tencent.HippyDemo2.db" - sources: - - path: HippyDemo - - path: Assets.xcassets - - path: fonts - buildPhase: - copyFiles: - destination: "resources" - - path: res - type: "folder" diff --git a/framework/ios/base/HippyKeyCommands.h b/framework/ios/base/HippyKeyCommands.h index 1ccdd4b6f39..1ca74362dec 100644 --- a/framework/ios/base/HippyKeyCommands.h +++ b/framework/ios/base/HippyKeyCommands.h @@ -29,7 +29,9 @@ /** * Register a single-press keyboard command. */ -- (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *command))block; +- (void)registerKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *command))block; /** * Unregister a single-press keyboard command. diff --git a/framework/ios/base/HippyKeyCommands.m b/framework/ios/base/HippyKeyCommands.m index e7a33ee1f17..728999066c1 100644 --- a/framework/ios/base/HippyKeyCommands.m +++ b/framework/ios/base/HippyKeyCommands.m @@ -25,12 +25,21 @@ #import "HippyAssert.h" #import "HippyDefines.h" #import "HippyUtils.h" +#import "HippyRootView.h" +#import +#import #if HIPPY_DEV -static BOOL HippyIsIOS8OrEarlier() { - return [UIDevice currentDevice].systemVersion.floatValue < 9; -} +@interface UIEvent (UIPhysicalKeyboardEvent) + +@property (nonatomic) NSString *_modifiedInput; +@property (nonatomic) NSString *_unmodifiedInput; +@property (nonatomic) UIKeyModifierFlags _modifierFlags; +@property (nonatomic) BOOL _isKeyDown; +@property (nonatomic) long _keyCode; + +@end @interface HippyKeyCommand : NSObject @@ -49,6 +58,8 @@ - (instancetype)initWithKeyCommand:(UIKeyCommand *)keyCommand block:(void (^)(UI return self; } +HIPPY_NOT_IMPLEMENTED(-(instancetype)init) + - (id)copyWithZone:(__unused NSZone *)zone { return self; } @@ -65,147 +76,114 @@ - (BOOL)isEqual:(HippyKeyCommand *)object { } - (BOOL)matchesInput:(NSString *)input flags:(UIKeyModifierFlags)flags { - return [_keyCommand.input isEqual:input] && _keyCommand.modifierFlags == flags; + return [_keyCommand.input isEqual:input] && (_keyCommand.modifierFlags == flags || flags == 0); } - (NSString *)description { - return [NSString stringWithFormat:@"<%@:%p input=\"%@\" flags=%zd hasBlock=%@>", [self class], self, _keyCommand.input, - (long)_keyCommand.modifierFlags, _block ? @"YES" : @"NO"]; + return [NSString stringWithFormat:@"<%@:%p input=\"%@\" flags=%zd hasBlock=%@>", + [self class], self, _keyCommand.input, (long)_keyCommand.modifierFlags, _block ? @"YES" : @"NO"]; } @end + +#pragma mark - + @interface HippyKeyCommands () @property (nonatomic, strong) NSMutableSet *commands; @end -@implementation UIResponder (HippyKeyCommands) - -+ (UIResponder *)hippy_getFirstResponder:(UIResponder *)view { - UIResponder *firstResponder = nil; - - if (view.isFirstResponder) { - return view; - } else if ([view isKindOfClass:[UIViewController class]]) { - if ([(UIViewController *)view parentViewController]) { - firstResponder = [UIResponder hippy_getFirstResponder:[(UIViewController *)view parentViewController]]; - } - return firstResponder ? firstResponder : [UIResponder hippy_getFirstResponder:[(UIViewController *)view view]]; - } else if ([view isKindOfClass:[UIView class]]) { - for (UIView *subview in [(UIView *)view subviews]) { - firstResponder = [UIResponder hippy_getFirstResponder:subview]; - if (firstResponder) { - return firstResponder; - } - } - } - return firstResponder; -} +@implementation HippyKeyCommands -- (NSArray *)hippy_keyCommands { - NSSet *commands = [HippyKeyCommands sharedInstance].commands; - return [[commands valueForKeyPath:@"keyCommand"] allObjects]; ++ (void)initialize { + SEL originalKeyEventSelector = NSSelectorFromString(@"handleKeyUIEvent:"); + SEL swizzledKeyEventSelector = NSSelectorFromString( + [NSString stringWithFormat:@"_hippy_swizzle_%x_%@", + arc4random(), NSStringFromSelector(originalKeyEventSelector)]); + + void (^handleKeyUIEventSwizzleBlock)(UIApplication *, UIEvent *) = ^(UIApplication *slf, UIEvent *event) { + [[[self class] sharedInstance] handleKeyUIEventSwizzle:event]; + ((void (*)(id, SEL, id))objc_msgSend)(slf, swizzledKeyEventSelector, event); + }; + + HippySwapInstanceMethodWithBlock([UIApplication class], originalKeyEventSelector, + handleKeyUIEventSwizzleBlock, swizzledKeyEventSelector); } -/** - * Single Press Key Command Response - * Command + KeyEvent (Command + R/D, etc.) - */ -- (void)hippy_handleKeyCommand:(UIKeyCommand *)key { - // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: - // method gets called repeatedly if the command key is held down. - static NSTimeInterval lastCommand = 0; - if (HippyIsIOS8OrEarlier() || CACurrentMediaTime() - lastCommand > 0.5) { - for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands.allObjects) { // add by stockGroup - if ([command.keyCommand.input isEqualToString:key.input] && command.keyCommand.modifierFlags == key.modifierFlags) { - if (command.block) { - command.block(key); - lastCommand = CACurrentMediaTime(); - } - } - } +- (void)handleKeyUIEventSwizzle:(UIEvent *)event { + NSString *modifiedInput = nil; + UIKeyModifierFlags modifierFlags = 0; + BOOL isKeyDown = NO; + + if ([event respondsToSelector:@selector(_modifiedInput)]) { + modifiedInput = [event _modifiedInput]; } -} - -/** - * Double Press Key Command Response - * Double KeyEvent (Double R, etc.) - */ -- (void)hippy_handleDoublePressKeyCommand:(UIKeyCommand *)key { - static BOOL firstPress = YES; - static NSTimeInterval lastCommand = 0; - static NSTimeInterval lastDoubleCommand = 0; - static NSString *lastInput = nil; - static UIKeyModifierFlags lastModifierFlags = 0; - - if (firstPress) { - for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands.allObjects) { // add by stockGroup - if ([command.keyCommand.input isEqualToString:key.input] && command.keyCommand.modifierFlags == key.modifierFlags && command.block) { - firstPress = NO; - lastCommand = CACurrentMediaTime(); - lastInput = key.input; - lastModifierFlags = key.modifierFlags; - return; + + if ([event respondsToSelector:@selector(_modifierFlags)]) { + modifierFlags = [event _modifierFlags]; + } + + if ([event respondsToSelector:@selector(_isKeyDown)]) { + isKeyDown = [event _isKeyDown]; + } + + BOOL interactionEnabled = !UIApplication.sharedApplication.isIgnoringInteractionEvents; + BOOL hasFirstResponder = NO; + if (isKeyDown && modifiedInput.length > 0 && interactionEnabled) { + UIResponder *firstResponder = nil; + for (UIWindow *window in [self allWindows]) { + firstResponder = [window valueForKey:@"firstResponder"]; + if (firstResponder) { + hasFirstResponder = YES; + break; } } - } else { - // Second keyevent within 0.2 second, - // with the same key as the first one. - if (CACurrentMediaTime() - lastCommand < 0.2 && lastInput == key.input && lastModifierFlags == key.modifierFlags) { - for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands.allObjects) { // add by stockGroup - if ([command.keyCommand.input isEqualToString:key.input] && command.keyCommand.modifierFlags == key.modifierFlags && command.block) { - // NOTE: throttle the key handler because on iOS 9 the handleKeyCommand: - // method gets called repeatedly if the command key is held down. - if (HippyIsIOS8OrEarlier() || CACurrentMediaTime() - lastDoubleCommand > 0.5) { - command.block(key); - lastDoubleCommand = CACurrentMediaTime(); - } - firstPress = YES; - return; - } - } + + // Ignore key commands (except escape) when there's an active responder + if (!firstResponder || [firstResponder isKindOfClass:HippyRootView.class]) { + [self hippy_handleKeyCommand:modifiedInput flags:modifierFlags]; } - - lastCommand = CACurrentMediaTime(); - lastInput = key.input; - lastModifierFlags = key.modifierFlags; } +}; + +- (NSArray *)allWindows { + BOOL includeInternalWindows = YES; + BOOL onlyVisibleWindows = NO; + + // Obfuscating selector allWindowsIncludingInternalWindows:onlyVisibleWindows: + NSArray *allWindowsComponents = + @[ @"al", @"lWindo", @"wsIncl", @"udingInt", @"ernalWin", @"dows:o", @"nlyVisi", @"bleWin", @"dows:" ]; + SEL allWindowsSelector = NSSelectorFromString([allWindowsComponents componentsJoinedByString:@""]); + + NSMethodSignature *methodSignature = [[UIWindow class] methodSignatureForSelector:allWindowsSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + + invocation.target = [UIWindow class]; + invocation.selector = allWindowsSelector; + [invocation setArgument:&includeInternalWindows atIndex:2]; + [invocation setArgument:&onlyVisibleWindows atIndex:3]; + [invocation invoke]; + + __unsafe_unretained NSArray *windows = nil; + [invocation getReturnValue:&windows]; + return windows; } -@end - -@implementation UIApplication (HippyKeyCommands) - -// Required for iOS 8.x -- (BOOL)hippy_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event { - if (action == @selector(hippy_handleKeyCommand:)) { - [self hippy_handleKeyCommand:sender]; - return YES; - } else if (action == @selector(hippy_handleDoublePressKeyCommand:)) { - [self hippy_handleDoublePressKeyCommand:sender]; - return YES; +- (void)hippy_handleKeyCommand:(NSString *)input flags:(UIKeyModifierFlags)modifierFlags { + for (HippyKeyCommand *command in [HippyKeyCommands sharedInstance].commands) { + if ([command matchesInput:input flags:modifierFlags]) { + if (command.block) { + command.block(nil); + } + } } - return [self hippy_sendAction:action to:target from:sender forEvent:event]; } -@end - -@implementation HippyKeyCommands -+ (void)initialize { - if (HippyIsIOS8OrEarlier()) { - // swizzle UIApplication - HippySwapInstanceMethods([UIApplication class], @selector(keyCommands), @selector(hippy_keyCommands)); - - HippySwapInstanceMethods([UIApplication class], @selector(sendAction:to:from:forEvent:), @selector(hippy_sendAction:to:from:forEvent:)); - } else { - // swizzle UIResponder - HippySwapInstanceMethods([UIResponder class], @selector(keyCommands), @selector(hippy_keyCommands)); - } -} +#pragma mark - + (instancetype)sharedInstance { static HippyKeyCommands *sharedInstance; @@ -213,7 +191,7 @@ + (instancetype)sharedInstance { dispatch_once(&onceToken, ^{ sharedInstance = [self new]; }); - + return sharedInstance; } @@ -224,19 +202,12 @@ - (instancetype)init { return self; } -- (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *))block { +- (void)registerKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *))block { HippyAssertMainQueue(); - - if (input.length && flags && HippyIsIOS8OrEarlier()) { - // Workaround around the first cmd not working: http://openradar.appspot.com/19613391 - // You can register just the cmd key and do nothing. This ensures that - // command-key modified commands will work first time. Fixed in iOS 9. - - [self registerKeyCommandWithInput:@"" modifierFlags:flags action:nil]; - } - - UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(hippy_handleKeyCommand:)]; - + + UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(description)]; HippyKeyCommand *keyCommand = [[HippyKeyCommand alloc] initWithKeyCommand:command block:block]; [_commands removeObject:keyCommand]; [_commands addObject:keyCommand]; @@ -244,7 +215,7 @@ - (void)registerKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifi - (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { [_commands removeObject:command]; @@ -255,8 +226,8 @@ - (void)unregisterKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModi - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - - for (HippyKeyCommand *command in _commands.allObjects) { // add by stockGroup + + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { return YES; } @@ -264,19 +235,12 @@ - (BOOL)isKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyMod return NO; } -- (void)registerDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags action:(void (^)(UIKeyCommand *))block { +- (void)registerDoublePressKeyCommandWithInput:(NSString *)input + modifierFlags:(UIKeyModifierFlags)flags + action:(void (^)(UIKeyCommand *))block { HippyAssertMainQueue(); - - if (input.length && flags && HippyIsIOS8OrEarlier()) { - // Workaround around the first cmd not working: http://openradar.appspot.com/19613391 - // You can register just the cmd key and do nothing. This ensures that - // command-key modified commands will work first time. Fixed in iOS 9. - - [self registerDoublePressKeyCommandWithInput:@"" modifierFlags:flags action:nil]; - } - - UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(hippy_handleDoublePressKeyCommand:)]; - + + UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input modifierFlags:flags action:@selector(description)]; HippyKeyCommand *keyCommand = [[HippyKeyCommand alloc] initWithKeyCommand:command block:block]; [_commands removeObject:keyCommand]; [_commands addObject:keyCommand]; @@ -284,7 +248,7 @@ - (void)registerDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:( - (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { [_commands removeObject:command]; @@ -295,8 +259,8 @@ - (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input modifierFlags - (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)flags { HippyAssertMainQueue(); - - for (HippyKeyCommand *command in _commands.allObjects) { // add by stockGroup + + for (HippyKeyCommand *command in _commands.allObjects) { if ([command matchesInput:input flags:flags]) { return YES; } @@ -319,10 +283,12 @@ - (void)registerKeyCommandWithInput:(__unused NSString *)input action:(__unused void (^)(UIKeyCommand *))block { } -- (void)unregisterKeyCommandWithInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (void)unregisterKeyCommandWithInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { } -- (BOOL)isKeyCommandRegisteredForInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (BOOL)isKeyCommandRegisteredForInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { return NO; } @@ -331,10 +297,12 @@ - (void)registerDoublePressKeyCommandWithInput:(__unused NSString *)input action:(__unused void (^)(UIKeyCommand *))block { } -- (void)unregisterDoublePressKeyCommandWithInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (void)unregisterDoublePressKeyCommandWithInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { } -- (BOOL)isDoublePressKeyCommandRegisteredForInput:(__unused NSString *)input modifierFlags:(__unused UIKeyModifierFlags)flags { +- (BOOL)isDoublePressKeyCommandRegisteredForInput:(__unused NSString *)input + modifierFlags:(__unused UIKeyModifierFlags)flags { return NO; } diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index 9b37f2a9ffd..d5697ad44db 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -42,20 +42,88 @@ NS_ASSUME_NONNULL_BEGIN * 注意:为兼容2.0版本,保持的相同的下划线前缀命名,不可修改 */ HIPPY_EXTERN NSString *const _HippySDKVersion; + /** * This notification triggers a reload of all bridges currently running. * Deprecated, use HippyBridge::requestReload instead. */ HIPPY_EXTERN NSString *const HippyReloadNotification; + +// Keys of userInfo for the following notifications +HIPPY_EXTERN NSString *const kHippyNotiBridgeKey; +HIPPY_EXTERN NSString *const kHippyNotiBundleUrlKey; +HIPPY_EXTERN NSString *const kHippyNotiBundleTypeKey; +HIPPY_EXTERN NSString *const kHippyNotiErrorKey; + +/// Bundle Type of Vendor (or Common Bundle), +/// used in kHippyNotiBundleTypeKey +HIPPY_EXTERN const NSUInteger HippyBridgeBundleTypeVendor; +/// Bundle Type Business, +/// used in kHippyNotiBundleTypeKey +HIPPY_EXTERN const NSUInteger HippyBridgeBundleTypeBusiness; + +/** + * This notification fires when the bridge starts loading and executing the JS bundle. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * } + * + * 备注:bundle包开始加载的通知, 注意与Hippy2不同的是,不仅指代`Common包`,`Business包`同样会发送该通知, + * 可通过userInfo中bundleType参数进行区分,see: HippyBridgeBundleTypeVendor + */ +HIPPY_EXTERN NSString *const HippyJavaScriptWillStartLoadingNotification; + +/** + * This notification fires when bridge has fetched JS bundle's source code. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * kHippyNotiErrorKey : $(error), // NSError object + * } + * + * 备注:获取到Bundle包的source code data时的通知 + */ +HIPPY_EXTERN NSString *const HippyJavaScripDidLoadSourceCodeNotification; + /** * This notification fires when the bridge has finished loading the JS bundle. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * } + * + * 备注:Bundle包`加载和执行`结束的通知 */ HIPPY_EXTERN NSString *const HippyJavaScriptDidLoadNotification; /** * This notification fires when the bridge failed to load the JS bundle. The * `error` key can be used to determine the error that occured. + * @discussion + * Notification.object: instance of HippyBridge + * Notification.userInfo: + * @{ + * kHippyNotiBridgeKey : $(instance of HippyBridge), + * kHippyNotiBundleUrlKey : $(bundleURL), + * kHippyNotiBundleTypeKey : $(bundleType), + * kHippyNotiErrorKey : $(error), // NSError object + * } + * + * 备注:Bundle包`加载和执行`失败的通知 */ HIPPY_EXTERN NSString *const HippyJavaScriptDidFailToLoadNotification; @@ -74,6 +142,8 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); +#pragma mark - + /// Async bridge used to communicate with the JavaScript application. @interface HippyBridge : NSObject @@ -94,7 +164,7 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); /// /// Note: 多个bridge使用相同的共享engineKey时,只有全部bridge实例销毁时engine资源才将释放,因此,请注意合理使用,避免出现意外的内存泄漏。 /// 传空时默认不共享,SDK内部默认分配一随机key。 -- (instancetype)initWithDelegate:(id)delegate +- (instancetype)initWithDelegate:(nullable id)delegate moduleProvider:(nullable HippyBridgeModuleProviderBlock)block launchOptions:(nullable NSDictionary *)launchOptions executorKey:(nullable NSString *)executorKey; @@ -112,7 +182,7 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); /// /// Note: 多个bridge使用相同的共享engineKey时,只有全部bridge实例销毁时engine资源才将释放,因此,请注意合理使用,避免出现意外的内存泄漏。 /// 传空时默认不共享,SDK内部默认分配一随机key。 -- (instancetype)initWithDelegate:(id)delegate +- (instancetype)initWithDelegate:(nullable id)delegate bundleURL:(nullable NSURL *)bundleURL moduleProvider:(nullable HippyBridgeModuleProviderBlock)block launchOptions:(nullable NSDictionary *)launchOptions @@ -143,14 +213,6 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); */ @property (nonatomic, strong, readonly) NSURL *debugURL; -/** - * Load js bundles from urls - * - * @param bundleURL bundles url - * @discussion HippyBridge makes sure bundles will be loaded in order. - */ -- (void)loadBundleURL:(NSURL *)bundleURL - completion:(void (^_Nullable)(NSURL * _Nullable, NSError * _Nullable))completion; #pragma mark - Image Related @@ -170,12 +232,12 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); /// - Parameter imageLoader: id - (void)setCustomImageLoader:(id)imageLoader; -/** - * Image provider method - * Users adds or obtains image providers in the following methods - */ +/// Get all classes that confirms to HippyImageProviderProtocol +@property (nonatomic, strong, nonnull, readonly) NSArray> *imageProviders; + +/// Add a custom ImageProvider class. +/// - Parameter cls: class confirms to HippyImageProviderProtocol - (void)addImageProviderClass:(Class)cls; -- (NSArray> *)imageProviderClasses; #pragma mark - @@ -338,6 +400,29 @@ HIPPY_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); - (void)setOSNightMode:(BOOL)isOSNightMode withRootViewTag:(NSNumber *)rootViewTag; + +#pragma mark - Advanced Usages + +/* 说明: + * 以下方法一般情况下无需调用,仅供高级定制化使用。 + * Following methods are only used for advanced customization, no need to be invoked in general. + */ + +typedef NSUInteger HippyBridgeBundleType; +typedef void (^HippyBridgeBundleLoadCompletionBlock)(NSURL * _Nullable bundleURL, NSError * _Nullable error); + +/// Load and Execute bundle from the given bundle URL +/// - Parameters: +/// - bundleURL: bundle url +/// - bundleType: type of bundle, e.g.: whether is `Vendor Bundle`(Common Bundle) or `Business Bundle` +/// - completion: Completion block +/// +/// - Disscusion: HippyBridge makes sure bundles will be loaded and execute in order. +- (void)loadBundleURL:(NSURL *)bundleURL + bundleType:(HippyBridgeBundleType)bundleType + completion:(HippyBridgeBundleLoadCompletionBlock)completion; + + @end diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index c6b6b166692..d175747632a 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -79,11 +79,22 @@ #include "devtools/devtools_data_source.h" #endif + +NSString *const _HippySDKVersion = @HIPPY_STR(HIPPY_VERSION); NSString *const HippyReloadNotification = @"HippyReloadNotification"; +NSString *const HippyJavaScriptWillStartLoadingNotification = @"HippyJavaScriptWillStartLoadingNotification"; +NSString *const HippyJavaScripDidLoadSourceCodeNotification = @"HippyJavaScripDidLoadSourceCodeNotification"; NSString *const HippyJavaScriptDidLoadNotification = @"HippyJavaScriptDidLoadNotification"; NSString *const HippyJavaScriptDidFailToLoadNotification = @"HippyJavaScriptDidFailToLoadNotification"; NSString *const HippyDidInitializeModuleNotification = @"HippyDidInitializeModuleNotification"; -NSString *const _HippySDKVersion = @HIPPY_STR(HIPPY_VERSION); + +NSString *const kHippyNotiBridgeKey = @"bridge"; +NSString *const kHippyNotiBundleUrlKey = @"bundleURL"; +NSString *const kHippyNotiBundleTypeKey = @"bundleType"; +NSString *const kHippyNotiErrorKey = @"error"; + +const NSUInteger HippyBridgeBundleTypeVendor = 1; +const NSUInteger HippyBridgeBundleTypeBusiness = 2; static NSString *const HippyNativeGlobalKeyOS = @"OS"; @@ -133,7 +144,6 @@ static inline void registerLogDelegateToHippyCore() { @interface HippyBridge() { - NSMutableArray> *_imageProviders; __weak id _methodInterceptor; HippyModulesSetup *_moduleSetup; __weak NSOperation *_lastOperation; @@ -176,6 +186,7 @@ @implementation HippyBridge @synthesize renderManager = _renderManager; @synthesize imageLoader = _imageLoader; +@synthesize imageProviders = _imageProviders; dispatch_queue_t HippyJSThread; @@ -371,25 +382,6 @@ - (HippyModuleData *)moduleDataForName:(NSString *)moduleName { return nil; } -- (void)addImageProviderClass:(Class)cls { - HippyAssertParam(cls); - @synchronized (self) { - if (!_imageProviders) { - _imageProviders = [NSMutableArray array]; - } - [_imageProviders addObject:cls]; - } -} - -- (NSArray> *)imageProviderClasses { - @synchronized (self) { - if (!_imageProviders) { - _imageProviders = [NSMutableArray array]; - } - return [_imageProviders copy]; - } -} - - (NSArray *)modulesConformingToProtocol:(Protocol *)protocol { NSMutableArray *modules = [NSMutableArray new]; for (Class moduleClass in self.moduleClasses) { @@ -432,17 +424,36 @@ - (void)setCustomImageLoader:(id)imageLoader { } } +- (NSArray> *)imageProviders { + @synchronized (self) { + if (!_imageProviders) { + NSMutableArray *moduleClasses = [NSMutableArray new]; + for (Class moduleClass in self.moduleClasses) { + if ([moduleClass conformsToProtocol:@protocol(HippyImageProviderProtocol)]) { + [moduleClasses addObject:moduleClass]; + } + } + _imageProviders = moduleClasses; + } + return [_imageProviders copy]; + } +} + +- (void)addImageProviderClass:(Class)cls { + HippyAssertParam(cls); + @synchronized (self) { + _imageProviders = [self.imageProviders arrayByAddingObject:cls]; + } +} #pragma mark - Debug Reload - (void)reload { - if ([self.delegate respondsToSelector:@selector(reload:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ self.invalidateReason = HippyInvalidateReasonReload; [self invalidate]; [self setUp]; - [self.delegate reload:self]; - self.invalidateReason = HippyInvalidateReasonDealloc; - } + }); } - (void)requestReload { @@ -479,7 +490,7 @@ - (void)setUp { _javaScriptExecutor.contextName = _contextName; } _displayLink = [[HippyDisplayLink alloc] init]; - + // Setup all extra and internal modules [_moduleSetup setupModulesWithCompletionBlock:^{ HippyBridge *strongSelf = weakSelf; @@ -501,36 +512,70 @@ - (void)setUp { /// 加载初始化bridge时传入的Bundle URL - (void)loadPendingVendorBundleURLIfNeeded { if (self.pendingLoadingVendorBundleURL) { - [self loadBundleURL:self.pendingLoadingVendorBundleURL completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + [self loadBundleURL:self.pendingLoadingVendorBundleURL + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) { if (error) { - HippyLogError(@"[Hippy_OC_Log][HippyBridge], bundle loaded error:%@, %@", url, error.description); + HippyLogError(@"[Hippy_OC_Log][HippyBridge], bundle loaded error:%@, %@", bundleURL, error.description); } else { - HippyLogInfo(@"[Hippy_OC_Log][HippyBridge], bundle loaded success:%@", url); + HippyLogInfo(@"[Hippy_OC_Log][HippyBridge], bundle loaded success:%@", bundleURL); } }]; } } +#define BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO \ + @{ kHippyNotiBridgeKey: strongSelf, \ + kHippyNotiBundleUrlKey: bundleURL, \ + kHippyNotiBundleTypeKey : @(bundleType) } + +#define BUNDLE_LOAD_NOTI_ERROR_USER_INFO \ + @{ kHippyNotiBridgeKey: strongSelf, \ + kHippyNotiBundleUrlKey: bundleURL, \ + kHippyNotiBundleTypeKey : @(bundleType), \ + kHippyNotiErrorKey : error } - (void)loadBundleURL:(NSURL *)bundleURL - completion:(void (^_Nullable)(NSURL * _Nullable, NSError * _Nullable))completion { + bundleType:(HippyBridgeBundleType)bundleType + completion:(nonnull HippyBridgeBundleLoadCompletionBlock)completion { if (!bundleURL) { if (completion) { static NSString *bundleError = @"bundle url is nil"; - NSError *error = [NSError errorWithDomain:@"Bridge Bundle Loading Domain" code:1 userInfo:@{NSLocalizedFailureReasonErrorKey: bundleError}]; + NSError *error = [NSError errorWithDomain:@"Bridge Bundle Loading Domain" + code:1 + userInfo:@{NSLocalizedFailureReasonErrorKey: bundleError}]; completion(nil, error); } return; } - HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); + + // bundleURL checking + NSURLComponents *components = [NSURLComponents componentsWithURL:bundleURL resolvingAgainstBaseURL:NO]; + if (components.scheme == nil) { + // If a given url has no scheme, it is considered a file url by default. + components.scheme = @"file"; + bundleURL = components.URL; + } + + HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", + HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), + HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); [_bundleURLs addObject:bundleURL]; + + __weak __typeof(self)weakSelf = self; dispatch_async(HippyBridgeQueue(), ^{ - [self beginLoadingBundle:bundleURL completion:completion]; + __strong __typeof(weakSelf)strongSelf = weakSelf; + NSDictionary *userInfo = BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO; + [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScriptWillStartLoadingNotification + object:strongSelf + userInfo:userInfo]; + [strongSelf beginLoadingBundle:bundleURL bundleType:bundleType completion:completion]; }); } - (void)beginLoadingBundle:(NSURL *)bundleURL - completion:(void (^)(NSURL * _Nullable, NSError * _Nullable))completion { + bundleType:(HippyBridgeBundleType)bundleType + completion:(HippyBridgeBundleLoadCompletionBlock)completion { dispatch_group_t group = dispatch_group_create(); __weak HippyBridge *weakSelf = self; __block NSData *script = nil; @@ -543,23 +588,35 @@ - (void)beginLoadingBundle:(NSURL *)bundleURL bundleURL:bundleURL queue:bundleQueue]; fetchOp.onLoad = ^(NSData *source, NSError *error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if (!strongSelf) { + dispatch_group_leave(group); + return; + } + NSDictionary *userInfo; if (error) { HippyBridgeFatal(error, weakSelf); + userInfo = BUNDLE_LOAD_NOTI_ERROR_USER_INFO; } else { script = source; + userInfo = BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO; } + [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScripDidLoadSourceCodeNotification + object:strongSelf + userInfo:userInfo]; dispatch_group_leave(group); }; dispatch_group_enter(group); HippyBundleExecutionOperation *executeOp = [[HippyBundleExecutionOperation alloc] initWithBlock:^{ - HippyBridge *strongSelf = weakSelf; + __strong __typeof(weakSelf)strongSelf = weakSelf; if (!strongSelf || !strongSelf.valid) { dispatch_group_leave(group); return; } __weak __typeof(strongSelf)weakSelf = strongSelf; [strongSelf executeJSCode:script sourceURL:bundleURL onCompletion:^(id result, NSError *error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; HippyLogInfo(@"End loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); @@ -567,7 +624,6 @@ - (void)beginLoadingBundle:(NSURL *)bundleURL if (completion) { completion(bundleURL, error); } - HippyBridge *strongSelf = weakSelf; if (!strongSelf || !strongSelf.valid) { dispatch_group_leave(group); return; @@ -575,6 +631,25 @@ - (void)beginLoadingBundle:(NSURL *)bundleURL if (error) { HippyBridgeFatal(error, strongSelf); } + __weak __typeof(self)weakSelf = strongSelf; + dispatch_async(dispatch_get_main_queue(), ^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSNotificationName notiName; + NSDictionary *userInfo; + if (error) { + notiName = HippyJavaScriptDidFailToLoadNotification; + userInfo = BUNDLE_LOAD_NOTI_ERROR_USER_INFO; + } else { + notiName = HippyJavaScriptDidLoadNotification; + userInfo = BUNDLE_LOAD_NOTI_SUCCESS_USER_INFO; + } + [[NSNotificationCenter defaultCenter] postNotificationName:notiName + object:strongSelf + userInfo:userInfo]; + }); dispatch_group_leave(group); }]; } queue:bundleQueue]; @@ -658,6 +733,10 @@ - (void)setInspectable:(BOOL)isInspectable { [self.javaScriptExecutor setInspecable:isInspectable]; } + +#pragma mark - Private + +/// Execute JS Bundle - (void)executeJSCode:(NSData *)script sourceURL:(NSURL *)sourceURL onCompletion:(HippyJavaScriptCallback)completion { @@ -680,14 +759,6 @@ - (void)executeJSCode:(NSData *)script if (error) { [strongSelf stopLoadingWithError:error scriptSourceURL:sourceURL]; } - else { - dispatch_async(dispatch_get_main_queue(), ^{ - NSDictionary *userInfo = @{@"bridge": self, sourceURL: sourceURL}; - [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScriptDidLoadNotification - object:self - userInfo:userInfo]; - }); - } completion(result, error); }]; } @@ -706,10 +777,6 @@ - (void)stopLoadingWithError:(NSError *)error scriptSourceURL:(NSURL *)sourceURL } } }]; - NSDictionary *userInfo = @{@"bridge": self, @"error": error, @"sourceURL": sourceURL}; - [[NSNotificationCenter defaultCenter] postNotificationName:HippyJavaScriptDidFailToLoadNotification - object:self - userInfo:userInfo]; if ([error userInfo][HippyJSStackTraceKey]) { [self.redBox showErrorMessage:[error localizedDescription] withStack:[error userInfo][HippyJSStackTraceKey]]; } diff --git a/framework/ios/base/bridge/HippyBridgeDelegate.h b/framework/ios/base/bridge/HippyBridgeDelegate.h index 2453facd476..b3e32225b54 100644 --- a/framework/ios/base/bridge/HippyBridgeDelegate.h +++ b/framework/ios/base/bridge/HippyBridgeDelegate.h @@ -55,14 +55,6 @@ */ - (void)cachedCodeCreated:(NSData *)cachedCode ForBridge:(HippyBridge *)bridge script:(NSString *)script sourceURL:(NSURL *)sourceURL; -//invalidate methods -/** - * Invoke when HippyBridge requests reloading - * - * @param bridge HippyBridge that requests reloading - */ -- (void)reload:(HippyBridge *)bridge; - /** * Tell delegate to remove root node * diff --git a/framework/ios/base/executors/HippyJSExecutor.h b/framework/ios/base/executors/HippyJSExecutor.h index 98c730c74f4..83ea6c17054 100644 --- a/framework/ios/base/executors/HippyJSExecutor.h +++ b/framework/ios/base/executors/HippyJSExecutor.h @@ -78,8 +78,6 @@ HIPPY_EXTERN NSString *const HippyJSCThreadName; */ @property (atomic, assign) std::shared_ptr pScope; -@property (nonatomic, copy) NSString *contextName; - @property(nonatomic, copy) HippyContextCreatedBlock contextCreatedBlock; - (instancetype)initWithEngineKey:(NSString *)engineKey bridge:(HippyBridge *)bridge; diff --git a/framework/ios/base/executors/HippyJSExecutor.mm b/framework/ios/base/executors/HippyJSExecutor.mm index f3b9e55a2fc..21f305e39c6 100644 --- a/framework/ios/base/executors/HippyJSExecutor.mm +++ b/framework/ios/base/executors/HippyJSExecutor.mm @@ -228,9 +228,11 @@ - (void)setup { HippyBridge *bridge = self.bridge; if (bridge && bridge.debugMode) { NSString *wsURL = [self completeWSURLWithBridge:bridge]; - auto workerManager = std::make_shared(1); - auto devtools_data_source = std::make_shared([wsURL UTF8String], workerManager); - self.pScope->SetDevtoolsDataSource(devtools_data_source); + if (wsURL.length > 0) { + auto workerManager = std::make_shared(1); + auto devtools_data_source = std::make_shared([wsURL UTF8String], workerManager); + self.pScope->SetDevtoolsDataSource(devtools_data_source); + } } #endif } @@ -331,9 +333,6 @@ - (SharedCtxValuePtr)JSTurboObjectWithName:(NSString *)name { return obj; } -- (void)setUp { -} - - (void)invalidate { if (!self.isValid) { return; diff --git a/framework/ios/base/modules/HippyModuleMethod.mm b/framework/ios/base/modules/HippyModuleMethod.mm index 0ac4f61637e..2a2ea61d786 100644 --- a/framework/ios/base/modules/HippyModuleMethod.mm +++ b/framework/ios/base/modules/HippyModuleMethod.mm @@ -293,14 +293,26 @@ - (void)processMethodSignature { } } } else if ([typeName isEqualToString:@"HippyResponseSenderBlock"]) { - HIPPY_ARG_BLOCK(if (HIPPY_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) { - HippyLogArgumentError(weakSelf, index, json, "should be a function"); - return NO; - } - __weak HippyBridge *weakBridge = bridge; - Hippy_BLOCK_ARGUMENT(^(NSArray *args) { - enqueueBlockCallback(weakBridge, weakSelf, json, args); - });) + HIPPY_ARG_BLOCK( + if (!json) { + HippyLogArgumentError(weakSelf, index, json, "should be a response sender function"); + return NO; + } + id blockArg = nil; + if (![json isKindOfClass:[NSNumber class]]) { + // In Hippy3.0, Dom Nodes call function by method name directly, + // so it is not a Number anymore. + // See NativeRenderManager::CallFunction() for more. + // TODO: add more type check for safe + blockArg = json; + } else { + __weak HippyBridge *weakBridge = bridge; + blockArg = ^(NSArray *args){ + enqueueBlockCallback(weakBridge, weakSelf, json, args); + }; + } + Hippy_BLOCK_ARGUMENT(blockArg); + ) } else if ([typeName isEqualToString:@"HippyResponseErrorBlock"]) { HIPPY_ARG_BLOCK(if (HIPPY_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) { HippyLogArgumentError(weakSelf, index, json, "should be a function"); @@ -325,6 +337,7 @@ - (void)processMethodSignature { // In Hippy3.0, Dom Nodes call function by method name directly, // so it is not a Number anymore. // See NativeRenderManager::CallFunction() for more. + // TODO: add more type check for safe blockArg = json; } else { __weak HippyBridge *weakBridge = bridge; @@ -464,31 +477,35 @@ - (id)invokeWithBridge:(HippyBridge *)bridge module:(id)module arguments:(NSArra %@ on a module of class %@", [self methodName], [module class]); // Safety check - if (arguments.count != _argumentBlocks.count) { - NSInteger actualCount = arguments.count; - NSInteger expectedCount = _argumentBlocks.count; - - // Subtract the implicit Promise resolver and rejecter functions for implementations of async functions - if (self.functionType == HippyFunctionTypePromise) { - actualCount -= 2; - expectedCount -= 2; + NSInteger actualCount = arguments.count; + NSInteger expectedCount = _argumentBlocks.count; + BOOL isArgumentsMismatch = NO; + if (actualCount > expectedCount) { + isArgumentsMismatch = YES; + } else if (self.functionType == HippyFunctionTypePromise && actualCount < expectedCount - 2) { + for (NSInteger index = actualCount; index < expectedCount - 2; index++) { + id arg = self.arguments[index]; + if (arg.nullability != HippyNullable) { + isArgumentsMismatch = YES; + } } - - HippyLogError(@"%@.%@ was called with %ld arguments, but expects %ld. \ - If you haven\'t changed this method " - @"yourself, this usually means that \ - your versions of the native code and JavaScript code are out " - @"of sync. \ - Updating both should make this error go away.", - HippyBridgeModuleNameForClass(_moduleClass), self.JSMethodName, (long)actualCount, (long)expectedCount); + } + if (isArgumentsMismatch) { + HippyLogError(@"%@.%@ was called with %lld arguments but expects %lld arguments. " + @"If you haven\'t changed this method yourself, this usually means that " + @"your versions of the native code and JavaScript code are out of sync. " + @"Updating both should make this error go away.", + HippyBridgeModuleNameForClass(_moduleClass), + self.JSMethodName, + (long long)actualCount, + (long long)expectedCount); } } // Set arguments NSUInteger index = 0; for (id json in arguments) { - // release模式下,如果前端给的参数多于终端所需参数,那会造成数组越界,引起整个逻辑return。 - //这里做个修改,如果前端给的参数过多,那忽略多余的参数。 + // 如果前端给的参数过多,忽略多余的参数 if ([_argumentBlocks count] <= index) { break; } diff --git a/framework/ios/module/dev/HippyDevMenu.h b/framework/ios/module/dev/HippyDevMenu.h index 3ca48042d49..fbf3c34499c 100644 --- a/framework/ios/module/dev/HippyDevMenu.h +++ b/framework/ios/module/dev/HippyDevMenu.h @@ -69,11 +69,6 @@ */ - (void)reload; -/** - * Deprecated. Use the `-addItem:` method instead. - */ -- (void)addItem:(NSString *)title handler:(void (^)(void))handler DEPRECATED_ATTRIBUTE; - /** * Add custom item to the development menu. The handler will be called * when user selects the item. diff --git a/framework/ios/module/dev/HippyDevMenu.mm b/framework/ios/module/dev/HippyDevMenu.mm index b04786630a2..09369a570d6 100644 --- a/framework/ios/module/dev/HippyDevMenu.mm +++ b/framework/ios/module/dev/HippyDevMenu.mm @@ -109,6 +109,7 @@ - (void)callHandler { @interface HippyDevMenu () { __weak UIAlertController *_actionSheet; + NSMutableArray *_extraMenuItems; NSUserDefaults *_defaults; } @@ -142,38 +143,19 @@ - (instancetype)init { - (void)setBridge:(HippyBridge *)bridge { _bridge = bridge; -#if TARGET_IPHONE_SIMULATOR +#if TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST if (bridge.debugMode) { - __weak HippyDevMenu *weakSelf = self; - HippyKeyCommands *commands = [HippyKeyCommands sharedInstance]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"d" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - + __weak __typeof(self) weakSelf = self; + // Toggle debug menu - [commands registerKeyCommandWithInput:@"e" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"b" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"u" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [weakSelf toggle]; - }]; - - // Toggle debug menu - [commands registerKeyCommandWithInput:@"g" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { + [commands registerKeyCommandWithInput:@"d" + modifierFlags:UIKeyModifierCommand + action:^(__unused UIKeyCommand *command) { [weakSelf toggle]; }]; } -#endif +#endif /* TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST */ } - (dispatch_queue_t)methodQueue { @@ -181,8 +163,7 @@ - (dispatch_queue_t)methodQueue { } - (void)invalidate { - [_actionSheet dismissViewControllerAnimated:YES completion:^(void) { - }]; + [_actionSheet dismissViewControllerAnimated:YES completion:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -204,24 +185,20 @@ - (void)toggle { } } -- (void)addItem:(NSString *)title handler:(void (^)(void))handler { - [self addItem:[HippyDevMenuItem buttonItemWithTitle:title handler:handler]]; -} - -- (void)addItem:(__unused HippyDevMenuItem *)item { - HippyAssert(NO, @"[HippyDevMenu addItem:]方法没有实现,怎么没问题?"); +- (void)addItem:(HippyDevMenuItem *)item { + [_extraMenuItems addObject:item]; } - (NSArray *)menuItems { NSMutableArray *items = [NSMutableArray new]; // Add built-in items - __weak HippyDevMenu *weakSelf = self; - [items addObject:[HippyDevMenuItem buttonItemWithTitle:@"Reload" handler:^{ [weakSelf reload]; }]]; + // Add extra items + [items addObjectsFromArray:_extraMenuItems]; return items; } @@ -236,11 +213,11 @@ - (void)addItem:(__unused HippyDevMenuItem *)item { NSString *title = [NSString stringWithFormat:@"Hippy: Development (%@)", [_bridge moduleName]]; // On larger devices we don't have an anchor point for the action sheet - UIAlertControllerStyle style = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone ? UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert; - UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:title - message:@"" - preferredStyle:style]; - _actionSheet = actionSheet; + UIAlertControllerStyle style = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone ? + UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert; + _actionSheet = [UIAlertController alertControllerWithTitle:title + message:nil + preferredStyle:style]; NSArray *items = [self menuItems]; for (HippyDevMenuItem *item in items) { @@ -264,7 +241,7 @@ - (void)addItem:(__unused HippyDevMenuItem *)item { handler:^(__unused UIAlertAction *action) { }]]; - [HippyPresentedViewController() presentViewController:_actionSheet animated:YES completion:^(void){}]; + [HippyPresentedViewController() presentViewController:_actionSheet animated:YES completion:nil]; } @end diff --git a/framework/ios/module/imageloader/HippyImageLoaderModule.mm b/framework/ios/module/imageloader/HippyImageLoaderModule.mm index a908df9ad1f..37926663ad8 100644 --- a/framework/ios/module/imageloader/HippyImageLoaderModule.mm +++ b/framework/ios/module/imageloader/HippyImageLoaderModule.mm @@ -47,7 +47,7 @@ @implementation HippyImageLoaderModule @synthesize bridge = _bridge; - (id)imageProviderForData:(NSData *)data { - NSArray> *providers = [self.bridge imageProviderClasses]; + NSArray> *providers = [self.bridge imageProviders]; for (Class cls in providers) { if ([cls canHandleData:data]) { id object = [[(Class)cls alloc] init]; diff --git a/framework/ios/module/turbo/HippyOCTurboModule.mm b/framework/ios/module/turbo/HippyOCTurboModule.mm index 840f7282316..0741570dd92 100644 --- a/framework/ios/module/turbo/HippyOCTurboModule.mm +++ b/framework/ios/module/turbo/HippyOCTurboModule.mm @@ -315,7 +315,7 @@ static id convertJSIObjectToNSObject(const std::shared_ptr &co } std::u16string u16Key = StringViewUtils::ConvertEncoding(string_view, string_view::Encoding::Utf16).utf16_value(); NSString *stringKey = [NSString stringWithCharacters:(const unichar*)u16Key.c_str() length:(u16Key.length())]; - id objValue = convertCtxValueToObjcObject(context, value, module); + id objValue = convertCtxValueToObjcObject(context, value, module) ?: [NSNull null]; [result setObject:objValue forKey:stringKey]; } return [result copy]; diff --git a/hippy.podspec b/hippy.podspec index 1d5142f4c90..3b48f1bbab1 100644 --- a/hippy.podspec +++ b/hippy.podspec @@ -167,6 +167,7 @@ Pod::Spec.new do |s| 'GCC_ENABLE_CPP_EXCEPTIONS' => false, 'GCC_ENABLE_CPP_RTTI' => false, } + iosvfs.dependency 'hippy/Base' iosvfs.dependency 'hippy/VFS' iosvfs.dependency 'hippy/Footstone' iosvfs.dependency 'hippy/FootstoneUtils' diff --git a/modules/footstone/include/footstone/logging.h b/modules/footstone/include/footstone/logging.h index dcf4a9cdfb0..9c0bc6eb469 100644 --- a/modules/footstone/include/footstone/logging.h +++ b/modules/footstone/include/footstone/logging.h @@ -108,6 +108,7 @@ class LogMessage { } delegate_ = delegate; } + inline static void LogWithFormat(const char * file, int line, const char *format, ...){ char *log_msg = NULL; va_list args; @@ -124,9 +125,9 @@ class LogMessage { <<"[thread:"< 0 ? @(lineNumber) : nil, message); + logFunction(level, HippyLogSourceNative, fileName ? @(fileName) : nil, @(lineNumber), message); } #if HIPPY_DEBUG diff --git a/modules/ios/base/HippyUtils.h b/modules/ios/base/HippyUtils.h index 96f36e02d79..1e1a17dc098 100644 --- a/modules/ios/base/HippyUtils.h +++ b/modules/ios/base/HippyUtils.h @@ -42,6 +42,7 @@ HIPPY_EXTERN void HippyExecuteOnMainThread(dispatch_block_t block, BOOL sync); // Method swizzling HIPPY_EXTERN void HippySwapClassMethods(Class cls, SEL original, SEL replacement); HIPPY_EXTERN void HippySwapInstanceMethods(Class cls, SEL original, SEL replacement); +HIPPY_EXTERN void HippySwapInstanceMethodWithBlock(Class cls, SEL original, id replacementBlock, SEL replacementSelector); // Module subclass support HIPPY_EXTERN BOOL HippyClassOverridesClassMethod(Class cls, SEL selector); diff --git a/modules/ios/base/HippyUtils.m b/modules/ios/base/HippyUtils.m index e257b5a0cc7..e9eb53b5aa8 100644 --- a/modules/ios/base/HippyUtils.m +++ b/modules/ios/base/HippyUtils.m @@ -87,6 +87,18 @@ void HippySwapInstanceMethods(Class cls, SEL original, SEL replacement) { } } +void HippySwapInstanceMethodWithBlock(Class cls, SEL original, id replacementBlock, SEL replacementSelector) { + Method originalMethod = class_getInstanceMethod(cls, original); + if (!originalMethod) { + return; + } + + IMP implementation = imp_implementationWithBlock(replacementBlock); + class_addMethod(cls, replacementSelector, implementation, method_getTypeEncoding(originalMethod)); + Method newMethod = class_getInstanceMethod(cls, replacementSelector); + method_exchangeImplementations(originalMethod, newMethod); +} + BOOL HippyClassOverridesClassMethod(Class cls, SEL selector) { return HippyClassOverridesInstanceMethod(object_getClass(cls), selector); } @@ -438,15 +450,7 @@ static void HPGetRGBAColorComponents(CGColorRef color, CGFloat rgba[4]) { if (nil == uriData) { return nil; } - CFURLRef urlRef = NULL; - if ([URLString hasPrefix:@"http"] || - [URLString hasPrefix:@"data:"] || - [URLString hasPrefix:@"file:"]) { - urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, (__bridge CFURLRef)baseURL); - } - else { - urlRef = CFURLCreateWithFileSystemPath(NULL, (__bridge CFStringRef)URLString, kCFURLPOSIXPathStyle, NO); - } + CFURLRef urlRef = CFURLCreateWithBytes(NULL, [uriData bytes], [uriData length], kCFStringEncodingUTF8, (__bridge CFURLRef)baseURL); NSURL *source_url = CFBridgingRelease(urlRef); return source_url; } diff --git a/modules/ios/image/HippyDefaultImageProvider.m b/modules/ios/image/HippyDefaultImageProvider.m index b8d1d5ebf74..1e3db65e4bc 100644 --- a/modules/ios/image/HippyDefaultImageProvider.m +++ b/modules/ios/image/HippyDefaultImageProvider.m @@ -35,7 +35,8 @@ @interface HippyDefaultImageProvider () { @implementation HippyDefaultImageProvider -@synthesize imageDataPath; +@synthesize scale = _scale; +@synthesize imageDataPath = _imageDataPath; + (BOOL)canHandleData:(NSData *)data { return YES; @@ -46,6 +47,14 @@ + (BOOL)isAnimatedImage:(NSData *)data { return ret; } +- (instancetype)init { + self = [super init]; + if (self) { + _scale = 1.0; + } + return self; +} + - (void)setImageData:(NSData *)imageData { if ([[self class] isAnimatedImage:imageData]) { _imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); @@ -121,13 +130,13 @@ - (void)prepareForDisplay:(void (^)(UIImage *_Nullable))completionHandler{ } - (UIImage *)image { - CGFloat scale = [UIScreen mainScreen].scale; if (!_image) { UIImage *tmp; if (_data) { CGFloat view_width = _imageViewSize.width; CGFloat view_height = _imageViewSize.height; if (_downSample && view_width > 0 && view_height > 0) { + CGFloat scale = self.scale; NSDictionary *options = @{ (NSString *)kCGImageSourceShouldCache: @(NO) }; CGImageSourceRef ref = CGImageSourceCreateWithData((__bridge CFDataRef)_data, (__bridge CFDictionaryRef)options); if (ref) { @@ -161,7 +170,7 @@ - (UIImage *)image { tmp = [self imageAtFrame:0]; } if(!tmp){ - tmp = [UIImage imageWithData:_data scale:scale]; + tmp = [UIImage imageWithData:_data scale:self.scale]; } @synchronized (self) { if(_image == nil){ diff --git a/modules/ios/image/HippyImageProviderProtocol.h b/modules/ios/image/HippyImageProviderProtocol.h index 406a11271b2..54a64015513 100644 --- a/modules/ios/image/HippyImageProviderProtocol.h +++ b/modules/ios/image/HippyImageProviderProtocol.h @@ -36,6 +36,11 @@ */ + (BOOL)isAnimatedImage:(NSData *)data; +/** + * Image scale + */ +@property (nonatomic, assign) CGFloat scale; + @property(nonatomic, copy)NSString *imageDataPath; /** diff --git a/modules/vfs/ios/VFSUriLoader.mm b/modules/vfs/ios/VFSUriLoader.mm index 0825443c4ba..d4c6ccda1c2 100644 --- a/modules/vfs/ios/VFSUriLoader.mm +++ b/modules/vfs/ios/VFSUriLoader.mm @@ -25,10 +25,9 @@ #import "TypeConverter.h" #import "VFSUriLoader.h" #import "VFSUriHandler.h" - +#import "HippyAssert.h" #include #include - #include "footstone/string_view_utils.h" NSString *const VFSErrorDomain = @"VFSErrorDomain"; @@ -91,7 +90,9 @@ NSOperationQueue *operationQueue, VFSHandlerProgressBlock progress, VFSHandlerCompletionBlock completion) { - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]; + NSURL *url = HippyURLWithString(urlString, nil); + HippyAssert(url, @"Invalid URL! %@", urlString); + NSURLRequest *request = [NSURLRequest requestWithURL:url]; RequestUntrustedContent(request, extraInfo, operationQueue, progress, completion); } diff --git a/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc b/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc index 543d6dd08bf..18457c3fb42 100644 --- a/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc +++ b/renderer/native/android/src/main/cpp/src/renderer/native_render_manager.cc @@ -259,7 +259,6 @@ void NativeRenderManager::MoveRenderNode(std::weak_ptr root_node, auto len = nodes.size(); footstone::value::HippyValue::HippyValueArrayType dom_node_array; dom_node_array.resize(len); - uint32_t pid; for (uint32_t i = 0; i < len; i++) { const auto& render_info = nodes[i]->GetRenderInfo(); footstone::value::HippyValue::HippyValueObjectType dom_node; @@ -267,7 +266,6 @@ void NativeRenderManager::MoveRenderNode(std::weak_ptr root_node, dom_node[kPid] = footstone::value::HippyValue(render_info.pid); dom_node[kIndex] = footstone::value::HippyValue(render_info.index); dom_node_array[i] = dom_node; - pid = render_info.pid; } serializer_->WriteValue(HippyValue(dom_node_array)); std::pair buffer_pair = serializer_->Release(); @@ -286,12 +284,12 @@ void NativeRenderManager::MoveRenderNode(std::weak_ptr root_node, FOOTSTONE_LOG(ERROR) << "CallNativeMethod j_class error"; return; } - jmethodID j_method_id = j_env->GetMethodID(j_class, "moveNode", "(II[B)V"); + jmethodID j_method_id = j_env->GetMethodID(j_class, "moveNode", "(I[B)V"); if (!j_method_id) { FOOTSTONE_LOG(ERROR) << "moveNode" << " j_method_id error"; return; } - j_env->CallVoidMethod(j_object, j_method_id, root->GetId(), pid, j_buffer); + j_env->CallVoidMethod(j_object, j_method_id, root->GetId(), j_buffer); JNIEnvironment::ClearJEnvException(j_env); j_env->DeleteLocalRef(j_buffer); j_env->DeleteLocalRef(j_class); diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java index 8217a164977..2a32858bfac 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/utils/DevtoolsUtil.java @@ -213,9 +213,10 @@ public static void getScreenShot(@NonNull List params, @NonNull final View view, Window window = ((Activity) ((ContextWrapper) context).getBaseContext()).getWindow(); final Bitmap finalBitmap = bitmap; final float finalScale = scale; - PixelCopy.request(window, + try { + PixelCopy.request(window, new Rect(location[0], location[1], location[0] + view.getWidth(), - location[1] + view.getHeight()), + location[1] + view.getHeight()), finalBitmap, new OnPixelCopyFinishedListener() { @Override @@ -227,6 +228,9 @@ public void onPixelCopyFinished(int copyResult) { } } }, new Handler(Looper.getMainLooper())); + } catch (IllegalArgumentException e) { + LogUtils.e(TAG, " PixelCopy.request error", e); + } } else { LogUtils.e(TAG, "getScreenShot context.getBaseContext() is not activity"); } diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java new file mode 100644 index 00000000000..75aeb646bca --- /dev/null +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalDialogView.java @@ -0,0 +1,98 @@ +/* Tencent is pleased to support the open source community by making Hippy available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.tencent.mtt.hippy.views.modal; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static com.tencent.renderer.utils.EventUtils.EVENT_ORIENTATION_CHANGED; + +import android.app.Dialog; +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import com.tencent.renderer.utils.EventUtils; +import java.lang.ref.WeakReference; +import java.util.HashMap; + +public class HippyModalDialogView extends Dialog { + private int mOrientation = ORIENTATION_UNDEFINED; + private final ConfigurationChangedListener mListener = new ConfigurationChangedListener();; + private final WeakReference mHostView; + + public HippyModalDialogView(@NonNull Context context, int themeResId, @NonNull View hostView) { + super(context, themeResId); + mHostView = new WeakReference<>(hostView); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Configuration configuration = getContext().getResources().getConfiguration(); + mOrientation = configuration.orientation; + } + + @Override + protected void onStart() { + super.onStart(); + getContext().registerComponentCallbacks(mListener); + } + + @Override + protected void onStop() { + super.onStop(); + getContext().unregisterComponentCallbacks(mListener); + } + + protected void sendOrientationChangeEvent(int orientation) { + final View hostView = mHostView.get(); + if (hostView != null) { + String value; + switch (orientation) { + case ORIENTATION_PORTRAIT: + value = "portrait"; + break; + case ORIENTATION_LANDSCAPE: + value = "landscape"; + break; + default: + value = ""; + } + HashMap params = new HashMap<>(); + params.put("orientation", value); + EventUtils.sendComponentEvent(hostView, EVENT_ORIENTATION_CHANGED, params); + } + } + + private class ConfigurationChangedListener implements ComponentCallbacks { + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (newConfig.orientation != mOrientation) { + sendOrientationChangeEvent(newConfig.orientation); + mOrientation = newConfig.orientation; + } + } + + @Override + public void onLowMemory() { + // Handle low memory event + } + } +} diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java index e7bb9ef9975..de6e82e72f9 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/modal/HippyModalHostView.java @@ -70,7 +70,7 @@ public enum AnimationStyleTheme { @NonNull private final DialogRootView mDialogRootView; @Nullable - private Dialog mDialog; + private HippyModalDialogView mDialog; @Nullable private View mContentView; @Nullable @@ -356,9 +356,9 @@ protected void showOrUpdate() { } @NonNull - protected Dialog createDialog(@NonNull Context context) { + protected HippyModalDialogView createDialog(@NonNull Context context) { int themeResId = android.R.style.Theme_Translucent_NoTitleBar; - return new Dialog(context, themeResId); + return new HippyModalDialogView(context, themeResId, this); } @NonNull diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java index 40009a013aa..372fbb2bcaf 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderDelegate.java @@ -30,7 +30,7 @@ public interface NativeRenderDelegate extends RenderExceptionHandler, RenderLogH void moveNode(int rootId, int[] ids, int newPid, int oldPid, int insertIndex) throws NativeRenderException; - void moveNode(int rootId, int pid, @NonNull List list) throws NativeRenderException; + void moveNode(int rootId, @NonNull List list) throws NativeRenderException; void updateLayout(int rootId, @NonNull List list) throws NativeRenderException; diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java index 4a38920618a..e2a6a55e64e 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderProvider.java @@ -213,17 +213,16 @@ public void moveNode(int rootId, int[] ids, int newPid, int oldPid, int insertIn * Adjust the order of child nodes under the same parent node * * @param rootId the root node id - * @param pid the parent node id * @param buffer the byte array serialize by native (C++) */ @CalledByNative @SuppressWarnings("unused") - public void moveNode(int rootId, int pid, byte[] buffer) { + public void moveNode(int rootId, byte[] buffer) { NativeRenderDelegate renderDelegate = mRenderDelegateRef.get(); if (renderDelegate != null) { try { final List list = bytesToArgument(ByteBuffer.wrap(buffer)); - renderDelegate.moveNode(rootId, pid, list); + renderDelegate.moveNode(rootId, list); } catch (NativeRenderException e) { renderDelegate.handleRenderException(e); } diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java index f9ad86af0e7..bf2e3586877 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/NativeRenderer.java @@ -656,16 +656,39 @@ public void moveNode(final int rootId, final int[] ids, final int newPid, final } @Override - public void moveNode(final int rootId, final int pid, @NonNull final List list) { + public void moveNode(final int rootId, @NonNull final List list) { if (LogUtils.isDebugMode()) { - LogUtils.d(TAG, "moveNode: pid " + pid + ", node list " + list + "\n "); + LogUtils.d(TAG, "moveNode: node list " + list + "\n "); } - VirtualNode parent = mVirtualNodeManager.getVirtualNode(rootId, pid); - if (parent == null) { - addUITask(() -> mRenderManager.moveNode(rootId, pid, list)); - } else { - mVirtualNodeManager.moveNode(rootId, parent, list); - addUITask(() -> mRenderManager.onMoveVirtualNode(rootId, pid, list)); + final Map> nodeMap = new HashMap<>(); + for (int i = 0; i < list.size(); i++) { + final Map moveNodeInfo = ArrayUtils.getMapValue(list, i); + if (moveNodeInfo == null) { + continue; + } + final Integer pid = MapUtils.getIntValue(moveNodeInfo, NODE_PID, INVALID_NODE_ID); + if (pid == INVALID_NODE_ID) { + continue; + } + List nodeList = nodeMap.get(pid); + if (nodeList == null) { + nodeList = new ArrayList<>(); + nodeList.add(moveNodeInfo); + nodeMap.put(pid, nodeList); + } else { + nodeList.add(moveNodeInfo); + } + } + for (Entry> entry : nodeMap.entrySet()) { + final Integer pid = entry.getKey(); + final List value = entry.getValue(); + VirtualNode parent = mVirtualNodeManager.getVirtualNode(rootId, pid); + if (parent == null) { + addUITask(() -> mRenderManager.moveNode(rootId, pid, value)); + } else { + mVirtualNodeManager.moveNode(rootId, parent, value); + addUITask(() -> mRenderManager.onMoveVirtualNode(rootId, pid, value)); + } } } @@ -738,8 +761,8 @@ public void updateEventListener(final int rootId, @NonNull List eventLis TAG + ": updateEventListener: invalid negative id=" + nodeId); } if (LogUtils.isDebugMode()) { - LogUtils.d(TAG, - "updateEventListener: id " + nodeId + ", eventProps " + eventProps + "\n "); +// LogUtils.d(TAG, +// "updateEventListener: id " + nodeId + ", eventProps " + eventProps + "\n "); } mVirtualNodeManager.updateEventListener(rootId, nodeId, eventProps); taskList.add(() -> mRenderManager.updateEventListener(rootId, nodeId, eventProps)); diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java b/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java index 4955f8773b3..12f5d3335ff 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/node/ImageVirtualNode.java @@ -144,7 +144,7 @@ protected TextImageSpan createImageSpan() { } } if (drawable == null) { - drawable = new ColorDrawable(Color.WHITE); + drawable = new ColorDrawable(Color.TRANSPARENT); } drawable.setBounds(0, 0, mWidth, mHeight); return new TextImageSpan(drawable, mUrl, this, mNativeRenderer); diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java b/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java index a6da91aac85..4b2ff6ee965 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/utils/EventUtils.java @@ -77,6 +77,8 @@ public class EventUtils { public static final String EVENT_MODAL_REQUEST_CLOSE = "requestClose"; // On modal view show. public static final String EVENT_MODAL_SHOW = "show"; + // On modal orientation changed. + public static final String EVENT_ORIENTATION_CHANGED = "orientationChange"; // On refresh wrapper view refresh. public static final String EVENT_REFRESH_WRAPPER_REFRESH = "refresh"; diff --git a/renderer/native/ios/renderer/HippyComponent.h b/renderer/native/ios/renderer/HippyComponent.h index c04f8ab73b0..66cebab031d 100644 --- a/renderer/native/ios/renderer/HippyComponent.h +++ b/renderer/native/ios/renderer/HippyComponent.h @@ -52,8 +52,8 @@ typedef void (^HippyDirectEventBlock)(NSDictionary *body); /// Inset /// - Parameters: /// - subview: id -/// - atIndex: NSInteger -- (void)insertHippySubview:(id)subview atIndex:(NSInteger)atIndex; +/// - atIndex: NSUInteger +- (void)insertHippySubview:(id)subview atIndex:(NSUInteger)atIndex; /// Remove /// - Parameter subview: id @@ -62,8 +62,8 @@ typedef void (^HippyDirectEventBlock)(NSDictionary *body); /// Move /// - Parameters: /// - subview: id -/// - atIndex: NSInteger -- (void)moveHippySubview:(id)subview toIndex:(NSInteger)atIndex; +/// - atIndex: NSUInteger +- (void)moveHippySubview:(id)subview toIndex:(NSUInteger)atIndex; /// Remove from superview - (void)removeFromHippySuperview; diff --git a/renderer/native/ios/renderer/HippyRootView.h b/renderer/native/ios/renderer/HippyRootView.h index 33a8aba604d..8860247c635 100644 --- a/renderer/native/ios/renderer/HippyRootView.h +++ b/renderer/native/ios/renderer/HippyRootView.h @@ -49,8 +49,7 @@ extern NSString *const HippyContentDidAppearNotification; /// Business bundle loading completion notification /// This notification is for compatibility with hippy2 and is not recommended for further use -extern NSString *const HippySecondaryBundleDidLoadNotification; - +extern NSString *const HippySecondaryBundleDidLoadNotification DEPRECATED_MSG_ATTRIBUTE("use HippyJavaScriptDidLoadNotification"); /// Native view used to host Hippy-managed views within the app. diff --git a/renderer/native/ios/renderer/HippyRootView.mm b/renderer/native/ios/renderer/HippyRootView.mm index f9ed9123ed5..b99d9553c45 100644 --- a/renderer/native/ios/renderer/HippyRootView.mm +++ b/renderer/native/ios/renderer/HippyRootView.mm @@ -40,6 +40,7 @@ // For compatibility, hippy3 retains this notice and its actual meaning. NSString *const HippySecondaryBundleDidLoadNotification = @"HippySecondaryBundleDidLoadNotification"; + NSNumber *AllocRootViewTag(void) { static NSString * const token = @"allocateRootTag"; @synchronized (token) { @@ -156,7 +157,9 @@ - (instancetype)initWithBridge:(HippyBridge *)bridge } } else { __weak __typeof(self)weakSelf = self; - [bridge loadBundleURL:businessURL completion:^(NSURL * _Nullable url, NSError * _Nullable error) { + [bridge loadBundleURL:businessURL + bundleType:HippyBridgeBundleTypeBusiness + completion:^(NSURL * _Nullable url, NSError * _Nullable error) { // Execute loadInstance first and then do call back, maintain compatibility with hippy2 dispatch_async(dispatch_get_main_queue(), ^{ __strong __typeof(weakSelf)strongSelf = weakSelf; @@ -166,12 +169,16 @@ - (instancetype)initWithBridge:(HippyBridge *)bridge if (!error && !strongSelf.disableAutoRunApplication) { [strongSelf runHippyApplication]; } - // 抛出业务包(BusinessBundle aka SecondaryBundle)加载完成通知, for hippy2兼容 - NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{ @"url": url, - @"bridge": strongSelf.bridge }]; - if (error) [userInfo setObject:error forKey:@"error"]; + + // 抛出业务包(BusinessBundle aka SecondaryBundle)加载完成通知,for hippy2兼容 + NSMutableDictionary *userInfo = @{ kHippyNotiBundleUrlKey: url, + kHippyNotiBridgeKey: strongSelf.bridge }.mutableCopy; + if (error) { [userInfo setObject:error forKey:kHippyNotiErrorKey]; } + HIPPY_IGNORE_WARNING_BEGIN(-Wdeprecated) [[NSNotificationCenter defaultCenter] postNotificationName:HippySecondaryBundleDidLoadNotification - object:strongSelf.bridge userInfo:userInfo]; + object:strongSelf.bridge + userInfo:userInfo]; + HIPPY_IGNORE_WARNING_END if ([delegate respondsToSelector:@selector(rootView:didLoadFinish:)]) { [delegate rootView:strongSelf didLoadFinish:(error == nil)]; @@ -285,15 +292,15 @@ - (void)javaScriptDidLoad:(NSNotification *)notification { // Use the bridge that's sent in the notification payload // Call runHippyApplication only if the RootView is initialized without a business bundle. - HippyBridge *bridge = notification.userInfo[@"bridge"]; + HippyBridge *bridge = notification.userInfo[kHippyNotiBridgeKey]; if (!self.disableAutoRunApplication && bridge == self.bridge && !_hasBusinessBundleToLoad) { [self runHippyApplication]; } } - (void)javaScriptDidFailToLoad:(NSNotification *)notification { - HippyBridge *bridge = notification.userInfo[@"bridge"]; - NSError *error = notification.userInfo[@"error"]; + HippyBridge *bridge = notification.userInfo[kHippyNotiBridgeKey]; + NSError *error = notification.userInfo[kHippyNotiErrorKey]; if (bridge == self.bridge && error) { NSError *retError = HippyErrorFromErrorAndModuleName(error, self.bridge.moduleName); HippyFatal(retError); diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index a4c832ccbe4..1bf2deedb4b 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -221,9 +221,6 @@ - (instancetype)init { return self; } -- (void)dealloc { -} - - (void)initContext { _shadowViewRegistry = [[HippyComponentMap alloc] initWithComponentsReferencedType:HippyComponentReferenceTypeStrong]; _viewRegistry = [[HippyComponentMap alloc] initWithComponentsReferencedType:HippyComponentReferenceTypeWeak]; @@ -957,21 +954,23 @@ - (void)renderMoveViews:(const std::vector &&)ids return; } int32_t rootTag = strongRootNode->GetId(); - - HippyShadowView *fromObjectView = [_shadowViewRegistry componentForTag:@(fromContainer) - onRootTag:@(rootTag)]; - HippyShadowView *toObjectView = [_shadowViewRegistry componentForTag:@(toContainer) - onRootTag:@(rootTag)]; - for (int32_t componentTag : ids) { - HippyShadowView *view = [_shadowViewRegistry componentForTag:@(componentTag) onRootTag:@(rootTag)]; - HippyAssert(fromObjectView == [view parent], @"parent of object view with tag %d is not object view with tag %d", componentTag, fromContainer); + std::lock_guard lock([self renderQueueLock]); + HippyShadowView *fromShadowView = [_shadowViewRegistry componentForTag:@(fromContainer) onRootTag:@(rootTag)]; + HippyShadowView *toShadowView = [_shadowViewRegistry componentForTag:@(toContainer) onRootTag:@(rootTag)]; + for (int32_t hippyTag : ids) { + HippyShadowView *view = [_shadowViewRegistry componentForTag:@(hippyTag) onRootTag:@(rootTag)]; + if (!view) { + HippyLogWarn(@"Invalid Move, No ShadowView! (%d of %d)", hippyTag, rootTag); + continue; + } + HippyAssert(fromShadowView == [view parent], @"ShadowView(%d)'s parent should be %d", hippyTag, fromContainer); [view removeFromHippySuperview]; - [toObjectView insertHippySubview:view atIndex:index]; + [toShadowView insertHippySubview:view atIndex:index]; } - [fromObjectView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; - [toObjectView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; - [fromObjectView didUpdateHippySubviews]; - [toObjectView didUpdateHippySubviews]; + [fromShadowView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; + [toShadowView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; + [fromShadowView didUpdateHippySubviews]; + [toShadowView didUpdateHippySubviews]; auto strongTags = std::move(ids); [self addUIBlock:^(__unused HippyUIManager *uiManager, NSDictionary *viewRegistry) { UIView *fromView = [viewRegistry objectForKey:@(fromContainer)]; @@ -1006,7 +1005,7 @@ - (void)renderMoveNodes:(std::vector> &&)nodes int32_t componentTag = node->GetId(); HippyShadowView *objectView = [_shadowViewRegistry componentForTag:@(componentTag) onRootTag:@(rootTag)]; [objectView dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; - HippyAssert(!parentObjectView || parentObjectView == [objectView parent], @"try to move object view on different parent object view"); + HippyAssert(!parentObjectView || parentObjectView == [objectView parent], @"parent not same!"); if (!parentObjectView) { parentObjectView = (HippyShadowView *)[objectView parent]; } @@ -1017,7 +1016,7 @@ - (void)renderMoveNodes:(std::vector> &&)nodes [self addUIBlock:^(__unused HippyUIManager *uiManager, NSDictionary *viewRegistry) { UIView *superView = nil; for (auto node : strongNodes) { - int32_t index = node->GetIndex(); + int32_t index = node->GetRenderInfo().index; int32_t componentTag = node->GetId(); UIView *view = [viewRegistry objectForKey:@(componentTag)]; if (!view) { diff --git a/renderer/native/ios/renderer/NativeRenderManager.mm b/renderer/native/ios/renderer/NativeRenderManager.mm index 463786279c7..17d3e5eb5c5 100644 --- a/renderer/native/ios/renderer/NativeRenderManager.mm +++ b/renderer/native/ios/renderer/NativeRenderManager.mm @@ -107,7 +107,26 @@ std::vector>&& nodes) { @autoreleasepool { HippyAssert(renderImpl_, @"renderImpl_ is null, did you forget to call Initialize()?"); - [renderImpl_ renderMoveNodes:std::move(nodes) onRootNode:root_node]; + // Check whether all nodes have the same pid + uint32_t firstPid = nodes[0]->GetPid(); + bool allSamePid = std::all_of(nodes.begin(), nodes.end(), + [firstPid](const std::shared_ptr& node) { + return node->GetPid() == firstPid; + }); + + if (allSamePid) { + // If all nodes have the same pid, call directly + [renderImpl_ renderMoveNodes:std::move(nodes) onRootNode:root_node]; + } else { + // If not, group them by pid and then call for each group + std::map>> pidNodeMap; + for (auto& node : nodes) { + pidNodeMap[node->GetPid()].push_back(node); + } + for (auto& pair : pidNodeMap) { + [renderImpl_ renderMoveNodes:std::move(pair.second) onRootNode:root_node]; + } + } } } diff --git a/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm b/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm index 9f925f814db..629be77135e 100644 --- a/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm +++ b/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm @@ -134,7 +134,7 @@ - (void)loadImageSource:(NSString *)path forView:(HippyImageView *)view { HippyBridge *bridge = strongSelf.bridge; if (bridge) { id imageProvider = nil; - for (Class cls in [bridge imageProviderClasses]) { + for (Class cls in [bridge imageProviders]) { if ([cls canHandleData:data]) { imageProvider = [[(Class)cls alloc] init]; break; diff --git a/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm b/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm index a3b6070b121..2a5a19c951c 100644 --- a/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm +++ b/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm @@ -163,7 +163,7 @@ - (void)reloadData { } } -- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(UIView *)subview atIndex:(NSUInteger)atIndex { if ([subview isKindOfClass:[HippyHeaderRefresh class]]) { if (_headerRefreshView) { [_headerRefreshView unsetFromScrollView]; diff --git a/renderer/native/ios/renderer/component/text/HippyShadowText.mm b/renderer/native/ios/renderer/component/text/HippyShadowText.mm index 46b3e36c56e..eb79f1399b7 100644 --- a/renderer/native/ios/renderer/component/text/HippyShadowText.mm +++ b/renderer/native/ios/renderer/component/text/HippyShadowText.mm @@ -450,8 +450,7 @@ - (NSAttributedString *)_attributedStringWithStyleInfo:(HippyAttributedStringSty if (!_textAlignSet) { if ([self isLayoutSubviewsRTL]) { self.textAlign = NSTextAlignmentRight; - } - else { + } else { self.textAlign = NSTextAlignmentLeft; } } @@ -488,18 +487,18 @@ - (NSAttributedString *)_attributedStringWithStyleInfo:(HippyAttributedStringSty } UIFont *font = [HippyFont updateFont:f - withFamily:styleInfo.fontFamily - size:styleInfo.fontSize - weight:styleInfo.fontWeight - style:styleInfo.fontStyle - variant:_fontVariant - scaleMultiplier:_allowFontScaling ? _fontSizeMultiplier : 1.0]; + withFamily:styleInfo.fontFamily + size:styleInfo.fontSize + weight:styleInfo.fontWeight + style:styleInfo.fontStyle + variant:_fontVariant + scaleMultiplier:_allowFontScaling ? _fontSizeMultiplier : 1.0]; CGFloat heightOfTallestSubview = 0.0; NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:self.text ?: @""]; for (HippyShadowView *child in [self subcomponents]) { if ([child isKindOfClass:[HippyShadowText class]]) { - HippyShadowText *shadowText = (HippyShadowText *)child; + HippyShadowText *childShadowText = (HippyShadowText *)child; HippyAttributedStringStyleInfo *childInfo = [HippyAttributedStringStyleInfo new]; childInfo.fontFamily = styleInfo.fontFamily; childInfo.fontSize = styleInfo.fontSize; @@ -507,11 +506,11 @@ - (NSAttributedString *)_attributedStringWithStyleInfo:(HippyAttributedStringSty childInfo.fontStyle = styleInfo.fontStyle; childInfo.letterSpacing = styleInfo.letterSpacing; childInfo.useBackgroundColor = YES; - childInfo.foregroundColor = [shadowText color] ?: styleInfo.foregroundColor; - childInfo.backgroundColor = shadowText.backgroundColor ?: styleInfo.backgroundColor; - childInfo.opacity = styleInfo.opacity * shadowText.opacity; + childInfo.foregroundColor = [childShadowText color] ?: styleInfo.foregroundColor; + childInfo.backgroundColor = childShadowText.backgroundColor ?: styleInfo.backgroundColor; + childInfo.opacity = styleInfo.opacity * childShadowText.opacity; childInfo.isNestedText = styleInfo.isNestedText; - NSAttributedString *subStr = [shadowText _attributedStringWithStyleInfo:childInfo]; + NSAttributedString *subStr = [childShadowText _attributedStringWithStyleInfo:childInfo]; [attributedString appendAttributedString:subStr]; [child setTextComputed]; } else { @@ -630,10 +629,11 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib heightOfTallestSubview:(CGFloat)heightOfTallestSubview isNestedText:(BOOL)isNestedText { NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; + CGFloat adjustedLineHeight = self.lineHeight; BOOL hasSetLineHeight = NO; - if (fabs(self.lineHeight - 0) < DBL_EPSILON) { + if (DirtyTextEqual(adjustedLineHeight, 0.0)) { // If no fixed lineHeight is set, fontLineHeight is used. - self.lineHeight = fontLineHeight; + adjustedLineHeight = fontLineHeight; } else if (!self.adjustsFontSizeToFit) { // Only when adjustsFontSizeToFit is not set, the fixed lineHeight can be used. hasSetLineHeight = YES; @@ -645,10 +645,10 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib hasParagraphStyle = YES; } - __block float newLineHeight = _lineHeight ?: 0.0; + __block CGFloat newLineHeight = adjustedLineHeight ?: 0.0; CGFloat fontSizeMultiplier = _allowFontScaling ? _fontSizeMultiplier : 1.0; - // check for lineHeight on each of our children, update the max as we go (in self.lineHeight) + // check for lineHeight on each of our children, update the max as we go [attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions @@ -662,9 +662,8 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib hasParagraphStyle = YES; } }]; - - if (self.lineHeight != newLineHeight) { - self.lineHeight = newLineHeight; + if (!DirtyTextEqual(adjustedLineHeight, newLineHeight)) { + adjustedLineHeight = newLineHeight; } __block CGFloat maximumFontLineHeight = 0.0; @@ -685,7 +684,7 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib if (hasParagraphStyle) { NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; paragraphStyle.alignment = _textAlign; - CGFloat lineHeight = round(_lineHeight * fontSizeMultiplier); + CGFloat lineHeight = round(adjustedLineHeight * fontSizeMultiplier); CGFloat maxHeight = lineHeight; if (heightOfTallestSubview > lineHeight) { maxHeight = ceilf(heightOfTallestSubview); diff --git a/renderer/native/ios/renderer/component/view/HippyShadowView.mm b/renderer/native/ios/renderer/component/view/HippyShadowView.mm index dc565ff3b07..84efcb1e904 100644 --- a/renderer/native/ios/renderer/component/view/HippyShadowView.mm +++ b/renderer/native/ios/renderer/component/view/HippyShadowView.mm @@ -27,6 +27,7 @@ #import "UIView+DirectionalLayout.h" #import "UIView+Hippy.h" #import "HippyShadowView+Internal.h" +#import "HippyAssert.h" static NSString *const HippyBackgroundColorPropKey = @"backgroundColor"; @@ -177,11 +178,15 @@ - (UIView *)createView:(HippyViewCreationBlock)creationBlock insertChildren:(Hip return container; } -- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSUInteger)atIndex { + if (!subview) { + HippyAssert(subview != nil, @"subview should not be nil!"); + HippyFatal(HippyErrorWithMessage(@"Illegal nil shadow subview in insertHippySubview!")); + return; + } if (atIndex <= [_objectSubviews count]) { [_objectSubviews insertObject:subview atIndex:atIndex]; - } - else { + } else { [_objectSubviews addObject:subview]; } subview->_superview = self; @@ -190,7 +195,7 @@ - (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex [self dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; } -- (void)moveHippySubview:(id)subview toIndex:(NSInteger)atIndex { +- (void)moveHippySubview:(id)subview toIndex:(NSUInteger)atIndex { if ([_objectSubviews containsObject:subview]) { [_objectSubviews removeObject:subview]; } diff --git a/renderer/native/ios/renderer/component/view/HippyViewManager.mm b/renderer/native/ios/renderer/component/view/HippyViewManager.mm index 3b28c925fee..9d5c38ee6bd 100644 --- a/renderer/native/ios/renderer/component/view/HippyViewManager.mm +++ b/renderer/native/ios/renderer/component/view/HippyViewManager.mm @@ -296,7 +296,7 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view { HippyBridge *bridge = strongSelf.bridge; if (bridge) { id imageProvider = nil; - for (Class cls in [bridge imageProviderClasses]) { + for (Class cls in [bridge imageProviders]) { if ([cls canHandleData:data]) { imageProvider = [[(Class)cls alloc] init]; break; diff --git a/renderer/native/ios/renderer/component/view/UIView+Hippy.mm b/renderer/native/ios/renderer/component/view/UIView+Hippy.mm index 439ccc2bc88..161e4b715b7 100644 --- a/renderer/native/ios/renderer/component/view/UIView+Hippy.mm +++ b/renderer/native/ios/renderer/component/view/UIView+Hippy.mm @@ -158,7 +158,7 @@ - (void)setParent:(id)parent { } } -- (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(UIView *)subview atIndex:(NSUInteger)atIndex { // We access the associated object directly here in case someone overrides // the `subcomponents` getter method and returns an immutable array. if (nil == subview) { @@ -172,14 +172,13 @@ - (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { if (atIndex <= [subviews count]) { [subviews insertObject:subview atIndex:atIndex]; - } - else { + } else { [subviews addObject:subview]; } subview.parent = self; } -- (void)moveHippySubview:(UIView *)subview toIndex:(NSInteger)atIndex { +- (void)moveHippySubview:(UIView *)subview toIndex:(NSUInteger)atIndex { if (nil == subview) { return; } diff --git a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm index cbb18c9e3b8..a19dc6f4d6f 100644 --- a/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm +++ b/renderer/native/ios/renderer/component/viewPager/HippyViewPager.mm @@ -46,6 +46,10 @@ @interface HippyViewPager () @property (nonatomic, assign) CGFloat previousStopOffset; @property (nonatomic, assign) NSUInteger lastPageSelectedCallbackIndex; +/// A weak property used to record the currently displayed item, +/// which is used for updating the page index when the data changes. +@property (nonatomic, weak) UIView *lastSelectedPageItem; + @end @implementation HippyViewPager @@ -62,6 +66,7 @@ - (instancetype)initWithFrame:(CGRect)frame { self.previousFrame = CGRectZero; self.scrollViewListener = [NSHashTable weakObjectsHashTable]; self.lastPageIndex = NSUIntegerMax; + self.lastPageSelectedCallbackIndex = NSUIntegerMax; self.targetContentOffsetX = CGFLOAT_MAX; if (@available(iOS 11.0, *)) { self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; @@ -85,7 +90,7 @@ - (void)didMoveToSuperview { #pragma mark native render native methods -- (void)insertHippySubview:(UIView *)view atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(UIView *)view atIndex:(NSUInteger)atIndex { if (atIndex > self.viewPagerItems.count) { HippyLogWarn(@"Error In HippyViewPager: addSubview —— out of bound of array"); return; @@ -149,6 +154,25 @@ - (void)hippySetFrame:(CGRect)frame { - (void)didUpdateHippySubviews { [super didUpdateHippySubviews]; self.needsLayoutItems = YES; + + // Update the latest page index based on the currently displayed item (aka lastSelectedPageItem). + // Keep the same logic as android: + // 1. If the previous item only changes its location, + // update the current location and keep the current item displayed. + // 2. If the previous item does not exist, do not adjust the position, + // but keep the current position in the valid range (that is, 0 ~ count-1). + UIView *previousSelectedItem = self.lastSelectedPageItem; + NSUInteger updatedPageIndex; + if (previousSelectedItem) { + updatedPageIndex = [self.viewPagerItems indexOfObject:previousSelectedItem]; + } else { + updatedPageIndex = MAX(0, MIN(self.lastPageIndex, self.viewPagerItems.count - 1)); + } + if (self.lastPageIndex != updatedPageIndex) { + self.lastPageIndex = updatedPageIndex; + self.needsResetPageIndex = YES; + } + [self setNeedsLayout]; } @@ -161,6 +185,7 @@ - (void)setPage:(NSInteger)pageNumber animated:(BOOL)animated { _lastPageIndex = pageNumber; UIView *theItem = self.viewPagerItems[pageNumber]; + self.lastSelectedPageItem = theItem; self.targetContentOffsetX = CGRectGetMinX(theItem.frame); [self setContentOffset:theItem.frame.origin animated:animated]; [self invokePageSelected:pageNumber]; @@ -363,6 +388,7 @@ - (NSUInteger)targetPageIndexFromTargetContentOffsetX:(CGFloat)targetContentOffs } if (_lastPageIndex != thePage) { _lastPageIndex = thePage; + _lastSelectedPageItem = self.viewPagerItems[thePage]; return thePage; } else { return _lastPageIndex; @@ -389,7 +415,6 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated { - (void)hippyBridgeDidFinishTransaction { BOOL isFrameEqual = CGRectEqualToRect(self.frame, self.previousFrame); BOOL isContentSizeEqual = CGSizeEqualToSize(self.contentSize, self.previousSize); - if (!isContentSizeEqual || !isFrameEqual) { self.previousFrame = self.frame; self.previousSize = self.contentSize; @@ -425,10 +450,12 @@ - (void)layoutSubviews { return; } - self.contentSize = CGSizeMake( - lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width, - lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height - ); + CGSize updatedSize = CGSizeMake(lastViewPagerItem.frame.origin.x + lastViewPagerItem.frame.size.width, + lastViewPagerItem.frame.origin.y + lastViewPagerItem.frame.size.height); + if (!CGSizeEqualToSize(self.contentSize, updatedSize)) { + self.contentSize = updatedSize; + } + if (!_didFirstTimeLayout) { [self setPage:self.initialPage animated:NO]; _didFirstTimeLayout = YES; diff --git a/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm b/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm index 484ce2b462e..cf19e5c4556 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/HippyShadowListView.mm @@ -150,7 +150,7 @@ - (WaterfallItemChangeContext *)itemChangeContext { return _itemChangeContext; } -- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex { +- (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSUInteger)atIndex { [super insertHippySubview:subview atIndex:atIndex]; if ([subview isKindOfClass:[HippyShadowWaterfallItem class]]) { HippyShadowWaterfallItem *objectItem = (HippyShadowWaterfallItem *)subview; @@ -168,7 +168,7 @@ - (void)removeHippySubview:(HippyShadowView *)subview { [_itemChangeContext appendDeletedItem:subview]; } -- (void)moveHippySubview:(id)subview toIndex:(NSInteger)atIndex { +- (void)moveHippySubview:(id)subview toIndex:(NSUInteger)atIndex { [super moveHippySubview:subview toIndex:atIndex]; [_itemChangeContext appendMovedItem:subview]; } diff --git a/tests/ios/HippyBridgeTest.mm b/tests/ios/HippyBridgeTest.mm new file mode 100644 index 00000000000..72c7adfb4a6 --- /dev/null +++ b/tests/ios/HippyBridgeTest.mm @@ -0,0 +1,63 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +@interface HippyBridgeTest : XCTestCase + +@end + +@implementation HippyBridgeTest + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testLoadBundleURL { + HippyBridge *bridge = [[HippyBridge alloc] initWithDelegate:nil moduleProvider:nil launchOptions:nil executorKey:nil]; + NSString *testNoSchemePath = @"/Users/ray/testNoSchemePath"; + NSURL *testUrl = [NSURL URLWithString:testNoSchemePath]; + XCTAssert(testUrl.scheme == nil); + [bridge loadBundleURL:testUrl + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) {}]; + NSURL *loadedUrl = bridge.bundleURLs.lastObject; + XCTAssert(loadedUrl.scheme != nil); + XCTAssertTrue(loadedUrl.isFileURL); + + testUrl = [NSURL URLWithString:@"http://hippyjs_no_exist.org"]; + XCTAssert([testUrl.scheme isEqualToString:@"http"]); + [bridge loadBundleURL:testUrl + bundleType:HippyBridgeBundleTypeVendor + completion:^(NSURL * _Nullable bundleURL, NSError * _Nullable error) {}]; + loadedUrl = bridge.bundleURLs.lastObject; + XCTAssert([loadedUrl.scheme isEqualToString:@"http"]); + XCTAssertFalse(loadedUrl.isFileURL); +} + + +@end diff --git a/tests/ios/HippyUtilsTest.m b/tests/ios/HippyUtilsTest.m new file mode 100644 index 00000000000..fb41d388923 --- /dev/null +++ b/tests/ios/HippyUtilsTest.m @@ -0,0 +1,90 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +@interface HippyUtilsTest : XCTestCase + +@end + +@implementation HippyUtilsTest + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testSDKVersionExists { + NSString *ver = [HippyUtils sdkVersion]; + XCTAssertNotNil(ver); +} + +- (void)testHippyURLWithString { + HIPPY_IGNORE_WARNING_BEGIN(-Wnonnull) + XCTAssertNil(HippyURLWithString(nil, nil)); + HIPPY_IGNORE_WARNING_END + XCTAssert([[HippyURLWithString(@"", nil) absoluteString] length] == 0); + + NSArray *testPaths = @[ + @"http://hippyjs.org", + @"https://hippyjs.org", + @"file:///testAbsulotePath/subPath", + @"hpfile://./testHippyRelativePath/subPath", + @"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAA", + // Some exceptions, such as Spaces or newlines + @"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAA\n\n ", + ]; + for (NSString *path in testPaths) { + NSURL *url = HippyURLWithString(path, nil); + XCTAssertNotNil(url); + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + XCTAssertNotNil(components.scheme); + XCTAssertNotNil(components.path); + } + testPaths = @[ + @"测试中文", + ]; + for (NSString *path in testPaths) { + NSURL *url = HippyURLWithString(path, nil); + XCTAssertNotNil(url); + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + XCTAssertNil(components.scheme); + XCTAssertNotNil(components.path); + } + + NSString *baseUrl = @"https://hippyjs.org/#/"; + testPaths = @[ + @"hello/hippy", + ]; + for (NSString *path in testPaths) { + NSURL *url = HippyURLWithString(path, baseUrl); + XCTAssert([url.absoluteString isEqualToString:@"https://hippyjs.org/hello/hippy"]); + } + +} + + +@end