6

I've encountered what I think is a bug in iOS16: When a localized string is passed from Swift to Objective-C and compared with another identical localized string (defined in Objective-CC), the result can be false and the parameter order can affect the result. See the demo:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let tc = TestClass()
        tc.receive(NSLocalizedString("Start", comment:""))
    }
}
@implementation TestClass
- (void)receive:(NSString *)swiftString {
    NSString *objcString = NSLocalizedString(@"Start", @"");
    BOOL result1 = [swiftString isEqualToString:objcString];
    BOOL result2 = [objcString isEqualToString:swiftString];
    NSLog(@"result: %d, %d", result1, result2);
}
@end

It is Localizable (take Japanese as one example, but any writing system other than Latin can reproduce the bug):

"Start" = "開始";

output:

result: 0, 1

We don't know whether the root cause of this is from NSLocalizedString() or -isEqualToString. This doesn't happen on iOS15.

Has anyone else encountered this bug?

TylerH
  • 20,799
  • 66
  • 75
  • 101
刘maxwell
  • 187
  • 10
  • 4
    Since you have a good minimal reproducible example, and it does indeed look like a bug, please file a Feedback with Apple. – DarkDust Oct 17 '22 at 06:48
  • 2
    Can you please add`NSLog(@"swiftString: %@", [[swiftString dataUsingEncoding:NSUTF32StringEncoding] debugDescription]);` and `NSLog(@"objcString: %@", [[objcString dataUsingEncoding:NSUTF32StringEncoding] debugDescription]);` to the Objective-C method and show the output? – Martin R Oct 17 '22 at 08:28
  • 1
    Exactly the same. ``` 2022-10-17 18:50:42.122946+0800 SwiftOC[94706:1197047] swiftString: 2022-10-17 18:50:42.123111+0800 SwiftOC[94706:1197047] objcString: ``` – 刘maxwell Oct 17 '22 at 10:51

1 Answers1

6

That definitely looks like a bug, I could reproduce it in the Xcode iOS 16 Simulator. The debugger shows that the string obtained by

NSString *objcString = NSLocalizedString(@"Start", @"");

is an instance of _NSBPlistMappedString, an undocumented subclass of NSString:

(lldb) p objcString
(_NSBPlistMappedString *) $1 = 0x8230ceeb696930f7
(lldb) p [objcString superclass]
(Class) $2 = NSString

Apparently the comparison of a Swift string with instances of that subclass is not implemented correctly in iOS 16. Here are two possible workaround:

Workaround #1: Use compare instead of isEqualToString:

NSString *objcString = NSLocalizedString(@"Start", @"");
BOOL result1 = [swiftString compare:objcString] == NSOrderedSame;
BOOL result2 = [objcString compare:swiftString] == NSOrderedSame;
NSLog(@"result: %d, %d", result1, result2);
// result: 1, 1

Workaround #2: Ensure that objcString is an instance of NSString:

NSString *objcString = @(NSLocalizedString(@"Start", @"").UTF8String);
BOOL result1 = [swiftString isEqualToString:objcString];
BOOL result2 = [objcString isEqualToString:swiftString];
NSLog(@"result: %d, %d", result1, result2);
// result: 1, 1

Of course both workarounds are not very satisfying. As suggested in the comments, the bug should be reported to Apple.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382