Skip to content

Conversation

smithemely
Copy link

  • fix: handle both quoted and unquoted bundle identifiers
  • feat: implement smarter base identifier detection logic
  • fix: properly preserve extensions with multiple segments

…edGuy#86)

* fix: handle both quoted and unquoted bundle identifiers
* feat: implement smarter base identifier detection logic
* fix: properly preserve extensions with multiple segments
@smithemely
Copy link
Author

I tested the updated implementation with various bundle identifier patterns in real projects, and it works correctly. The solution now handles:

  • Both quoted and unquoted identifiers
  • Extensions with multiple segments and dots
  • Preservation of specialized extension (.RunnerTests)
  • Proper detection of base identifiers

This fix ensures bundle identifiers are consistently renamed throughout the project file while maintaining the structure of complex extension identifiers. It's more robust than the previous implementation.

@OutdatedGuy
Copy link
Owner

I'll test this once I get some free time. Although I was thinking of adding an additional property where users can provide the old package name, so no need for any complex logic.

@OutdatedGuy OutdatedGuy changed the title fix(ios): improve bundle identifier detection and replacement (#86) fix(ios): improve bundle identifier detection and replacement Apr 29, 2025
@OutdatedGuy OutdatedGuy linked an issue Apr 29, 2025 that may be closed by this pull request
Copy link
Owner

@OutdatedGuy OutdatedGuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good to be. Just few questions and changes.


// Extract all bundle identifiers, accounting for both quoted and unquoted formats
final bundleIdRegex = RegExp(
r'PRODUCT_BUNDLE_IDENTIFIER = "?([^";]+)"?;',
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use only the allowed characters to match group. i.e. ``r'PRODUCT_BUNDLE_IDENTIFIER = "?([A-Za-z0-9.-_]+)"?;'`

// Extract all bundle identifiers, accounting for both quoted and unquoted formats
final bundleIdRegex = RegExp(
r'PRODUCT_BUNDLE_IDENTIFIER = "?([^";]+)"?;',
multiLine: true,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed AFAIK

final bundleIdentifierMatches = bundleIdRegex
.allMatches(iosProjectString)
.map((m) => m.group(1)!)
.toList();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to convert to list. Iterable is fine and faster

for (final identifier in bundleIdentifierMatches) {
bool isBaseForOthers = false;
for (final other in bundleIdentifierMatches) {
if (identifier != other && other.startsWith(identifier)) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is one value is com.example.app and other is com.example.application?

Comment on lines +190 to +201
// Special case for RunnerTests which might not be caught by the pattern above
if (identifierReplacements.containsKey(baseIdentifier)) {
newIosProjectString = newIosProjectString.replaceAll(
RegExp('PRODUCT_BUNDLE_IDENTIFIER = "$baseIdentifier\\.RunnerTests";'),
'PRODUCT_BUNDLE_IDENTIFIER = "$packageName.RunnerTests";',
);

newIosProjectString = newIosProjectString.replaceAll(
RegExp('PRODUCT_BUNDLE_IDENTIFIER = $baseIdentifier\\.RunnerTests;'),
'PRODUCT_BUNDLE_IDENTIFIER = $packageName.RunnerTests;',
);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? Looks redundant to me. Are there any special cases you faced that requires this?

Comment on lines +157 to +188
// Create a map of all identifiers to their new values
final identifierReplacements = <String, String>{};

for (final identifier in bundleIdentifierMatches) {
if (identifier == baseIdentifier) {
identifierReplacements[identifier] = packageName;
} else if (identifier.startsWith(baseIdentifier)) {
// This is an extension
final extension = identifier.substring(baseIdentifier.length);
identifierReplacements[identifier] = '$packageName$extension';
} else {
// This is an unrelated identifier, skip it
_logger.w('Skipping unrelated identifier: $identifier');
}
}

// Apply all replacements to the project file
var newIosProjectString = iosProjectString;

identifierReplacements.forEach((oldId, newId) {
// Replace unquoted format
newIosProjectString = newIosProjectString.replaceAll(
'PRODUCT_BUNDLE_IDENTIFIER = $oldId;',
'PRODUCT_BUNDLE_IDENTIFIER = $newId;',
);

// Replace quoted format
newIosProjectString = newIosProjectString.replaceAll(
'PRODUCT_BUNDLE_IDENTIFIER = "$oldId";',
'PRODUCT_BUNDLE_IDENTIFIER = "$newId";',
);
});
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be merged in a single logic? Like:

newIosProjectString = iosProjectString.replaceAllMapped(
  'PRODUCT_BUNDLE_IDENTIFIER = ("?)$baseIdentifier',
  (match) {
    final quote = match.group(1) ?? '';
    return 'PRODUCT_BUNDLE_IDENTIFIER = $quote$packageName';
  },
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Issues with PRODUCT_BUNDLE_IDENTIFIER handling in iOS projects
2 participants