Skip to content

Commit

Permalink
deps: Use expo-application for app version number.
Browse files Browse the repository at this point in the history
Primarily to prove the correctness of our `react-native-unimodules`
installation, install expo-application and use it instead of
`react-native-device-info` in those places where it reads the
application's version.

NOTE: After discussing it at
https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/unimodules.20unmet.20peer.20dependency/near/849254,
we decided to ignore the following warning:

warning " > expo-application@2.1.0" has unmet peer dependency "@unimodules/core@~1.0.0".

I filed expo/expo#7728 for it, but
basically, the latest version of expo-application (2.1.0) declares
that it depends on a version of @unimodules/core (1.0.0) that was
already several months out-of-date at the time expo-application was
first created, and it seems unlikely that this was well-considered.
In our testing so far, everything seems to work correctly, and the
vast majority of unimodules don't specify a version constraint at
all. But this adds to the need to be wary about Expo's handling of
version constraints in general; see the notes on the commit, earlier
in this series, that introduces unimodules.

To choose between `nativeApplicationVersion` and
`nativeBuildVersion`, the deciding factor was which one of these
more closely mirrors the behavior of `getVersion` from
`react-native-device-info`. (`getVersion` isn't documented well
enough to give the answer outright.)

`react-native-device-info` is missing documentation on where they
get the version number, so we dug into the code and found it:

- iOS: `CFBundleShortVersionString` from `info.plist`
- Android: `PackageInfo#versionName` (doc at
  https://developer.android.com/reference/android/content/pm/PackageInfo#versionName)

From inspecting `expo-application` code, it turns out that these
same exact sources are used for their `nativeApplicationVersion`.
The `expo-application` doc for Android actually refers to an
`app.json`, which we don't have, and it doesn't mention
`versionName`. Maintaining an `app.json` is a normal part of
development when you're working entirely in the Expo ecosystem
(which we're not), and it's documented
(https://docs.expo.io/versions/latest/workflow/configuration/#version)
that the "version" key there does indeed correspond to the
`versionName` we found by looking in the code.

So, use `nativeApplicationVersion`.

We don't expect `nativeApplicationVersion` to ever be null, but the
type annotation indicates it might be, so handle that case with a
"?.?.?" string.
  • Loading branch information
Chris Bobbe authored and gnprice committed Apr 8, 2020
1 parent 77785f5 commit 18c37ce
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class BasePackageList {
public List<Package> getPackageList() {
return Arrays.<Package>asList(
new expo.modules.application.ApplicationPackage(),
new expo.modules.constants.ConstantsPackage(),
new expo.modules.filesystem.FileSystemPackage(),
new expo.modules.permissions.PermissionsPackage()
Expand Down
30 changes: 30 additions & 0 deletions flow-typed/npm/expo-application_vx.x.x.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// flow-typed signature: bdd6d740ed1472fde8ecf483ba589452
// flow-typed version: <<STUB>>/expo-application_v2.1.0/flow_v0.92.0

/**
* Flowtype definitions for Application
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.10.0
*/
declare module 'expo-application' {
declare export var nativeApplicationVersion: string | null;
declare export var nativeBuildVersion: string | null;
declare export var applicationName: string | null;
declare export var applicationId: string | null;
declare export var androidId: string | null;
declare export function getInstallReferrerAsync(): Promise<string>;
declare export function getIosIdForVendorAsync(): Promise<string>;

declare export var ApplicationReleaseType: {|
+UNKNOWN: 0, // 0
+SIMULATOR: 1, // 1
+ENTERPRISE: 2, // 2
+DEVELOPMENT: 3, // 3
+AD_HOC: 4, // 4
+APP_STORE: 5, // 5
|};
declare export function getIosApplicationReleaseTypeAsync(): Promise<$Values<typeof ApplicationReleaseType>, >;
declare export function getIosPushNotificationServiceEnvironmentAsync(): Promise<string>;
declare export function getInstallationTimeAsync(): Promise<Date>;
declare export function getLastUpdateTimeAsync(): Promise<Date>;
}
7 changes: 7 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
PODS:
- boost-for-react-native (1.63.0)
- DoubleConversion (1.1.6)
- EXApplication (2.1.0):
- UMCore
- EXAppLoaderProvider (7.0.0)
- EXConstants (7.0.1):
- UMConstantsInterface
Expand Down Expand Up @@ -136,6 +138,7 @@ PODS:

DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXApplication (from `../node_modules/expo-application/ios`)
- EXAppLoaderProvider (from `../node_modules/expo-app-loader-provider/ios`)
- EXConstants (from `../node_modules/expo-constants/ios`)
- EXFileSystem (from `../node_modules/expo-file-system/ios`)
Expand Down Expand Up @@ -196,6 +199,9 @@ SPEC REPOS:
EXTERNAL SOURCES:
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXApplication:
:path: !ruby/object:Pathname
path: "../node_modules/expo-application/ios"
EXAppLoaderProvider:
:path: !ruby/object:Pathname
path: "../node_modules/expo-app-loader-provider/ios"
Expand Down Expand Up @@ -286,6 +292,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
EXApplication: 777c2f1742b381725b3e8c1da41f0113ebfd4694
EXAppLoaderProvider: 5d348813a9cf09b03bbe5b8b55437bc1bfbddbd1
EXConstants: 857aa7b1c84e2878f8402d712061860bca16a697
EXFileSystem: 7e53a2c30a2eb6987ba6d5158ab908f947523228
Expand Down
4 changes: 4 additions & 0 deletions jest/jestSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ jest.mock('react-native-simple-toast', () => ({
show: jest.fn(),
showWithGravity: jest.fn(),
}));

jest.mock('expo-application', () => ({
nativeApplicationVersion: '26.23.146',
}));
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"color": "^3.0.0",
"date-fns": "^1.29.0",
"katex": "^0.11.1",
"expo-application": "^2.1.0",
"lodash.escape": "^4.0.1",
"lodash.isequal": "^4.4.0",
"lodash.omit": "^4.5.0",
Expand Down
5 changes: 3 additions & 2 deletions src/diagnostics/DiagnosticsScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import React, { PureComponent } from 'react';
import { StyleSheet } from 'react-native';

import DeviceInfo from 'react-native-device-info';
import { nativeApplicationVersion } from 'expo-application';

import type { Dispatch } from '../types';
import { connect } from '../react-redux';
import { OptionButton, OptionDivider, Screen, RawLabel } from '../common';
Expand Down Expand Up @@ -31,7 +32,7 @@ class DiagnosticsScreen extends PureComponent<Props> {

return (
<Screen title="Diagnostics">
<RawLabel style={styles.versionLabel} text={`v${DeviceInfo.getVersion()}`} />
<RawLabel style={styles.versionLabel} text={`v${nativeApplicationVersion ?? '?.?.?'}`} />
<OptionDivider />
<OptionButton
label="Variables"
Expand Down
5 changes: 3 additions & 2 deletions src/sentry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* @flow strict-local */
import * as Sentry from '@sentry/react-native';
import DeviceInfo from 'react-native-device-info';
import { nativeApplicationVersion } from 'expo-application';

import config from './config';

export const isSentryActive = (): boolean => {
Expand Down Expand Up @@ -29,7 +30,7 @@ const preventNoise = (): void => {
normal version number -- preferably with your name and/or Github ID in
it. This will allow events produced by these debug builds to be easily
identified in the Sentry console. */
if (DeviceInfo.getVersion().match(/^\d+\.\d+\.\d+$/)) {
if (nativeApplicationVersion !== null && nativeApplicationVersion.match(/^\d+\.\d+\.\d+$/)) {
throw new Error('Sentry should not be initialized in debug builds');
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/utils/userAgent.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* @flow strict-local */
import { NativeModules } from 'react-native';
import DeviceInfo from 'react-native-device-info';
import { nativeApplicationVersion } from 'expo-application';

const { getVersion, getSystemName, getSystemVersion } = DeviceInfo;
const { getSystemName, getSystemVersion } = DeviceInfo;

export default !NativeModules.RNDeviceInfo
? ''
: `ZulipMobile/${getVersion()} (${getSystemName()} ${getSystemVersion()})`;
: `ZulipMobile/${nativeApplicationVersion ?? '?.?.?'} (${getSystemName()} ${getSystemVersion()})`;
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3627,6 +3627,11 @@ expo-app-loader-provider@~7.0.0:
resolved "https://registry.yarnpkg.com/expo-app-loader-provider/-/expo-app-loader-provider-7.0.0.tgz#9bfff831a204d0a8896e0120bce2209c4304ef03"
integrity sha512-C+5zpZN2T7PCj7weLs/ZgAC+y9dvu0VdTXD00Jf9Wo7Pxu/lsLh6ljg9JL91c+2tYDzMEODPNmT+JOUIxAr5zQ==

expo-application@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-2.1.0.tgz#ce5b13f025643e26478e5f7e4a89f21e96b2f908"
integrity sha512-JuWpToRjx81mNwJCL80Rf7wbEcNgyapqcfB3Iz1kqDSK8mF7ZmLL05dmdvPrqfGdhJImbMlDS4MTZU9ArUTZuQ==

expo-asset@~7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-7.0.0.tgz#6d2ba460dd43807f40580199c0b76c508eb1ca63"
Expand Down

0 comments on commit 18c37ce

Please sign in to comment.