diff --git a/README.md b/README.md index 6fbda8d5..fe27321e 100644 --- a/README.md +++ b/README.md @@ -11,29 +11,38 @@ This library gets the latest app version by parsing google play store, apple app Parsing code is referenced from [here](http://itmir.tistory.com/524) ### Looking for maintainers! + I have almost zero experience in ios development, and I am no longer working on mobile app development(doing backend and devops works mainly and some web frontend). It makes it hard to maintain this library actively. Hope to have someone to help maintaining react-native-version-check! ### expo + react-native-version-check supports [expo](https://expo.io)! with [react-native-version-check-expo](https://www.npmjs.com/package/react-native-version-check-expo) + - usage + ```js // import -import VersionCheck from 'react-native-version-check-expo' +import VersionCheck from 'react-native-version-check-expo'; -VersionCheck.getCountry().then(country => console.log(country)) +VersionCheck.getCountry().then(country => console.log(country)); ``` ## Getting started - - npm - ```bash - $ npm install react-native-version-check - ``` - - yarn - ```bash - $ yarn add react-native-version-check - ``` + +- npm + +```bash +$ npm install react-native-version-check +``` + +- yarn + +```bash +$ yarn add react-native-version-check +``` ### Example + ```bash $ git clone https://github.com/kimxogus/react-native-version-check.git $ cd react-native-version-check/example @@ -42,35 +51,43 @@ $ react-native run-android # or react-native run-ios ``` ### Automatic Installation + ```bash $ react-native link react-native-version-check ``` ### Manual Installation + #### - iOS - Link Manually -* Add ```.xcodeproj``` file as library to XCode project. + +- Add `.xcodeproj` file as library to XCode project. + 1. In project navigator, right click Libraries - 2. Select ```Add Files to [PROJECT_NAME]``` - 3. Add the ```node_modules/react-native-version-check/ios/RNVersionCheck.xcodeproj``` file + 2. Select `Add Files to [PROJECT_NAME]` + 3. Add the `node_modules/react-native-version-check/ios/RNVersionCheck.xcodeproj` file -* Add the ```libRNVersionCheck.a``` from the ```RNVersionCheck``` project to your project's Build Phases > Link Binary With Libraries +- Add the `libRNVersionCheck.a` from the `RNVersionCheck` project to your project's Build Phases > Link Binary With Libraries #### iOS - CocoaPods Package Manager -* Add to your `Podfile` (assuming it's in `ios/Podfile`): + +- Add to your `Podfile` (assuming it's in `ios/Podfile`): ```ruby pod 'react-native-version-check', :path => '../node_modules/react-native-version-check' ``` -* Reinstall pod with `cd ios && pod install && cd ..` +- Reinstall pod with `cd ios && pod install && cd ..` #### - Android -* Append the following lines to `android/settings.gradle`: +- Append the following lines to `android/settings.gradle`: + ```gradle ... include ':react-native-version-check' project(':react-native-version-check').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-version-check/android') ``` -* Insert the following lines inside the dependencies block in `android/app/build.gradle`: + +- Insert the following lines inside the dependencies block in `android/app/build.gradle`: + ```gradle ... dependencies { @@ -78,7 +95,9 @@ dependencies { compile project(':react-native-version-check') } ``` -* Open up `android/app/src/main/java/[...]/MainApplication.java` + +- Open up `android/app/src/main/java/[...]/MainApplication.java` + ```java ...... import io.xogus.reactnative.versioncheck.RNVersionCheckPackage; // <--- HERE @@ -94,79 +113,80 @@ protected List getPackages() { ``` ## Usage + ```javascript import { Linking } from 'react-native'; import VersionCheck from 'react-native-version-check'; -VersionCheck.getCountry() - .then(country => console.log(country)); // KR -console.log(VersionCheck.getPackageName()); // com.reactnative.app +VersionCheck.getCountry().then(country => console.log(country)); // KR +console.log(VersionCheck.getPackageName()); // com.reactnative.app console.log(VersionCheck.getCurrentBuildNumber()); // 10 -console.log(VersionCheck.getCurrentVersion()); // 0.1.1 +console.log(VersionCheck.getCurrentVersion()); // 0.1.1 -VersionCheck.getLatestVersion() - .then(latestVersion => { - console.log(latestVersion); // 0.1.2 - }); +VersionCheck.getLatestVersion().then(latestVersion => { + console.log(latestVersion); // 0.1.2 +}); VersionCheck.getLatestVersion({ - provider: 'appStore' // for iOS - }) - .then(latestVersion => { - console.log(latestVersion); // 0.1.2 - }); + provider: 'appStore', // for iOS +}).then(latestVersion => { + console.log(latestVersion); // 0.1.2 +}); VersionCheck.getLatestVersion({ - provider: 'playStore' // for Android - }) - .then(latestVersion => { - console.log(latestVersion); // 0.1.2 - }); + provider: 'playStore', // for Android +}).then(latestVersion => { + console.log(latestVersion); // 0.1.2 +}); -VersionCheck.getLatestVersion() // Automatically choose profer provider using `Platform.select` by device platform. +VersionCheck.getLatestVersion() // Automatically choose profer provider using `Platform.select` by device platform. .then(latestVersion => { - console.log(latestVersion); // 0.1.2 + console.log(latestVersion); // 0.1.2 }); VersionCheck.getLatestVersion({ forceUpdate: true, - provider: () => fetch('http://your.own/api') - .then(r => r.json()) - .then(({version}) => version), // You can get latest version from your own api. -}).then(latestVersion =>{ + provider: () => + fetch('http://your.own/api') + .then(r => r.json()) + .then(({ version }) => version), // You can get latest version from your own api. +}).then(latestVersion => { console.log(latestVersion); }); -VersionCheck.needUpdate() - .then(async res => { - console.log(res.isNeeded); // true - if (res.isNeeded) { - Linking.openURL(res.storeUrl); // open store if update is needed. - } +VersionCheck.getLatestUpdatedTime() // You can check when the app is last updated. + .then(updatedTime => { + console.log(updateTime); // 2022-05-30T16:00:00.000Z (Date) }); +VersionCheck.needUpdate().then(async res => { + console.log(res.isNeeded); // true + if (res.isNeeded) { + Linking.openURL(res.storeUrl); // open store if update is needed. + } +}); + VersionCheck.needUpdate({ - depth: 2 + depth: 2, }).then(res => { console.log(res.isNeeded); // false; because first two fields of current and the latest versions are the same as "0.1". }); VersionCheck.needUpdate({ - currentVersion: "1.0", - latestVersion: "2.0" + currentVersion: '1.0', + latestVersion: '2.0', }).then(res => { - console.log(res.isNeeded); // true + console.log(res.isNeeded); // true }); VersionCheck.needUpdate({ depth: 1, - currentVersion: "2.1", - latestVersion: "2.0", + currentVersion: '2.1', + latestVersion: '2.0', }).then(res => { - console.log(res.isNeeded); // false + console.log(res.isNeeded); // false }); - ``` ## Methods @@ -176,58 +196,71 @@ VersionCheck.needUpdate({ - #**`getCurrentBuildNumber()`** _(buildNumber: Number)_ - Returns current app build number. - #**`getStoreUrl([option: Object])`** _(Promise)_ - Returns url of Play Market or App Store of app. - #**`getAppStoreUrl([option: Object])`** _(Promise)_ - Returns url of App Store of app. + - Option - Field | Type | Default - --- | --- | --- - appID | _string_ | App ID - ignoreErrors | _boolean_ | true + | Field | Type | Default | + | ------------ | --------- | ------- | + | appID | _string_ | App ID | + | ignoreErrors | _boolean_ | true | + - #**`getPlayStoreUrl([option: Object])`** _(Promise)_ - Returns url of Play Store of app. + - Option - Field | Type | Default - --- | --- | --- - packageName | _string_ | Package Name - ignoreErrors | _boolean_ | true + | Field | Type | Default | + | ------------ | --------- | ------------ | + | packageName | _string_ | Package Name | + | ignoreErrors | _boolean_ | true | - #**`getCurrentVersion()`** _(currentVersion: String)_ - Returns current app version. -- #**`getLatestVersion([option: Object])`** _(Promise)_ - Returns the latest app version parsed from url. Returns `null` when parsing error occurs. +- #**`getLatestVersion([option: Object])`** _(Promise)_ - Returns the latest app version parsed from url. + - Option - Field | Type | Default - --- | --- | --- - forceUpdate | _boolean_ | ```false``` - provider | _string_ or _function_ | provider name or function that returns promise or value of the latest version - fetchOptions | _object_ | isomorphic-fetch options (https://github.github.io/fetch/) - ignoreErrors | _boolean_ | true + | Field | Type | Default | + | ------------ | ---------------------- | ----------------------------------------------------------------------------- | + | forceUpdate | _boolean_ | `false` | + | provider | _string_ or _function_ | provider name or function that returns promise or value of the latest version | + | fetchOptions | _object_ | isomorphic-fetch options (https://github.github.io/fetch/) | + | ignoreErrors | _boolean_ | true | + +* #**`getLatestUpdatedTime([option: Object])`** _(Promise)_ - Returns the latest app updated time parsed from url. -- #**`needUpdate([option: Object])`** _(Promise)_ - Returns an object contains with boolean value whether update needed, current version and latest version. Current and the latest app versions are first split by delimiter, and check each split numbers into depth. - Option - Field | Type | Default - --- | --- | --- - currentVersion | _string_ | app's current version from [getCurrentVersion()](#getCurrentVersion) - latestVersion | _string_ | app's latest version from [getLatestVersion()](#getLatestVersion) - depth | _number_ | ```Infinity``` - forceUpdate | _boolean_ | ```false``` - provider | _string_ or _function_ | provider name or function that returns promise or value of the latest version - fetchOptions | _object_ | isomorphic-fetch options (https://github.github.io/fetch/) - ignoreErrors | _boolean_ | true + | Field | Type | Default | + | ------------ | ---------------------- | ----------------------------------------------------------------------------- | + | provider | _string_ or _function_ | provider name or function that returns promise or value of the latest version | + | fetchOptions | _object_ | isomorphic-fetch options (https://github.github.io/fetch/) | + | ignoreErrors | _boolean_ | true | - - Result +* #**`needUpdate([option: Object])`** _(Promise)_ - Returns an object contains with boolean value whether update needed, current version and latest version. Current and the latest app versions are first split by delimiter, and check each split numbers into depth. + + - Option - Field | Type - --- | --- - isNeeded | _boolean_ - storeUrl | _string_ - currentVersion | _string_ - latestVersion | _string_ + | Field | Type | Default | + | -------------- | ---------------------- | ----------------------------------------------------------------------------- | + | currentVersion | _string_ | app's current version from [getCurrentVersion()](#getCurrentVersion) | + | latestVersion | _string_ | app's latest version from [getLatestVersion()](#getLatestVersion) | + | depth | _number_ | `Infinity` | + | forceUpdate | _boolean_ | `false` | + | provider | _string_ or _function_ | provider name or function that returns promise or value of the latest version | + | fetchOptions | _object_ | isomorphic-fetch options (https://github.github.io/fetch/) | + | ignoreErrors | _boolean_ | true | + - Result + | Field | Type | + | -------------- | --------- | + | isNeeded | _boolean_ | + | storeUrl | _string_ | + | currentVersion | _string_ | + | latestVersion | _string_ | ## License -MIT +MIT [npm-image]: https://img.shields.io/npm/v/react-native-version-check.svg [npm-url]: https://npmjs.org/package/react-native-version-check diff --git a/packages/react-native-version-check/builder.js b/packages/react-native-version-check/builder.js index 2ed3a84e..dae14cf8 100644 --- a/packages/react-native-version-check/builder.js +++ b/packages/react-native-version-check/builder.js @@ -1,5 +1,6 @@ // @flow import { getLatestVersion } from './src/getLatestVersion'; +import { getLatestUpdatedTime } from './src/getLatestUpdatedTime'; import needUpdate from './src/needUpdate'; import { setVersionInfo, type IVersionInfo } from './src/versionInfo'; import getStoreUrl, { @@ -12,6 +13,7 @@ export interface ReactNativeVersionCheck extends IVersionInfo { getAppStoreUrl: typeof getAppStoreUrl; getPlayStoreUrl: typeof getPlayStoreUrl; getLatestVersion: typeof getLatestVersion; + getLatestUpdatedTime: typeof getLatestUpdatedTime; needUpdate: typeof needUpdate; } @@ -28,6 +30,7 @@ export default (VersionInfoObject: IVersionInfo): ReactNativeVersionCheck => { getAppStoreUrl, getPlayStoreUrl, getLatestVersion, + getLatestUpdatedTime, needUpdate, }; diff --git a/packages/react-native-version-check/package.json b/packages/react-native-version-check/package.json index 76d62dbe..d7d1cf0b 100644 --- a/packages/react-native-version-check/package.json +++ b/packages/react-native-version-check/package.json @@ -1,5 +1,5 @@ { - "name": "react-native-version-check", + "name": "@mydidian/react-native-version-check", "version": "3.4.7", "description": "A version checker for react-native applications", "main": "index.js", @@ -32,6 +32,7 @@ "lodash.isfunction": "^3.0.9", "lodash.isnil": "^4.0.0", "lodash.pick": "^4.4.0", + "luxon": "^3.3.0", "semver": "^6.1.1" }, "devDependencies": { diff --git a/packages/react-native-version-check/src/getLatestUpdatedTime.js b/packages/react-native-version-check/src/getLatestUpdatedTime.js new file mode 100644 index 00000000..8d2814bc --- /dev/null +++ b/packages/react-native-version-check/src/getLatestUpdatedTime.js @@ -0,0 +1,69 @@ +// @flow +import { Platform } from 'react-native'; +import isFunction from 'lodash.isfunction'; + +import * as providers from './providers'; +import { IProvider, IVersionAndStoreUrl } from './providers/types'; + +export type GetLatestUpdatedTimeOption = { + provider?: string | Function | IProvider, + fetchOptions?: any, + ignoreErrors?: boolean, +}; + +export const defaultOption: GetLatestUpdatedTimeOption = { + ignoreErrors: true, + provider: Platform.select({ + ios: 'appStore', + android: 'playStore', + }), +}; + +const error = { + message: + "Parse Error. Your app's play store page doesn't seem to have latest app version info.", +}; + +export async function getLatestUpdatedTime( + option: ?GetLatestUpdatedTimeOption = {} +): Promise { + try { + option = { ...defaultOption, ...option }; + + if (option.provider.getVersion) { + const { + updatedTime, + }: IVersionAndStoreUrl = await option.provider.getVersion(option); + + if (updatedTime instanceof Date) { + return Promise.resolve(updatedTime); + } + + return Promise.reject(error); + } + + if (providers[option.provider]) { + const { updatedTime }: IVersionAndStoreUrl = await providers[ + option.provider + ].getVersion(option); + + if (updatedTime instanceof Date) { + return Promise.resolve(updatedTime); + } + + return Promise.reject(error); + } + + if (isFunction(option.provider)) { + return Promise.resolve(option.provider(option)); + } + + return Promise.reject(`Invalid provider: ${option.provider}`); + } catch (e) { + if (option.ignoreErrors) { + console.warn(e); // eslint-disable-line no-console + } else { + throw e; + } + } +} diff --git a/packages/react-native-version-check/src/providers/__tests__/playStore.js b/packages/react-native-version-check/src/providers/__tests__/playStore.js index 3fe9d832..5085cefc 100644 --- a/packages/react-native-version-check/src/providers/__tests__/playStore.js +++ b/packages/react-native-version-check/src/providers/__tests__/playStore.js @@ -36,7 +36,7 @@ describe('PlayStoreProvider get version from older Play Store layouts', () => { ); await PlayStoreProvider.getVersion(options).then(r => - expect(r).toEqual({ version: '0.10.0', storeUrl }) + expect(r).toEqual({ version: '0.10.0', updatedTime: undefined, storeUrl }) ); }); @@ -57,7 +57,7 @@ describe('PlayStoreProvider get version from older Play Store layouts', () => { ); await PlayStoreProvider.getVersion(options).then(r => - expect(r).toEqual({ version: '0.10.0', storeUrl }) + expect(r).toEqual({ version: '0.10.0', updatedTime: undefined, storeUrl }) ); }); }); @@ -82,7 +82,7 @@ describe('PlayStoreProvider get version current (since ~Dec, 2018) Play Store', ); await PlayStoreProvider.getVersion(options).then(r => - expect(r).toEqual({ version: '0.10.0', storeUrl }) + expect(r).toEqual({ version: '0.10.0', updatedTime: undefined, storeUrl }) ); }); @@ -105,7 +105,7 @@ describe('PlayStoreProvider get version current (since ~Dec, 2018) Play Store', ); await PlayStoreProvider.getVersion(options).then(r => - expect(r).toEqual({ version: '234', storeUrl }) + expect(r).toEqual({ version: '234', updatedTime: undefined, storeUrl }) ); }); }); @@ -113,21 +113,29 @@ describe('PlayStoreProvider get version current (since ~Dec, 2018) Play Store', describe('PlayStoreProvider get version current (since ~May, 2022) Play Store', () => { it('with format x.x.x', async () => { mockSuccesfulResponse( - 'null,null,[[null,1]],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[2],null,[[["0.10.0"]],[[[30,"11"]],[[[16,"4.1"]]]],[["May 31, 2022"]]]' + 'null,null,[[null,1]],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[2],null,[[["0.10.0"]],[[[30,"11"]],[[[16,"4.1"]]]],[["May 31, 2022",[1684168484,717000000]]]]' ); await PlayStoreProvider.getVersion(options).then(r => - expect(r).toEqual({ version: '0.10.0', storeUrl }) + expect(r).toEqual({ + version: '0.10.0', + updatedTime: new Date('May 31, 2022'), + storeUrl, + }) ); }); it('with format xxx', async () => { mockSuccesfulResponse( - 'null,null,[[null,1]],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[2],null,[[["234"]],[[[30,"11"]],[[[16,"4.1"]]]],[["May 31, 2022"]]]' + 'null,null,[[null,1]],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[2],null,[[["234"]],[[[30,"11"]],[[[16,"4.1"]]]],[["May 31, 2022",[1684372379,891000000]]]]' ); await PlayStoreProvider.getVersion(options).then(r => - expect(r).toEqual({ version: '234', storeUrl }) + expect(r).toEqual({ + version: '234', + updatedTime: new Date('May 31, 2022'), + storeUrl, + }) ); }); }); diff --git a/packages/react-native-version-check/src/providers/appStore.js b/packages/react-native-version-check/src/providers/appStore.js index c3dc836f..46161bd9 100644 --- a/packages/react-native-version-check/src/providers/appStore.js +++ b/packages/react-native-version-check/src/providers/appStore.js @@ -36,11 +36,20 @@ class AppStoreProvider implements IProvider { .then(res => res.json()) .then(json => { if (json.resultCount) { - const version = json.results[0].version; - const appId = json.results[0].trackId; + const result = json.results[0]; + const version = result.version; + const updatedTime = (() => { + const datetime = result.currentVersionReleaseDate + ? Date.parse(result.currentVersionReleaseDate) + : undefined; + + return datetime ? new Date(datetime) : undefined; + })(); + const appId = result.trackId; const storeUrl = `itms-apps://apps.apple.com/${countryCode}app/id${appId}`; return Promise.resolve({ version, + updatedTime, storeUrl, }); } diff --git a/packages/react-native-version-check/src/providers/playStore.js b/packages/react-native-version-check/src/providers/playStore.js index 3efba47c..77108044 100644 --- a/packages/react-native-version-check/src/providers/playStore.js +++ b/packages/react-native-version-check/src/providers/playStore.js @@ -1,4 +1,6 @@ // @flow +import { DateTime } from 'luxon'; + import { getVersionInfo } from '../versionInfo'; import { IProvider, IVersionAndStoreUrl } from './types'; @@ -39,18 +41,37 @@ class PlayStoreProvider implements IProvider { return fetch(storeUrl, opt.fetchOptions) .then(res => res.text()) .then(text => { - const match = text.match(/Current Version.+?>([\d.-]+)<\/span>/); - if (match) { - const latestVersion = match[1].trim(); + const version = (() => { + const match = text.match(/Current Version.+?>([\d.-]+)<\/span>/); + if (match) { + return match[1].trim(); + } - return Promise.resolve({ version: latestVersion, storeUrl }); - } + const matchNewLayout = text.match(/\[\[\["([\d.]+?)"\]\]/); + if (matchNewLayout) { + return matchNewLayout[1].trim(); + } + + return undefined; + })(); + const updatedTime = (() => { + const match = text.match( + /\]\],\[\["([a-zA-Z,0-9\s]+?)",\[\d+,\d+\]\]\]/ + ); + if (match) { + const datetime = DateTime.fromFormat( + match[1].trim(), + 'MMM d, yyyy' + ); + + return datetime.toJSDate(); + } - const matchNewLayout = text.match(/\[\[\["([\d.]+?)"\]\]/); - if (matchNewLayout) { - const latestVersion = matchNewLayout[1].trim(); + return undefined; + })(); - return Promise.resolve({ version: latestVersion, storeUrl }); + if (version) { + return Promise.resolve({ version, updatedTime, storeUrl }); } return Promise.reject(error(text)); diff --git a/packages/react-native-version-check/src/providers/types.js b/packages/react-native-version-check/src/providers/types.js index 78a81f6d..0c7c4fa6 100644 --- a/packages/react-native-version-check/src/providers/types.js +++ b/packages/react-native-version-check/src/providers/types.js @@ -5,5 +5,6 @@ export interface IProvider { export interface IVersionAndStoreUrl { version: string; + updatedTime: Date | undefined; storeUrl: string; }