What @tgf has is good for Android, but I wanted ios as well. The default Info.plist sets CFBundleShortVersionString to $(MARKETING_VERSION), which is the same as versionName in android/build.gradle, and CFBundleVersion is set to  $(CURRENT_PROJECT_VERSION), which is the same as versionCode in android/build.gradle. These are defined in the project.pbxproj in ios/AppName.xcodeproj/. Here is a prerelease script that can update the xcode file. If you have modifications that have no been committed yet, it will add 1 to the build number so that building without any changes will not result in a file change.
yarn add glob --dev
    "ios-update-version": "node scripts/iosUpdateVersion.js",
Use pre scripts to run this command.
scripts/iosUpdateVersion.js:
const { globSync } = require('glob');
var fs = require('fs');
const { execSync } = require('child_process');
const APP_VERSION = require('../package.json').version;
const GET_COMMIT_COUNT = 'git rev-list master --first-parent --count';
let BUILD_NUMBER = 0;
if (execSync('git status --porcelain').length) {
    console.log('increasing build number by one to include untracked changes');
    BUILD_NUMBER++;
}
const response = execSync(GET_COMMIT_COUNT).toString();
BUILD_NUMBER += Number.parseInt(response.trim());
// android (taken care of in build.gradle)
// versionCode googleVerver  // BUILD_NUMBER
// versionName userVer      // APP_VERSION
// ios
// CFBundleVersion CURRENT_PROJECT_VERSION = 1;            // BUILD_NUMBER
// CFBundleShortVersionString MARKETING_VERSION = 1.0;     // APP_VERSION
const plist = '/ios/SplitTheTank/Info.plist'
const xcodeprojs = globSync('ios/*.xcodeproj');
if (xcodeprojs.length === 0) {
    throw 'Could not find any xcodeproj folder'
}
const pbxproj = `${xcodeprojs[0]}/project.pbxproj`;
const data = fs.readFileSync(pbxproj, 'utf8');
let result = data.replace(/(CURRENT_PROJECT_VERSION = )(.*);/g, `$1${BUILD_NUMBER};`);
result = result.replace(/(MARKETING_VERSION = )(.*);/g, `$1${APP_VERSION};`);
fs.writeFileSync(pbxproj, result, 'utf8');