detectVersion static method
Detects one stable marketing version from conventional project files.
Literal MARKETING_VERSION build settings take precedence over a literal
CFBundleShortVersionString. Dynamic build-setting references are
ignored.
Implementation
static Future<String?> detectVersion(String appRoot) async {
final iosDirectory = Directory(p.join(appRoot, 'ios'));
if (!(await iosDirectory.exists())) return null;
final projects =
(await iosDirectory.list(followLinks: false).toList())
.whereType<Directory>()
.where((entry) => p.extension(entry.path) == '.xcodeproj')
.toList()
..sort((left, right) => left.path.compareTo(right.path));
final projectVersions = <String>{};
for (final project in projects) {
final settings = File(p.join(project.path, 'project.pbxproj'));
if (!(await settings.exists())) continue;
final source = await settings.readAsString();
projectVersions.addAll(
_stableVersions(
RegExp(
r'^\s*MARKETING_VERSION\s*=\s*"?([^";]+)"?;\s*$',
multiLine: true,
).allMatches(source).map((match) => match.group(1)),
),
);
}
if (projectVersions.isNotEmpty) {
return _oneVersion(projectVersions);
}
final infoPlist = File(p.join(iosDirectory.path, 'Runner', 'Info.plist'));
if (!(await infoPlist.exists())) return null;
final source = await infoPlist.readAsString();
final match = RegExp(
r'<key>\s*CFBundleShortVersionString\s*</key>\s*'
r'<string>\s*([^<$]+)\s*</string>',
multiLine: true,
).firstMatch(source);
final versions = _stableVersions(<String?>[match?.group(1)]);
return versions.isEmpty ? null : versions.single;
}