Get App Name in Swift - cocoa

How do I get the application name in Swift?
Googling gave me this:
[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleName"];
I converted it to Swift; error - method doesn't exist:
NSBundle.mainBundle().infoDictionary.objectForKey("CFBundleName")

This should work:
NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
infoDictionary is declared as a var infoDictionary: [NSObject : AnyObject]! so you have to unwrap it, access it as a Swift dictionary (rather than use objectForKey), and, as the result is an AnyObject, cast it.
Update Swift 3 (Xcode 8 beta 2)
Always better to use constants (and optionals) where possible, too:
Bundle.main.infoDictionary?[kCFBundleNameKey as String] as? String

I believe this solution is more elegant. What's more, using object(forInfoDictionaryKey:) is encouraged by Apple:
"Use of this method is preferred over other access methods because it returns the localized value of a key when one is available."
extension Bundle {
var displayName: String? {
return object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
}
}
Accessing bundle display name:
if let displayName = Bundle.main.displayName {
print(displayName)
}

I have created a simple extension to get the app name that is shown under the icon on the Home screen.
By default, apps only have CFBundleName set. Some apps, however, set CFBundleDisplayName (The user-visible name of the bundle) to change the title under the app icon. Adding spaces is often the case, e.g. bundle name "ExampleApp" could have bundle display name set to "Example App".
extension Bundle {
// Name of the app - title under the icon.
var displayName: String? {
return object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ??
object(forInfoDictionaryKey: "CFBundleName") as? String
}
}
Usage:
let appName = Bundle.main.displayName

Same answer in Swift 4.2
extension Bundle {
class var applicationName: String {
if let displayName: String = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String {
return displayName
} else if let name: String = Bundle.main.infoDictionary?["CFBundleName"] as? String {
return name
}
return "No Name Found"
}
}
you can use it like below
Bundle.applicationName
OR, An other way would be to avoid static or class method or property but to add to instance level.
extension Bundle {
var applicationName: String {
if let displayName: String = self.infoDictionary?["CFBundleDisplayName"] as? String {
return displayName
} else if let name: String = self.infoDictionary?["CFBundleName"] as? String {
return name
}
return "No Name Found"
}
}
and all it like following
Bundle.main.applicationName
Hope this helps :)

Swift 4
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String

simple way:
let appName = NSBundle.mainBundle().infoDictionary?[kCFBundleNameKey as String] as? String
convenient way:
extension NSBundle {
class func mainInfoDictionary(key: CFString) -> String? {
return self.mainBundle().infoDictionary?[key as String] as? String
}
}
print(NSBundle.mainInfoDictionary(kCFBundleNameKey))
kCFBundleNameKey – Standard Info.plist key, see more in CFBundle

// Returns app's name
public static var appDisplayName: String? {
if let bundleDisplayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
return bundleDisplayName
} else if let bundleName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String {
return bundleName
}
return nil
}

let appDisplayName = Bundle.main.infoDictionary?["CFBundleName"] as? String
It's optional, so put it in if let or guard statement.

All answers that just return CFBundleName will often not return the name the user expects, as if bundles have a CFBundleDisplayName, then this key is displayed by Finder, system frameworks, and most other apps.
Most answers just directly access the info dictionary but info dictionaries can be localized by string files and when accessing them directly, this localization is also ignored and thus again a wrong name may be returned, as Finder will display the localized name.
While CFBundleDisplayName is optional in Info.plist files, CFBundleName actually isn't, but if you forget to add it, nothing will break in your system, so you have a corrupt info dict, yet most users will probably never notice and in that case the code most answers may not return anything meaningful at all.
Here's my solution (Swift 3):
private
func stripFileExtension ( _ filename: String ) -> String {
var components = filename.components(separatedBy: ".")
guard components.count > 1 else { return filename }
components.removeLast()
return components.joined(separator: ".")
}
func nameOfApp ( ) -> String {
let bundle = Bundle.main
if let name = bundle.object(forInfoDictionaryKey: "CFBundleDisplayName")
?? bundle.object(forInfoDictionaryKey: kCFBundleNameKey as String),
let stringName = name as? String
{ return stringName }
let bundleURL = bundle.bundleURL
let filename = bundleURL.lastPathComponent
return stripFileExtension(filename)
}
How is this solution better?
It will check CFBundleDisplayName first and only fall back to CFBundleName if not present.
The object() method always operates on the localized version of the info dictionary, so if a localization exists, it will automatically be used.
If neither CFBundleDisplayName nor CFBundleName exist in the dictionary, the code falls back to just using the bundle filename on disk without the extension (so "My Cool App.app" will be "My Cool App"), this is a fallback so that this function will never return nil.

This one works for me in Swift 4.2
guard let dictionary = Bundle.main.infoDictionary else { return "" }
if let version: String = dictionary["CFBundleDisplayName"] as? String {
return version
} else {
return ""
}

This is what worked for me in Xcode 11.0 and Swift 5
let bundleID = Bundle.main.bundleIdentifier
let bundleInfoDict: NSDictionary = Bundle.main.infoDictionary! as NSDictionary
let appName = bundleInfoDict["CFBundleName"] as! String
print(bundleID!)
print(appName)

This should be more like what you are looking for:
let infoDictionary: NSDictionary = NSBundle.mainBundle().infoDictionary as NSDictionary!
let appName: NSString = infoDictionary.objectForKey("CFBundleName") as NSString
NSLog("Name \(appName)")
There may still be a better way to do this but it at least returns the app name correctly in my very limited testing...

Try this:
extension Bundle {
var displayName: String {
let name = object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
return name ?? object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String
}
}

let bundleInfoDict: NSDictionary = NSBundle.mainBundle().infoDictionary!
let appName = bundleInfoDict["CFBundleName"] as String

This one works perfect for me
let appName = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleDisplayName") as! String

For swift 5, iOS 13*
As mentioned before, it‘s an optional, so put it in a guard statement. I do this using a struct:
struct Model {
struct ProgVariablen{
static var appBundleName:String {
get {guard Bundle.main.infoDictionary != nil else {return ""}
return Bundle.main.infoDictionary!["CFBundleName"] as! String
}//end get
}//end computed property
static var appBundleShortVersion:String {
get {guard Bundle.main.infoDictionary != nil else {return ""}
return Bundle.main.infoDictionary ["CFBundleShortVersionString"] as! String
}//end get
}//end computed property
static var appBundleBuild:String {
get {guard Bundle.main.infoDictionary != nil else {return ""}
return Bundle.main.infoDictionary["CFBundleVersion"] as! String
}//end get
}//end computed property
//initialsieren der Variablen
init(
appBundleName:String,
appBundleShortVersion:String,
appBundleBuild:String,
)
{
// do here nothing for 'let'
// do here nothing for 'computed properties'
// your other ‘var’ are here like:
// ProgVariablen.var1 = var1
}//end init
}//end struct ProgVariablen
}//end struct Model
Usage:
print("Model.ProgVariablen.appBundleName: '\(Model.ProgVariablen.appBundleName)'")

extension NSApplication {
static var name: String {
Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String ?? ProcessInfo.processInfo.processName
}
}

// Bundle+appName.swift
extension Bundle {
var appName: String {
object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ??
object(forInfoDictionaryKey: "CFBundleName") as? String ??
""
}
}
// Usage (non optional)
let appName = Bundle.main.appName

Try this one,
let bundleID = NSBundle.mainBundle().bundleIdentifier

Related

Ambiguous use of Subscript Swift

I have a problem in fetching a String of a Dictionary inside NSUserdefaults, this is my code. I dont know what seem to be the problem:
static func getItemInUserDefaultsDictionary(key: String,dictionaryName: String) -> String {
return defaults.objectForKey(key)![dictionaryName] as? String ?? ""
}
The compiler does not know the proper type of objectForKey because it returns AnyObject
You have to cast the type:
static func getItemInUserDefaultsDictionary(key: String,dictionaryName: String) -> String {
guard let dictionary = defaults.objectForKey(key) as? [String:AnyObject] else { return "" }
return dictionary[dictionaryName] as? String ?? ""
}
try this
static func getItemInUserDefaultsDictionary(key: String,dictionaryName: String) -> String
{
return (defaults.objectForKey("key")![dictionaryName] as? String)!
}

Swift2 access component with string-name

Im more familiar with ActionScript3 and see many similarities in Swift2, kind of why i am trying out basic coding in Swift2 and Xcode.
Here's my example:
#IBOutlet weak var b1CurrSpeed: NSTextField!
I want to store b1CurrSpeed as a string so i could access the actual textfield component to set its default value when application is loaded.
I'm aiming for Swift2 for osx apps.
Here is a fictional example, not related to any actual code:
var tf:NSTextField = this.getItem("b1CurrSpeed");
tf.stringValue = "Hello world";
Reason to this approach is following...
I would like to store textfield value in NSUserDefaults, the key for defaults would be name of that textfield. So when looping thru the defaults, i would like to get key as string and when ive got that i'd have access to actual component to set its stringvalue property.
Tho, is that good approach in Swift / xCode ?
If you want to create a function for it, do someting like this:
func getStringForKey(key: String) -> String {
guard let result = NSUserDefaults.standardUserDefaults().objectForKey(key) as! String else { return "" }
return result
}
You can set the TextFields value with myTextField.text
Swift's Mirror type can get you close to it but it is limited to NSObject subclasses, can only access stored properties and is read-only.
Yet, there are ways around these limitations if your requirements will allow.
For example, here's an extension that will save and restore defaults values for all UITextfields on a view controller using the name you gave to each IBOutlet.
extension UIViewController
{
func loadDefaults(userDefaults: NSUserDefaults)
{
for prop in Mirror(reflecting:self).children
{
// add variants for each type/property you want to support
if let field = prop.value as? UITextField,
let name = prop.label
{
if let defaultValue = userDefaults.objectForKey(name) as? String
{ field.text = defaultValue }
}
}
}
func saveDefaults(userDefaults: NSUserDefaults)
{
for prop in Mirror(reflecting:self).children
{
if let field = prop.value as? UITextField,
let name = prop.label
{
if let currentValue = field.text
{ userDefaults.setObject(currentValue, forKey: name) }
}
}
}
}

How to fix `Ambiguous use of 'subscript'` every time?

I'm using this class that was written in Swift 1.2 and now I want to use it with Swift 2.0.
I get an error: Ambiguous use of 'subscript' # let artist = result["name"] as! String
} else if let jsonArtists = jsonResult["artists"] as? NSDictionary {
if let results:NSArray = jsonArtists["items"] as? NSArray {
dispatch_async(dispatch_get_main_queue(), {
self.searching = false
var suggestionResults: [spotifySearchResult] = []
for result in results {
let artist = result["name"] as! String
var sugresult = spotifySearchResult()
sugresult.artist = artist
if !suggestionResults.contains(sugresult) {
suggestionResults.append(sugresult)
}
}
handler(suggestionResults)
})
}
}
}
I tried different fixes such as let artist = (result["name"] as! String) or let artist = (result["name"] as! String) as! String
But nothing worked.
I know that the question was already post 4 times but, I can't find anyone explaining how to fix it in every case, only case by case.
Can someone explain me how to investigate to fix it? Not just only a fix for my case. I would prefer fix it by myself with your hints!
BTW what does mean subscript? Is subscript the thing between quotation mark? IMHO the error message is a bit vague...
EDIT:
class spotifySearchResult : NSObject {
var artist=""
var track=""
var duration=0
var spotifyURL = NSURL()
var spotifyURI = NSURL()
override func isEqual(theObject: AnyObject?) -> Bool {
if let myObject = theObject as? spotifySearchResult {
return (myObject.artist.uppercaseString == self.artist.uppercaseString && myObject.track.uppercaseString == self.track.uppercaseString)
}
return false
}
}
Subscription means to use the shorter syntax item["key"] for item.objectForKey["key"]
results seems to be an array of dictionaries so I suggest to cast down to a more specific type
if let results = jsonArtists["items"] as? [[String:AnyObject]] {
or even, if all values are guaranteed to be strings
if let results = jsonArtists["items"] as? [[String:String]] {

Get Contact Image Data using ABPersonCopyImageData

I'm trying to get the contact details out of the address book on the Mac. I can get the first name and last name fields etc, but I'm struggling with the syntax for ABPersonCopyImageData.
Now according to the documentation ABPersonCopyImageData takes a single parameter of type ABPerson.
Here is my code:
import AddressBook
let thisPerson : ABPerson
let addressBook = ABAddressBook.sharedAddressBook()
rec = addressBook.recordForUniqueId("0005A360-327F-4E12-BBB9-24A842497E12:ABPerson")
let firstName = rec.valueForProperty(kABFirstNameProperty) as! String
let lastName = rec.valueForProperty(kABLastNameProperty) as! String
println("\(firstName) \(lastName)")
let contactImage = ABPersonCopyImageData(thisPerson)
The last line stops the compiler with an error: Cannot invoke 'ABPersonCopyImageData' with an argument list of type (ABPerson). As far as I can tell thisPerson is of type ABPerson. What is going wrong?
I found out how to do this in ElCapitan:
import Contacts
func getContactImage(name:String) -> NSImage?
{
let store = CNContactStore()
do
{
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName(name), keysToFetch:[CNContactImageDataKey])
if contacts.count > 0
{
if let image = contacts[0].imageData
{
return NSImage.init(data: image)
}
}
}
catch
{
}
return nil
}

Swift Parse.com unable to retrieve object

So I am trying to get a String that is saved within an object from Parse.com. Here is the code that I am using to do so:
var query: PFQuery = PFQuery(className: "Replys")
query.getObjectInBackgroundWithId("ipmdKB0N1N") {
(object: PFObject?, error: NSError?) -> Void in
if error == nil && object != nil {
println(object)
self.replyField.text = object["Replys"]
} else {
println(error)
}
}
I want to make this string the text of the label called "replyField" but when I try to do that, Xcode gives an error like "Cannot assign value "AnyObject?" to type "String?"" Even when I add as! String, it still gives a similar (though not exactly the same) error. Any ideas why?
You should unwrap optional of object. Such as object!["Replys"] or object?["Replys"]
Like below:
self.replyField.text = object!["Replys"] as! String
or
self.replyField.text = object?["Replys"] as! String
let replysString = object["Replys"] as! NSString
self.replyField.text = replysString as String

Resources