How to delete last path component of a String in Swift? - ios

I have a String 11/Passion/01PassionAwakening.mp3 and I need to delete the last path component 01PassionAwakening.mp3 in order to get 11/Passion.
How can I do this while saving both components?

You can separate your url into two parts like so:
let str : NSString = "www.music.com/Passion/PassionAwakening.mp3"
let path : NSString = str.stringByDeletingLastPathComponent
let ext : NSString = str.lastPathComponent
print(path)
print(ext)
Output
www.music.com/Passion
PassionAwakening.mp3
For more info please have a look at this link.

You should really do away with legacy NS Objective-C classes and manual path string splitting where possible. Use URL instead:
let url = URL(fileURLWithPath: "a/b/c.dat", isDirectory: false)
let path = url.deletingLastPathComponent().relativePath // 'a/b'
let file = url.lastPathComponent // 'c.dat'
That being said, Apple has an explicit FilePath type starting with macOS 11, but with no path manipulation methods. For those you'd have to include the external system package
If you are on macOS 12, the methods from the external package are now also available on the system.

Swift 4+:
let components = path.split(separator: "/")
let directory = components.dropLast(1).map(String.init).joined(separator: "/")
Swift 3:
let str = "11/Passion/01PassionAwakening.mp3"
if !str.isEmpty {
let components = str.characters.split("/")
let head = components.dropLast(1).map(String.init).joinWithSeparator("/")
let tail = components.dropFirst(components.count-1).map(String.init)[0]
print("head:",head,"tail:", tail) // head: 11/Passion tail: 01PassionAwakening.mp3
} else {
print("path should not be an empty string!")
}

This works for Swift 3.0 as well:
let fileName = NSString(string: "11/Passion/01PassionAwakening.mp3").lastPathComponent

Swift 3.0 version
if !str.isEmpty {
let components = str.characters.split(separator: "/")
let head = components.dropLast(1).map(String.init).joined(separator: "/")
let words = components.count-1
let tail = components.dropFirst(words).map(String.init)[0]
print("head:",head,"tail:", tail) // head: 11/Passion tail: 01PassionAwakening.mp3
} else {
print("path should not be an empty string!")
}

rolling back to NSString:
extension String {
var ns: NSString {
return self as NSString
}
var pathExtension: String {
return ns.pathExtension
}
var lastPathComponent: String {
return ns.lastPathComponent
}
var stringByDeletingLastPathComponent: String {
return ns.deletingLastPathComponent
}
}
so you can do:
let folderPath = pathString.stringByDeletingLastPathComponent

Just improvised the solution for URL String. Thank you so much ingconti
extension String {
var ns: URL? {
return URL.init(string: self)
}
var pathExtension: String {
return ns?.pathExtension ?? ""
}
var lastPathComponent: String {
return ns?.lastPathComponent ?? ""
}
var stringByDeletingLastPathComponent: String {
return ns?.deletingLastPathComponent().absoluteString ?? ""
}
}

Related

Swift Realm. How to put 3 millions objects in database

I make offline dictionary app. And now I convert dictionary file to realm database.
Convert function:
if let path = Bundle.main.path(forResource: "dictionary", ofType: "dsl") {
do {
let data = try String(contentsOfFile: path, encoding: .utf8)
let myStrings = data.components(separatedBy: .newlines)
for (index, row) in myStrings.enumerated() {
if(row.containsChineseCharacters)
{
let firstWord = CNDict()
firstWord.word = row
firstWord.pinyin = myStrings[index+1]
firstWord.translate = myStrings[index+2]
try! realm.write {
realm.add(firstWord)
}
}
}
print("The task end.")
} catch {
print(error)
}
}
When I try to convert the dictionary immediately, the database file becomes a lot of gigabytes and crashes around the middle
Splitting the dictionary in parts is not an option, because there are about 3 million lines. It will take very much... (realm plugin crashes)
I need help on how to add as many values to the database with our crashes.
The issue is that your file is large and at this point, you load it into the memory:
let data = try String(contentsOfFile: path, encoding: .utf8)
And then you double the memory footprint here:
let myStrings = data.components(separatedBy: .newlines)
So my guess is that you received out of memory signal from the system.
Instead of loading into the memory all the data and than double it, you can use lazy collection. It will read and parse the line only when it needed for writing. It will not load all lines at once. One downside of lazy collections in Swift is that they could not provide all the functions that we get used to.
Here the full code for playground that solves your issue. You could and maybe should optimize some parts of it, but anyway it just showing how it could be done with lazy collection. (I changed some names, but it's still what you want).
import Foundation
extension String {
var containsOneSymbol: Bool {
return contains("1")
}
}
extension Character {
var isNewLine: Bool {
let string = String(self)
let set = CharacterSet(charactersIn: string)
return !set.isDisjoint(with: CharacterSet.newlines)
}
}
/// Add Object subclass for Realm
#objcMembers
final class CNDict {
dynamic var word = ""
dynamic var pinyin = ""
dynamic var translate = ""
}
final class ModelWriterWrapper {
private let bufferCapacity = 3
private var buffer: [String] = []
init() {
buffer.reserveCapacity(bufferCapacity)
}
func proccess(line: String) {
guard buffer.count == bufferCapacity else {
assert(buffer.count < bufferCapacity, "Buffer failer count: \(buffer.count)!")
buffer.append(line)
return
}
if let firstLine = buffer.first, firstLine.containsOneSymbol {
let dict = CNDict()
dict.word = firstLine
dict.pinyin = buffer[1]
dict.translate = buffer[2]
print("Ready for writing into DB \n word: \(dict.word) pinyin: \(dict.pinyin) translate: \(dict.translate)")
}
buffer.removeFirst()
buffer.append(line)
}
}
let data = stride(from: 0, to: 100_000, by: 1).map { "Line number \($0)\n"}.joined()
var line: String = ""
let myLines = data.lazy.map { char -> String in
line.append(char)
guard !char.isNewLine else {
defer { line = "" }
return line
}
return ""
}.filter { !$0.isEmpty }
let databaseWritter = ModelWriterWrapper()
myLines.forEach {
databaseWritter.proccess(line: $0)
}
If have any questions regarding the code, please ask.

Substitute for CFURLCreateStringByAddingPercentEscapes in Swift3 iOS10?

I know the method CFURLCreateStringByAddingPercentEscapes is deprecated in iOS10, it suggests me using stringByAddingPercentEncodingWithAllowedCharacters, but I do not using UTF-8, how can I use stringByAddingPercentEncodingWithAllowedCharacters to do the same thing as following:
extension String {
var gbkEncoded: String {
let cfEnc = CFStringEncodings.GB_18030_2000
return CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, self as CFString!, nil, nil, CFStringEncoding(cfEnc.rawValue)) as String
}
}
I'm afraid Apple would not provide us a replacement for CFURLCreateStringByAddingPercentEscapes as non-UTF-8 is not recommended in the modern web standards.
You may need to do it yourself. An example:
extension CharacterSet {
static let rfc3986Unreserved = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
}
extension String.Encoding {
static let gb_18030_2000 = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue)))
}
extension String {
func addingPercentEncoding(withAllowedCharacters characterSet: CharacterSet, using encoding: String.Encoding) -> String {
let stringData = self.data(using: encoding, allowLossyConversion: true) ?? Data()
let percentEscaped = stringData.map {byte->String in
if characterSet.contains(UnicodeScalar(byte)) {
return String(UnicodeScalar(byte))
} else if byte == UInt8(ascii: " ") {
return "+"
} else {
return String(format: "%%%02X", byte)
}
}.joined()
return percentEscaped
}
var gbkEncoded: String {
return self.addingPercentEncoding(withAllowedCharacters: .rfc3986Unreserved, using: .gb_18030_2000)
}
}
print("互联网".gbkEncoded) //->%BB%A5%C1%AA%CD%F8

How to remove characters from a String in swift

I am looking for a simple way to remove the 4 characters in the tilesColored String "ment" from the shuffledWord1.
var word1: String = "employment"
var shuffledWord1: String = "melpyoemtn"
var tilesColored: String = "ment"
var characters = Array(tilesColored) // gives ["m","e","n","t"]
let newWord1 = word1.StringByReplacingOccurencesOfString("\(characters[0])", withString:"") // gives "elpyoetn"
stringByReplacingOccurencesOfString only allows 1 character to be checked and removes BOTH m's, so how can I check against all 4 and only remove ONE instance of each to return "melpyo"?
Thanks in advance for any help possible
Swift 3+ version with better performance than the previous top answers. (Because we don't separate into arrays with substrings, which all would need seperate allocations.)
This here just works on the unicode scalar level. You can paste it right into a playground.
import Foundation
extension String {
func removeCharacters(from forbiddenChars: CharacterSet) -> String {
let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
return String(String.UnicodeScalarView(passed))
}
func removeCharacters(from: String) -> String {
return removeCharacters(from: CharacterSet(charactersIn: from))
}
}
let str = "n1o d2i3g4i5t6s!!!789"
let t1 = str.removeCharacters(from: CharacterSet.decimalDigits.inverted)
print(t1) // will print: 123456789
let t2 = str.removeCharacters(from: "0123456789")
print(t2) // will print: no digits!!!
Swift 3 version of Macondo2Seattle's answer, which, I think, is the most elegant solution.
extension String {
func removing(charactersOf string: String) -> String {
let characterSet = CharacterSet(charactersIn: string)
let components = self.components(separatedBy: characterSet)
return components.joined(separator: "")
}
}
Swift 5:
var phrase = "The rain in Spain stays mainly in the plain."
let vowels: Set<Character> = ["a", "e", "i", "o", "u"]
phrase.removeAll(where: { vowels.contains($0) })
// phrase == "Th rn n Spn stys mnly n th pln."
extension String {
func removeCharacters(characters: String) -> String {
let characterSet = NSCharacterSet(charactersInString: characters)
let components = self.componentsSeparatedByCharactersInSet(characterSet)
let result = components.joinWithSeparator("")
return result
}
}
Swift 2.0:
extension String {
func stringByRemovingOnce(chars: String) -> String {
var cs = Set(chars.characters)
let fd = characters.filter { c in
cs.remove(c).map { _ in false } ?? true
}
return String(fd)
}
}
"melpyoemtn".stringByRemovingOnce("ment") // "lpyoem"
Swift 1.2:
extension String {
func stringByRemovingOnce(chars: String) -> String {
var cs = Set(chars)
let fd = filter(self) { c in
cs.remove(c).map { _ in false } ?? true
}
return String(fd)
}
}
"melpyoemtn".stringByRemovingOnce("ment") // "lpyoem"

How do I get the App version and build number using Swift?

I have an IOS app with an Azure back-end, and would like to log certain events, like logins and which versions of the app users are running.
How can I return the version and build number using Swift?
EDIT
Updated for Swift 4.2
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
EDIT
As pointed out by #azdev on the new version of Xcode you will get a compile error for trying my previous solution, to solve this just edit it as suggested to unwrap the bundle dictionary using a !
let nsObject: AnyObject? = Bundle.main.infoDictionary!["CFBundleShortVersionString"]
End Edit
Just use the same logic than in Objective-C but with some small changes
//First get the nsObject by defining as an optional anyObject
let nsObject: AnyObject? = NSBundle.mainBundle().infoDictionary["CFBundleShortVersionString"]
//Then just cast the object as a String, but be careful, you may want to double check for nil
let version = nsObject as! String
Updated for Swift 3.0
The NS-prefixes are now gone in Swift 3.0 and several properties/methods have changed names to be more Swifty. Here's what this looks like now:
extension Bundle {
var releaseVersionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
}
}
Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber
Old Updated Answer
I've been working with Frameworks a lot since my original answer, so I
wanted to update my solution to something that is both simpler and
much more useful in a multi-bundle environment:
extension NSBundle {
var releaseVersionNumber: String? {
return self.infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return self.infoDictionary?["CFBundleVersion"] as? String
}
}
Now this extension will be useful in apps to identify both the main
bundle and any other included bundles (such as a shared framework for
extension programming or third frameworks like AFNetworking), like so:
NSBundle.mainBundle().releaseVersionNumber
NSBundle.mainBundle().buildVersionNumber
// or...
NSBundle(URL: someURL)?.releaseVersionNumber
NSBundle(URL: someURL)?.buildVersionNumber
Original Answer
I wanted to improve on some of the answers already posted. I wrote a
class extension that can be added to your tool chain to handle this in
a more logical fashion.
extension NSBundle {
class var applicationVersionNumber: String {
if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"]
as? String {
return version
}
return "Version Number Not Available"
}
class var applicationBuildNumber: String {
if let build = NSBundle.mainBundle().infoDictionary?["CFBundleVersion"] as? String {
return build
}
return "Build Number Not Available"
}
}
So now you can access this easily by:
let versionNumber = NSBundle.applicationVersionNumber
I know this has already been answered but personally I think this is a little cleaner:
Swift 3.0:
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
self.labelVersion.text = version
}
Swift <2.3
if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String {
self.labelVersion.text = version
}
This way, the if let version takes care of the conditional processing (setting the label text in my case) and if infoDictionary or CFBundleShortVersionString are nil the optional unwrapping will cause the code to be skipped.
I also know this has already been answered but I wrapped up the previous answers:
(*)Updated for extensions
extension Bundle {
var releaseVersionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildVersionNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
}
var releaseVersionNumberPretty: String {
return "v\(releaseVersionNumber ?? "1.0.0")"
}
}
Usage:
someLabel.text = Bundle.main.releaseVersionNumberPretty
#Deprecated: Old answers
Swift 3.1:
class func getVersion() -> String {
guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
return "no version info"
}
return version
}
For older versions:
class func getVersion() -> String {
if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String {
return version
}
return "no version info"
}
So if you want to set label text or want to use somewhere else;
self.labelVersion.text = getVersion()
2022, Swift 5
extension Bundle {
public var appName: String { getInfo("CFBundleName") }
public var displayName: String { getInfo("CFBundleDisplayName")}
public var language: String { getInfo("CFBundleDevelopmentRegion")}
public var identifier: String { getInfo("CFBundleIdentifier")}
public var copyright: String { getInfo("NSHumanReadableCopyright").replacingOccurrences(of: "\\\\n", with: "\n") }
public var appBuild: String { getInfo("CFBundleVersion") }
public var appVersionLong: String { getInfo("CFBundleShortVersionString") }
//public var appVersionShort: String { getInfo("CFBundleShortVersion") }
fileprivate func getInfo(_ str: String) -> String { infoDictionary?[str] as? String ?? "⚠️" }
}
usage (SwiftUI sample):
Text("Ver: \(Bundle.main.appVersionLong) (\(Bundle.main.appBuild)) ")
Text(Bundle.main.copyright)
.font(.system(size: 10, weight: .thin))
.multilineTextAlignment(.center)
Bonus: Copyright supports \n symbols!
For Swift 4.0
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
let build = Bundle.main.infoDictionary!["CFBundleVersion"]!
I made an Extension on Bundle
extension Bundle {
var appName: String {
return infoDictionary?["CFBundleName"] as! String
}
var bundleId: String {
return bundleIdentifier!
}
var versionNumber: String {
return infoDictionary?["CFBundleShortVersionString"] as! String
}
var buildNumber: String {
return infoDictionary?["CFBundleVersion"] as! String
}
}
and then use it
versionLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"
Swift 5 as UIApplication extension
extension UIApplication {
static var release: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String? ?? "x.x"
}
static var build: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String? ?? "x"
}
static var version: String {
return "\(release).\(build)"
}
}
example use:
print("release: \(UIApplication.release)")
print("build: \(UIApplication.build)")
print("version: \(UIApplication.version)")
Bundle+Extension.swift (SwiftUI, Swift 5, Xcode 11)
I combined ideas from a few answers, and extended a bit:
a SwiftUI example
Displays a warning triangle emoticon (rather than crashing the app) if the key is missing from the Info.plist
import Foundation
extension Bundle {
public var appVersionShort: String {
if let result = infoDictionary?["CFBundleShortVersionString"] as? String {
return result
} else {
return "⚠️"
}
}
public var appVersionLong: String {
if let result = infoDictionary?["CFBundleVersion"] as? String {
return result
} else {
return "⚠️"
}
}
public var appName: String {
if let result = infoDictionary?["CFBundleName"] as? String {
return result
} else {
return "⚠️"
}
}
}
SwiftUI example use
VStack {
Text("Version: \(Bundle.main.appVersionShort!) (\(Bundle.main.appVersionLong!))")
.font(.subheadline)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
I created an extension for UIApplication.
extension UIApplication {
static var appVersion: String {
let versionNumber = Bundle.main.infoDictionary?[IdentifierConstants.InfoPlist.versionNumber] as? String
let buildNumber = Bundle.main.infoDictionary?[IdentifierConstants.InfoPlist.buildNumber] as? String
let formattedBuildNumber = buildNumber.map {
return "(\($0))"
}
return [versionNumber,formattedBuildNumber].compactMap { $0 }.joined(separator: " ")
}
}
enum Constants {
enum InfoPlist {
static let versionNumber = "CFBundleShortVersionString"
static let buildNumber = "CFBundleVersion"
}
}
For Swift 3.0 NSBundle doesn't work, Following code works perfectly.
let versionNumberString =
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")
as! String
and for just the build number, it is:
let buildNumberString =
Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion")
as! String
Confusingly 'CFBundleVersion' is the build number as entered in Xcode on General->Identity.
Xcode 9.4.1 Swift 4.1
Note the use of localizedInfoDictionary to pick up the right language version of the bundle display name.
var displayName: String?
var version: String?
var build: String?
override func viewDidLoad() {
super.viewDidLoad()
// Get display name, version and build
if let displayName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String {
self.displayName = displayName
}
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
self.version = version
}
if let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
self.build = build
}
}
Xcode 8, Swift 3:
let gAppVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "0"
let gAppBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "0"
Swift 4, useful Extension for Bundle
import Foundation
public extension Bundle {
public var shortVersion: String {
if let result = infoDictionary?["CFBundleShortVersionString"] as? String {
return result
} else {
assert(false)
return ""
}
}
public var buildVersion: String {
if let result = infoDictionary?["CFBundleVersion"] as? String {
return result
} else {
assert(false)
return ""
}
}
public var fullVersion: String {
return "\(shortVersion)(\(buildVersion))"
}
}
Bundle+Extensions.swift
import Foundation
extension Bundle {
var versionNumber: String? {
return infoDictionary?["CFBundleShortVersionString"] as? String
}
var buildNumber: String? {
return infoDictionary?["CFBundleVersion"] as? String
}
var bundleName: String? {
return infoDictionary?["CFBundleName"] as? String
}
}
Usage:
someLabel.text = Bundle.main.versionNumber
OP asked for both version number and build number. Unfortunately most of the answers don't provide both of those options. Additionally, others add unnecessary extension methods. Here's one that's pretty simple and solves OP's problem:
// Example output: "1.0 (234)"
private func versionAndBuildNumber() -> String {
let versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
if let versionNumber = versionNumber, let buildNumber = buildNumber {
return "\(versionNumber) (\(buildNumber))"
} else if let versionNumber = versionNumber {
return versionNumber
} else if let buildNumber = buildNumber {
return buildNumber
} else {
return ""
}
}
My answer (as at Aug 2015), given Swift keeps evolving:
let version = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Having looked at the documentation, I believe that the following is cleaner:
let version =
NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString")
as? String
Source:
"Use of this method is preferred over other access methods because it returns the localized value of a key when one is available."
For Swift 5.0 :
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
Swift 5.3
let infoDictionaryKey = kCFBundleVersionKey as String
guard let currentVersion = Bundle.main.object(forInfoDictionaryKey: infoDictionaryKey) as? String
else { fatalError("Expected to find a bundle version in the info dictionary") }
For Swift 1.2 it's:
let version = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"] as! String
let build = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Swift 3:
Version Number
if let versionNumberString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { // do something }
Build number
if let buildNumberString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { // do something }
Here's an updated version for Swift 3.2:
extension UIApplication
{
static var appVersion:String
{
if let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")
{
return "\(appVersion)"
}
return ""
}
static var buildNumber:String
{
if let buildNum = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String)
{
return "\(buildNum)"
}
return ""
}
static var versionString:String
{
return "\(appVersion).\(buildNumber)"
}
}
Swift 4
func getAppVersion() -> String {
return "\(Bundle.main.infoDictionary!["CFBundleShortVersionString"] ?? "")"
}
Bundle.main.infoDictionary!["CFBundleShortVersionString"]
Swift old syntax
let appVer: AnyObject? = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"]
Update for Swift 5
here's a function i'm using to decide whether to show an "the app updated" page or not. It returns the build number, which i'm converting to an Int:
if let version: String = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
guard let intVersion = Int(version) else { return }
if UserDefaults.standard.integer(forKey: "lastVersion") < intVersion {
print("need to show popup")
} else {
print("Don't need to show popup")
}
UserDefaults.standard.set(intVersion, forKey: "lastVersion")
}
If never used before it will return 0 which is lower than the current build number. To not show such a screen to new users, just add the build number after the first login or when the on-boarding is complete.
extension UIApplication {
static var appVersion: String {
if let appVersion = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") {
return "\(appVersion)"
} else {
return ""
}
}
static var build: String {
if let buildVersion = NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleVersionKey as String) {
return "\(buildVersion)"
} else {
return ""
}
}
static var versionBuild: String {
let version = UIApplication.appVersion
let build = UIApplication.build
var versionAndBuild = "v\(version)"
if version != build {
versionAndBuild = "v\(version)(\(build))"
}
return versionAndBuild
}
}
Attention:
You should use if let here in case that the app version or build is not set which will lead to crash if you try to use ! to unwrap.
You can now use a constant for this, rather than having to use stringly-typed code like before, which makes things even more convenient.
var appVersion: String {
return Bundle.main.infoDictionary![kCFBundleVersionKey as String] as! String
}
public var appVersionNumberString: String {
get {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
}
}
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
self.lblAppVersionValue.text = version
}
SWIFT 4
//First get the nsObject by defining as an optional AnyObject
let nsObject: AnyObject? = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as AnyObject
//Then just cast the object as a String, but be careful, you may want to double check for nil
let version = nsObject as! String

Getting version and build information with Swift

I am attempting to gain access to the main NSBundle to retrieve version and build information. Thing is, I want to try it in Swift, I know how to retrieve it in Objective-C with:
text = [NSBundle.mainBundle.infoDictionary objectForKey:#"CFBundleVersion"];
Yet I don't know where to start with Swift, I have attempted to write it in the new syntax with no avail.
What was wrong with the Swift syntax? This seems to work:
if let text = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
print(text)
}
Swift 3/4 Version
func version() -> String {
let dictionary = Bundle.main.infoDictionary!
let version = dictionary["CFBundleShortVersionString"] as! String
let build = dictionary["CFBundleVersion"] as! String
return "\(version) build \(build)"
}
Swift 2.x Version
func version() -> String {
let dictionary = NSBundle.mainBundle().infoDictionary!
let version = dictionary["CFBundleShortVersionString"] as String
let build = dictionary["CFBundleVersion"] as String
return "\(version) build \(build)"
}
as seen here.
Swift 5.0
I created a wrapper for Swift 5 to get some app related strings at one place in all my apps, called AppInfo.
struct AppInfo {
var appName : String {
return readFromInfoPlist(withKey: "CFBundleName") ?? "(unknown app name)"
}
var version : String {
return readFromInfoPlist(withKey: "CFBundleShortVersionString") ?? "(unknown app version)"
}
var build : String {
return readFromInfoPlist(withKey: "CFBundleVersion") ?? "(unknown build number)"
}
var minimumOSVersion : String {
return readFromInfoPlist(withKey: "MinimumOSVersion") ?? "(unknown minimum OSVersion)"
}
var copyrightNotice : String {
return readFromInfoPlist(withKey: "NSHumanReadableCopyright") ?? "(unknown copyright notice)"
}
var bundleIdentifier : String {
return readFromInfoPlist(withKey: "CFBundleIdentifier") ?? "(unknown bundle identifier)"
}
var developer : String { return "my awesome name" }
// lets hold a reference to the Info.plist of the app as Dictionary
private let infoPlistDictionary = Bundle.main.infoDictionary
/// Retrieves and returns associated values (of Type String) from info.Plist of the app.
private func readFromInfoPlist(withKey key: String) -> String? {
return infoPlistDictionary?[key] as? String
}
}
You can use it like so:
print("The apps name = \(AppInfo.appname)")
For the final release of Xcode 6 use
NSBundle.mainBundle().infoDictionary?["CFBundleVersion"] as? String
The "?" character after infoDictionary is important here
Here is simple way to get Build and version.
For Swift 4.X
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
print(version)
}
if let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
print(build)
}
For Objective C
NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
NSString * currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
Let me know if any issue. This is working for me.
Swifty way for AppName, AppVersion and BuildNumber...
if let dict = NSBundle.mainBundle().infoDictionary {
if let version = dict["CFBundleShortVersionString"] as? String,
let bundleVersion = dict["CFBundleVersion"] as? String,
let appName = dict["CFBundleName"] as? String {
return "You're using \(appName) v\(version) (Build \(bundleVersion))."
}
}
In swift, I would make it as extension for UIApplication, like this:
extension UIApplication {
func applicationVersion() -> String {
return NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as! String
}
func applicationBuild() -> String {
return NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleVersionKey as String) as! String
}
func versionBuild() -> String {
let version = self.applicationVersion()
let build = self.applicationBuild()
return "v\(version)(\(build))"
}
}
Then you can just use following to get everything you need:
let version = UIApplication.sharedApplication.applicationVersion() // 1
let build = UIApplication.sharedApplication.applicationBuild() // 80
let both = UIApplication.sharedApplication.versionBuild() // 1(80)
//Returns app's version number
public static var appVersion: String? {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
}
//Return app's build number
public static var appBuild: String? {
return Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String
}
This code works for Swift 3, Xcode 8:
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "0"
let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "0"
For Swift 3,Replace NSBundle with Bundle and mainBundle is replaced simply by main.
let AppVersion = Bundle.main.infoDictionary!["CFBundleVersion"] as! String
[Update: Xcode 6.3.1] I tried all of the above and none of these work in Xcode 6.3.1 but I found that this does:
(NSBundle.mainBundle().infoDictionary?["CFBundleVersion"] as? String)!
Another option is to define in the AppDelegate the variables:
var applicationVersion:String {
return NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as! String
}
var applicationBuild:String {
return NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleVersionKey as String) as! String
}
var versionBuild:String {
let version = self.applicationVersion
let build = self.applicationBuild
return "version:\(version) build:(\(build))"
}
that can be referenced as variables in the AppDelegate
Swift 3 :
let textVersion
= Bundle.main.infoDictionary?["CFBundleVersion"] as? String
SWIFT 3 Version
if let infoPath = Bundle.main.path(forResource: "Info.plist", ofType: nil),
let infoAttr = try? FileManager.default.attributesOfItem(atPath: infoPath),
let infoDate = infoAttr[.creationDate] as? Date
{
return infoDate
}
return Date()
Get version from Framework's bundle
To have result for framework you can use
[Access to Framework bundle]
//inside framework
let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String
Swift 100% working tested
You can get that easily by using single variable and make it public. You can use it everywhere you want.
(I am getting here User Agent for API header)
public let userAgent: String = {
if let info = Bundle.main.infoDictionary {
let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
let osNameVersion: String = {
let version = ProcessInfo.processInfo.operatingSystemVersion
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
let osName: String = {
#if os(iOS)
return "iOS"
#elseif os(watchOS)
return "watchOS"
#elseif os(tvOS)
return "tvOS"
#elseif os(macOS)
return "OS X"
#elseif os(Linux)
return "Linux"
#else
return "Unknown"
#endif
}()
return "\(osName) \(versionString)"
}()
return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) "
}
return "MyApp"
}()
Output:
"User-Agent": "MyApp/4.6.0 (com.app.myapp; build:4.6.0.0; iOS 15.2.0) "

Resources