addFirebase method
Implementation
String addFirebase() {
try {
final file = File(_pbxprojPath);
var content = file.readAsStringSync();
if (content.contains('GoogleService-Info.plist')) {
return '⚠️ project.pbxproj — GoogleService-Info.plist already registered, skipped';
}
// Verify all required anchors before touching the file.
const anchors = [
'/* Begin PBXBuildFile section */',
'/* Begin PBXFileReference section */',
// Tab-prefixed + comma-suffixed forms appear exactly once in their
// correct sections, preventing replaceFirst from hitting the wrong
// occurrence (e.g. a fileRef = ... line in PBXBuildFile).
'\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,',
'\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,',
];
for (final anchor in anchors) {
if (!content.contains(anchor)) {
return '❌ Could not modify project.pbxproj — unexpected file structure.\n'
'Please check your project manually or open an issue on GitHub.';
}
}
final uuidBuildFile = generatePbxUuid();
final uuidFileRefGoogle = generatePbxUuid();
final uuidFileRefEntitlements = generatePbxUuid();
// 1. Add PBXBuildFile entry
content = content.replaceFirst(
'/* Begin PBXBuildFile section */',
'/* Begin PBXBuildFile section */\n'
'\t\t$uuidBuildFile /* GoogleService-Info.plist in Resources */ = '
'{isa = PBXBuildFile; fileRef = $uuidFileRefGoogle /* GoogleService-Info.plist */; };',
);
// 2. Add PBXFileReference entries
content = content.replaceFirst(
'/* Begin PBXFileReference section */',
'/* Begin PBXFileReference section */\n'
'\t\t$uuidFileRefEntitlements /* Runner.entitlements */ = '
'{isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; '
'path = Runner.entitlements; sourceTree = "<group>"; };\n'
'\t\t$uuidFileRefGoogle /* GoogleService-Info.plist */ = '
'{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; '
'path = "GoogleService-Info.plist"; sourceTree = "<group>"; };',
);
// 3. Add files to Runner PBXGroup children (before Main.storyboard).
// Anchor includes 4-tab prefix + trailing comma so replaceFirst hits
// the children-list line and not a fileRef = ... occurrence elsewhere.
const groupAnchor =
'\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,';
content = content.replaceFirst(
groupAnchor,
'\t\t\t\t$uuidFileRefEntitlements /* Runner.entitlements */,\n'
'\t\t\t\t$uuidFileRefGoogle /* GoogleService-Info.plist */,\n'
'$groupAnchor',
);
// 4. Add to PBXResourcesBuildPhase files list (before Assets.xcassets).
// Same reasoning: include tabs + comma for uniqueness.
const resourcesAnchor =
'\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,';
content = content.replaceFirst(
resourcesAnchor,
'\t\t\t\t$uuidBuildFile /* GoogleService-Info.plist in Resources */,\n'
'$resourcesAnchor',
);
// 5-7. Add CODE_SIGN_ENTITLEMENTS to all 3 Runner build configurations
// (Debug, Release, Profile) — only blocks that also have INFOPLIST_FILE.
if (!content.contains(
'CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;')) {
content = _addCodeSignEntitlements(content);
}
file.writeAsStringSync(content);
return '✅ Updated project.pbxproj — registered GoogleService-Info.plist';
} catch (e) {
return '❌ Failed to modify project.pbxproj: $e';
}
}