# rn-update

用于 react-native 的热更新组件包，支持 rncli、expo框架的 Android、iOS平台的bundle文件热更新

## 安装


```sh
npm i @xiaoyu-design/rn-update

yarn add @xiaoyu-design/rn-update
```


## Usage

```jsx
import RNUpdate, { AppUpdate } from "@xiaoyu-design/rn-update"
```

```jsx
// 包裹你的组件树
<AppUpdate.Provider>
  <RNUpdateTestCase />
</AppUpdate.Provider>
```
```jsx
import { AppUpdate } from '@xiaoyu-design/rn-update';

// hook使用

const appUpdate = AppUpdate.useUpdate();
const client=  useMemo(function() {
  return appUpdate.client;
}, [appUpdate]);

// 使用 client 调用api即可

// 如：获取当前已安装的热更新版本信息
client.getCurrentVersionInfo().then(versionInfo => {})

```
## iOS使用

### Swift 使用

```swift
import RnUpdate  // 这里要引入

// .... 省略代码

class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
  override func sourceURL(for bridge: RCTBridge) -> URL? {
    self.bundleURL()
  }

  override func bundleURL() -> URL? {
    #if DEBUG
      return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
    #else
      return RCTRnUpdate.bundleURL() // 这里引入
//    return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
    #endif
  }
}
```

### OC使用

```c

// ... 其它代码
#import "RCTRnUpdate.h" // <-- import头文件，注意要放到if条件外面


// rn 版本 >= 0.74 需要修改 bundleURL 方法
- (NSURL *)bundleURL
{
#if DEBUG
  // 原先DEBUG这里的写法不作修改
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [RCTRnUpdate bundleURL];  // <--  把这里非DEBUG的情况替换为热更新bundle
#endif
}

// rn 版本 < 0.74 需要修改sourceURLForBridge方法
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  // 原先DEBUG这里的写法不作修改
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [RCTRnUpdate bundleURL]; // <--  把这里非DEBUG的情况替换为热更新bundle
#endif
}

```


## Android使用

```java
import com.xiaoyudesign.rnupdate.UpdateContext; // 引入包

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Nullable
    @Override
    protected String getJSBundleFile() {
      // 这里指定 热更新路径
      return UpdateContext.getBundleUrl(MainApplication.this, super.getJSBundleFile());
    }

  }
}

```


## TestCase

```jsx
import {
  Image,
  ScrollView,
  Text,
  TextInput,
  TouchableOpacity,
  View,
} from 'react-native';
import { useEffect, useMemo, useState } from 'react';
import { AppUpdate } from '@xiaoyu-design/rn-update';

const Notifications = {
  show: function(text: string) {
    console.log("Notifications: "+ text);
  }
}

export function RNUpdateTestCase() {

  const appUpdate = AppUpdate.useUpdate();

  const [downloadUrl, setDownloadUrl] = useState("http://192.168.2.90:8080/hot-update.zip")
  // const [downloadUrl, setDownloadUrl] = useState("http://192.168.1.65:8080/hot-update.zip")
  const [downloadVersionCode, setDownloadVersionCode] = useState<string>("100")
  const [versionInfo, setVersionInfo] = useState<string>("");
  const [downloadVersionInfo, setDownloadVersionInfo] = useState<string>("");
  const [checkVersionExistsResult, setCheckVersionExistsResult] = useState<string>("");
  const [sourceSrcInfo, setSourceSrcInfo] = useState<string>("");


  const client=  useMemo(function() {
    return appUpdate.client;
  }, [appUpdate]);

  useEffect(function() {
    if(client) {
      getVersionInfo();
    }
  }, [client])

  function checkUpdate() {
    client.checkUpdate({
      versionCode: downloadVersionCode
    }).then(r => {
      console.log("checkUpdate: ", r);
    })
  }

  function getVersionInfo() {
    client.getCurrentVersionInfo().then(versionInfo => {
      if(versionInfo) {
        setVersionInfo(JSON.stringify(versionInfo));
      }
    })
  }

  function onDownloadVersion() {
    if(downloadVersionCode == "") {
      Notifications.show("请输入版本号")
      return;
    }
    if(downloadUrl == "") {
      Notifications.show("请输入下载路径")
      return;
    }

    console.log("当前版本：", downloadVersionCode, "路径：", downloadUrl);
    client.downloadPPKVersion({
      url: downloadUrl,
      newVersion: downloadVersionCode
    }).then(res=> {
      console.log("downloadPPKVersion res ==>", res);
      setDownloadVersionInfo(res ? JSON.stringify(res) : "-")
    }).catch(err=> {
      setDownloadVersionInfo(err ? JSON.stringify(err) : "-")
      console.log("downloadPPKVersion err ==>", err);
    })
  }

  function onRestart() {
    if(downloadVersionCode == "") {
      Notifications.show("请输入版本号")
      return;
    }
    client.reloadUpdate({
      newVersion: downloadVersionCode
    }).then(res=> {
      if(res) {
        if(res.code == 200) {
        } else {
          Notifications.show(res.msg)
        }
      }
      console.log("restart restart", res);
    })
  }

  function onCheckVersion() {
    if(downloadVersionCode == "") {
      Notifications.show("请输入版本号")
      return;
    }
    client.checkVersionExists({
      versionCode: downloadVersionCode
    }).then((res) => {
      setCheckVersionExistsResult(JSON.stringify(res))
    })
  }

  console.log("versionInfo==>", versionInfo)

  useEffect(() => {
    // 测试资源加载
    try {
      const src = Image.resolveAssetSource(require('../../../assets/images/hot_test.png'));
      setSourceSrcInfo(JSON.stringify(src));
    } catch (e) {
      setSourceSrcInfo(JSON.stringify({
        msg: e.message,
        code: 500
      }));
    }
  }, []);

  return (
    <ScrollView>
      <RowItem
        title={"版本信息"}
        rightNode={
          <Text>{ versionInfo }</Text>
        }
      />

      <RowItem
        title={"新版本"}
        rightNode={
          <Text>908</Text>
        }
      />

      <RowItem
        title={"版本号"}
        rightNode={
          <View>
            <TextInput
              value={downloadVersionCode}
              placeholder={"请输入"}
              onChangeText={text=> setDownloadVersionCode(text)}
            />
          </View>
        }
      />

      <RowItem
        title={"下载路径"}
        rightNode={
          <View>
            <TextInput
              value={downloadUrl}
              placeholder={"请输入"}
              onChangeText={text=> setDownloadUrl(text)}
            />
          </View>
        }
      />

      <RowItem
        title={"点击下载"}
        rightNode={
          <TouchableOpacity style={{
            width: 200,
            height: 40,
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: "blue"
          }}
                            onPress={onDownloadVersion}
          >
            <Text style={{ color: '#fff' }}>下载</Text>
          </TouchableOpacity>
        }
      />


      <RowItem
        title={"下载结果"}
        rightNode={
          <View>
            <Text>{ downloadVersionInfo }</Text>
          </View>
        }
      />


      <RowItem
        title={"检测版本"}
        rightNode={
          <TouchableOpacity style={{
            width: 200,
            height: 40,
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: "green"
          }}
                            onPress={onCheckVersion}
          >
            <Text style={{ color: '#fff' }}>检测版本</Text>
          </TouchableOpacity>
        }
      />


      <RowItem
        title={"检测版本信息："}
        rightNode={
          <View>
            <Text>{ checkVersionExistsResult }</Text>
          </View>
        }
      />

      <RowItem
        title={"切换版本"}
        rightNode={
          <TouchableOpacity style={{
            width: 200,
            height: 40,
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: "green"
          }}
                            onPress={onRestart}
          >
            <Text style={{ color: '#fff' }}>切换版本</Text>
          </TouchableOpacity>
        }
      />
    </ScrollView>
  )
}


function RowItem({
                   title, rightNode
                 }: {
  title: string;
  rightNode: React.ReactNode
}) {
  return (
    <View
      style={{
        backgroundColor: "#fff"
      }}
    >
      <View style={{
        flexDirection: 'row',
        paddingHorizontal: 10,
        paddingVertical: 20,
      }}>
        <View>
          <Text>{ title }</Text>
        </View>
        <View style={{
          flex: 1,
          justifyContent: "center",
          alignItems: "flex-end"
        }}>
          { rightNode }
        </View>
      </View>
      <View style={{ height: 1, backgroundColor: "#c5c5c5" }} />
    </View>
  )
}


```


