2

I have a swift function to get the localized app name from the main bundle's localizedInfoDictionary with some fallback cases.

private func defaultAppName() -> String {
    var name: NSString = ""
    // Check for a localized version of the CFBundleDisplayName
    var mainBundle = NSBundle.mainBundle()
    // EXC_BAD_ACCESS HERE, despite the optional
    var mainBundleInfoDictionary: Dictionary<NSObject, AnyObject>? = mainBundle.localizedInfoDictionary

    if let infoDictionary = mainBundleInfoDictionary {
        name = infoDictionary["CFBundleDisplayName"] as NSString

        if (name.length == 0) {
            name = infoDictionary[kCFBundleNameKey] as NSString
        }
    }

    if (name.length == 0) {
        mainBundleInfoDictionary = mainBundle.infoDictionary

        if let infoDictionary = mainBundleInfoDictionary {
            name = infoDictionary["CFBundleDisplayName"] as NSString

            if (name.length == 0) {
                name = infoDictionary[kCFBundleNameKey] as NSString
            }
        }
    }

    return name
}

I know it's not concise, but I am still learning swift. The problem I am seeing is that accessing mainBundle.localizedInfoDictionary is not returning nil when not found, but rather throwing an EXC_BAD_ACCESS. I cannot guarantee that there will be a localized info in the apps where this function is used, yet the standard swift way of handling optionals is not working here.

How can I either catch the exception thrown by NSBundle's localizedInfoDictionary call or adapt my swift code to move on?

jww
  • 97,681
  • 90
  • 411
  • 885
coneybeare
  • 33,113
  • 21
  • 131
  • 183
  • It seems that there currently is no solution for this problem. See http://stackoverflow.com/questions/24710424/catch-an-exception-for-invalid-user-input-in-swift for a similar problem, and http://stackoverflow.com/questions/24023112/try-catch-exceptions-in-swift. – Martin R Sep 14 '14 at 18:39
  • Yeah, i didn't think there was a way to catch the exception. I am still interested in getting the localized name without it crashing though. – coneybeare Sep 14 '14 at 18:54

2 Answers2

6

First, this is a bug in Swift and should be reported to bugreport.apple.com. This method used to return an implicit optional. They should have converted it to an explicit optional, but instead converted it to a non-optional since the method can return nil:

NSDictionary *mainBundleInfoDictionary = [[NSBundle mainBundle] localizedInfoDictionary];
NSLog(@"%@", mainBundleInfoDictionary);

But you don't generally need localizedInfoDictionary. You can just ask NSBundle directly:

private func defaultAppName() -> String {
  let mainBundle = NSBundle.mainBundle()
  let displayName = mainBundle.objectForInfoDictionaryKey("CFBundleDisplayName") as? String
  let name = mainBundle.objectForInfoDictionaryKey(kCFBundleNameKey) as? String
  return displayName ?? name ?? "Unknown"
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

Maybe you get lucky by accessing the object directly? At least I had Playground behave better (without crashing) when doing it like this:

let name = mainBundle.objectForInfoDictionaryKey("CFBundleDisplayName") as? String

I know this does not directly address the localization problem. To also give an actionable suggestion, you could just make you own (duplicate) key in Localizable.strings.

Mundi
  • 79,884
  • 17
  • 117
  • 140
  • This is going to be part of some open source software. I cannot guarantee that the implementor will have localized his/her app – coneybeare Sep 14 '14 at 18:52