In some languages, like C# for example, you can create a string in the following way:
"String {0} formatted {1} "
And then format it with String.format by passing in the values to format.
The above declaration is good, because you don't have to know of what type its parameters are when you create the string.
I tried to find similar approach in Swift, but what I found out was something like the following format:
"String %d formatted %d"
which requires you to format the string with String(format: , parameters). This is not good because you would also have to know parameter types when declaring the string.
Is there a similar approach in Swift where I wouldn't have to know the parameter types?
Use this one:
let printfOutput = String(format:"%# %2.2d", "string", 2)
It's the same as printf or the Obj-C formatting.
You can also mix it in this way:
let parm = "string"
let printfOutput = String(format:"\(parm) %2.2d", 2)
Edit: Thanks to MartinR (he knows it all ;-)
Be careful when mixing string interpolation and formatting. String(format:"\(parm) %2.2d", 2) will crash if parm contains a percent character. In (Objective-)C, the clang compiler will warn you if a format string is not a string literal.
This gives some room for hacking:
let format = "%#"
let data = "String"
let s = String(format: "\(format)", data) // prints "String"
In contrast to Obj-C which parses the format string at compile time, Swift does not do that and just interprets it at runtime.
In Swift, types need to conform to the CustomStringConvertible protocol in order to be used inside strings. This is also a requirement for the types used in string interpolation like this:
"Integer value \(intVal) and double value \(doubleVal)"
When you understand the CustomStringConvertible, you can create your own function to fulfill your needs. The following function formats the string based on the given arguments and prints it. It uses {} as a placeholder for the argument, but you can change it to anything you want.
func printWithArgs(string: String, argumentPlaceHolder: String = "{}", args: CustomStringConvertible...) {
var formattedString = string
// Get the index of the first argument placeholder
var nextPlaceholderIndex = string.range(of: argumentPlaceHolder)
// Index of the next argument to use
var nextArgIndex = 0
// Keep replacing the next placeholder as long as there's more placeholders and more unused arguments
while nextPlaceholderIndex != nil && nextArgIndex < args.count {
// Replace the argument placeholder with the argument
formattedString = formattedString.replacingOccurrences(of: argumentPlaceHolder, with: args[nextArgIndex].description, options: .caseInsensitive, range: nextPlaceholderIndex)
// Get the next argument placeholder index
nextPlaceholderIndex = formattedString.range(of: argumentPlaceHolder)
nextArgIndex += 1
}
print(formattedString)
}
printWithArgs(string: "First arg: {}, second arg: {}, third arg: {}", args: "foo", 4.12, 100)
// Prints: First arg: foo, second arg: 4.12, third arg: 100
Using a custom implementation allows you to have more control over it and tweak its behavior. For example, if you wanted to, you could modify this code to display the same argument multiple times using placeholders like {1} and {2}, you could fill the arguments in a reversed order, etc.
For more information about string interpolation in Swift: https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292
Related
This question already has an answer here:
In Swift, does Int have a hidden initializer that takes a String?
(1 answer)
Closed 7 years ago.
I'm new to Swift. I saw this code online:
let number = Int("123")
I want to read a bit more about the "Int" type initialiser that take a string as a argument. However, when I looked at Apple's offical Swift documentations:
https://developer.apple.com/library/tvos/documentation/Swift/Reference/Swift_Int_Structure/index.html#//apple_ref/swift/struct/s:Si
I couldn't find a Int type initialiser that actually takes a string as its argument. Am I looking at the wrong place? Or is there something I'm missing or unaware of?
If you click on init(_:radix:) to expand the declaration then you'll see
Construct from an ASCII representation in the given radix.
Declaration
init?(_ text: String, radix radix: Int = default)
The first parameter is a string (and has an empty external parameter
name). The second parameter "radix" has a default value,
therefore it can be omitted when calling the function:
let number = Int("123")
but you can specify the radix to create a number from a string
representation in another base:
let numberFromHexString = Int("100", radix: 16) // Optional(256)
let numberFomOctalString = Int("077", radix: 8) // Optional(63)
There is also a "trick" which I learned at
"Jump to definition" for methods without external parameter names: If you write
let number = Int("123")
as
let number = Int.init("123")
then you can "command-click" on "init" in Xcode, and you are led
directly to the declaration
public init?(_ text: String, radix: Int = default)
Hello I have a for in loop where elements is the variable being changed and in this case "elements" is a string but there is a corresponding variable out side of the for in loop that has the same name as the string called elements. So what I mean is out side there is a Var time = [some,text,words] and theres a for in loop that calls a STRING named "time" and I would like to know how to convert the string in the for in loop into the variable by some how taking off the "'s (not that simple I know) without specifically saying "time"(the variable) but instead converting the "elements"(which is the string 'time') string into the variable. I hope I was clear enough if I'm not making sense I'll try again.
You cannot refer to local variables dynamically by their names in Swift. This would break a lot of compiler optimizations as well as type safety if you could.
You can refer to object properties by their names if the class conforms to key-value coding. For example:
class X : NSObject {
let time = ["some", "text", "words"]
func readWordsFromProp(name: String) -> String {
guard let list = self.valueForKey(name) as? [String] else {
return ""
}
var result = ""
for word in list {
result += word
}
return result
}
}
let x = X()
print(x.readWordsFromProp("time"))
In general, there are better ways to do things in Swift using closures that don't rely on fragile name-matching. But KVC can be a very powerful tool
Is there a function to capitalize each word in a string or is this a manual process?
For e.g. "bob is tall"
And I would like "Bob Is Tall"
Surely there is something and none of the Swift IOS answers I have found seemed to cover this.
Are you looking for capitalizedString
Discussion
A string with the first character in each word changed to its corresponding uppercase value, and all remaining characters set to their corresponding lowercase values.
and/or capitalizedStringWithLocale(_:)
Returns a capitalized representation of the receiver using the specified locale.
For strings presented to users, pass the current locale ([NSLocale currentLocale]). To use the system locale, pass nil.
Swift 3:
var lowercased = "hello there"
var stringCapitalized = lowercased.capitalized
//prints: "Hello There"
Since iOS 9 a localised capitalization function is available as capitalised letters may differ in languages.
if #available(iOS 9.0, *) {
"istanbul".localizedCapitalizedString
// In Turkish: "İstanbul"
}
An example of the answer provided above.
var sentenceToCap = "this is a sentence."
println(sentenceToCap.capitalizedStringWithLocale(NSLocale.currentLocale()) )
End result is a string "This Is A Sentence"
For Swift 3 it has been changed to capitalized .
Discussion
This property performs the canonical (non-localized) mapping. It is suitable for programming operations that require stable results not depending on the current locale.
A capitalized string is a string with the first character in each word changed to its corresponding uppercase value, and all remaining characters set to their corresponding lowercase values. A “word” is any sequence of characters delimited by spaces, tabs, or line terminators (listed under getLineStart(_:end:contentsEnd:for:)). Some common word delimiting punctuation isn’t considered, so this property may not generally produce the desired results for multiword strings.
Case transformations aren’t guaranteed to be symmetrical or to produce strings of the same lengths as the originals. See lowercased for an example.
There is a built in function for that
nameOfString.capitalizedString
This will capitalize every word of string. To capitalize only the first letter you can use:
nameOfString.replaceRange(nameOfString.startIndex...nameOfString.startIndex, with: String(nameOfString[nameOfString.startIndex]).capitalizedString)
Older Thread
Here is what I came up with that seems to work but I am open to anything that is better.
func firstCharacterUpperCase(sentenceToCap:String) -> String {
//break it into an array by delimiting the sentence using a space
var breakupSentence = sentenceToCap.componentsSeparatedByString(" ")
var newSentence = ""
//Loop the array and concatinate the capitalized word into a variable.
for wordInSentence in breakupSentence {
newSentence = "\(newSentence) \(wordInSentence.capitalizedString)"
}
// send it back up.
return newSentence
}
or if I want to use this as an extension of the string class.
extension String {
var capitalizeEachWord:String {
//break it into an array by delimiting the sentence using a space
var breakupSentence = self.componentsSeparatedByString(" ")
var newSentence = ""
//Loop the array and concatinate the capitalized word into a variable.
for wordInSentence in breakupSentence {
newSentence = "\(newSentence) \(wordInSentence.capitalizedString)"
}
// send it back up.
return newSentence
}
}
Again, anything better is welcome.
Swift 5 version of Christopher Wade's answer
let str = "my string"
let result = str.capitalized(with: NSLocale.current)
print(result) // prints My String
I would like to create a function that looks at a string, and if it's a decimal string, returns it as a currency-formatted string. The function below does that, however if I pass in a string that is already formatted, it will fail of course (it expects to see a string like '25' or '25.55' but not '$15.25'
Is there a way to modify my function below to add another if condition that says "if you've already been formatted as a currency string, or your string is not in the right format, return X" (maybe X will be 0, or maybe it will be self (the same string) i'm not sure yet).
func toCurrencyStringFromDecimalString() -> String
{
var numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
if (self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).utf16Count == 0)
{
//If whitespace is passed in, just return 0.0 as default
return numberFormatter.stringFromNumber(NSDecimalNumber(string: "0.0"))!
}
else if (IS_NOT_A_DECIMAL_OR_ALREADY_A_CURRENCY_STRING)
{
//So obviously this would go here to see if it's not a decimal (or already contains a current placeholder etc)
}
else
{
return numberFormatter.stringFromNumber(NSDecimalNumber(string: self))!
}
}
Thank you for your help!
Sounds like you need to use NSScanner.
According to the docs, the scanDecimal function of NSScanner:
Skips past excess digits in the case of overflow, so the receiver’s
position is past the entire integer representation.
Invoke this method with NULL as value to simply scan past a decimal integer representation.
I've been mostly programming in Obj-C so my Swift is rubbish, but here's my attempt at translating the appropriate code for detecting numeric strings (as also demonstrated in this answer):
let scanner: NSScanner = NSScanner(string:self)
let isNumeric = scanner.scanDecimal(nil) && scanner.atEnd
If the string is not a decimal representation, isNumeric should return false.
Is there anyway to use a string literal as an argument to a function within a println statement.
func greetings(name: String) -> String {
return "Greetings \(name)!"
}
What I was trying to do: (I tried escaping the quotes around Earthling.)
println("OUTPUT: \(greetings("Earthling"))")
You can alternatively do this:
let name = "Earthling"
println("OUTPUT: \(greetings(name))")
And this works too:
println(greetings("Earthling"))
I tried escaping the quotes in the first example but with no luck, its not super important as its only a test, I was just curious if there was a way to do this, using a function call with a string literal as an argument within a print or println statement that contains other text.
From the Apple docs:
The expressions you write inside parentheses within an interpolated
string cannot contain an unescaped double quote (") or backslash (\),
and cannot contain a carriage return or line feed.
The problem is of course not with println but with the embedding of expressions with quotes in string literals.
Thus
let b = false
let s1 = b ? "is" : "isn't"
let s2 = "it \(b ? "is" : "isn't")" // won't compile
However NSLog as a one-liner'' works quite well here
NSLog("it %#", b ? "is" : "isn't")
Note %#, not %s. Try the latter in a playground to see why.