How does `= default` parameter in Swift work? [duplicate] - swift

This question already has an answer here:
Default keyword in Swift parameter
(1 answer)
Closed 5 years ago.
Excerpt from Swift Standard Library
...
/// - separator: A string to print between each item. The default is a single
/// space (`" "`).
/// - terminator: The string to print after all items have been printed. The
/// default is a newline (`"\n"`).
...
public func print(_ items: Any..., separator: String = default, terminator: String = default)
How is default value for separator is being set to space and for terminator - \n?

This is normally not possible in swift. As you can see, this does not compile:
func a(_ a: String = default) {
print(a)
}
a()
It's another one of those things that they do when you go to the definition of something in the standard library. It's like the "header file" of swift. :)
As you can see, the print method you showed does not even have a body. How is that possible? No it's not. But they can do what they want because its aim is to show you the declaration, not how it is implemented.

Related

What is the keyword repeating used for in Swift? [duplicate]

This question already has answers here:
What is the point of having two different names for the same parameter?
(3 answers)
What are the new "for", "at", "in" keywords in Swift3 function declarations?
(1 answer)
Closed 4 years ago.
I am reading the official "The Swift Programming Language (Swift 4.2)" book and in the part Swift Tour/Generics I encounter the following code
func makeArray1<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
let arr1 = makeArray1(repeating: "knock", numberOfTimes: 4)
print(arr1)
which prints out
["knock", "knock", "knock", "knock"]
I am confused with the role of the word 'repeating' in the definition of the function makeArray1. I tried to run the code without that word with the following code
func makeArray2<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
let arr2 = makeArray2(item:"knock", numberOfTimes:4)
print (arr2)
and the code gave the same result as before.
["knock", "knock", "knock", "knock"]
So what is the use of 'repeating' in the code?
It's not a keyword, it's an optional function argument label that can differ from the local parameter name that's used inside the function/method.
Read the section Function Argument Labels and Parameter Names in The Swift Programming Language:
Each function parameter has both an argument label and a parameter name. The argument label is used when calling the function; each argument is written in the function call with its argument label before it. The parameter name is used in the implementation of the function. By default, parameters use their parameter name as their argument label. …
You write an argument label before the parameter name, separated by a space …
If you don’t want an argument label for a parameter, write an underscore (_) instead of an explicit argument label for that parameter.

What is the best way to test if a CharacterSet contains a Character in Swift 4?

I'm looking for a way, in Swift 4, to test if a Character is a member of an arbitrary CharacterSet. I have this Scanner class that will be used for some lightweight parsing. One of the functions in the class is to skip any characters, at the current position, that belong to a certain set of possible characters.
class MyScanner {
let str: String
var idx: String.Index
init(_ string: String) {
str = string
idx = str.startIndex
}
var remains: String { return String(str[idx..<str.endIndex])}
func skip(charactersIn characters: CharacterSet) {
while idx < str.endIndex && characters.contains(str[idx])) {
idx = source.index(idx, offsetBy: 1)
}
}
}
let scanner = MyScanner("fizz buzz fizz")
scanner.skip(charactersIn: CharacterSet.alphanumerics)
scanner.skip(charactersIn: CharacterSet.whitespaces)
print("what remains: \"\(scanner.remains)\"")
I would like to implement the skip(charactersIn:) function so that the above code would print buzz fizz.
The tricky part is characters.contains(str[idx])) in the while - .contains() requires a Unicode.Scalar, and I'm at a loss trying to figure out the next step.
I know I could pass in a String to the skip function, but I'd like to find a way to make it work with a CharacterSet, because of all the convenient static members (alphanumerics, whitespaces, etc.).
How does one test a CharacterSet if it contains a Character?
Not sure if it's the most efficient way but you can create a new CharSet and check if they are sub/super-sets (Set comparison is rather quick)
let newSet = CharacterSet(charactersIn: "a")
// let newSet = CharacterSet(charactersIn: "\(character)")
print(newSet.isSubset(of: CharacterSet.decimalDigits)) // false
print(newSet.isSubset(of: CharacterSet.alphanumerics)) // true
Swift 4.2
CharacterSet extension function to check whether it contains Character:
extension CharacterSet {
func containsUnicodeScalars(of character: Character) -> Bool {
return character.unicodeScalars.allSatisfy(contains(_:))
}
}
Usage example:
CharacterSet.decimalDigits.containsUnicodeScalars(of: "3") // true
CharacterSet.decimalDigits.containsUnicodeScalars(of: "a") // false
I know that you wanted to use CharacterSet rather than String, but CharacterSet does not (yet, at least) support characters that are composed of more than one Unicode.Scalar. See the "family" character (πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦) or the international flag characters (e.g. "πŸ‡―πŸ‡΅" or "πŸ‡―πŸ‡²") that Apple demonstrated in the string discussion in WWDC 2017 video What's New in Swift. The multiple skin tone emoji also manifest this behavior (e.g. πŸ‘©πŸ» vs πŸ‘©πŸ½).
As a result, I'd be wary of using CharacterSet (which is a "set of Unicode character values for use in search operations"). Or, if you want to provide this method for the sake of convenience, be aware that it will not work correctly with characters represented by multiple unicode scalars.
So, you might offer a scanner that provides both CharacterSet and String renditions of the skip method:
class MyScanner {
let string: String
var index: String.Index
init(_ string: String) {
self.string = string
index = string.startIndex
}
var remains: String { return String(string[index...]) }
/// Skip characters in a string
///
/// This rendition is safe to use with strings that have characters
/// represented by more than one unicode scalar.
///
/// - Parameter skipString: A string with all of the characters to skip.
func skip(charactersIn skipString: String) {
while index < string.endIndex, skipString.contains(string[index]) {
index = string.index(index, offsetBy: 1)
}
}
/// Skip characters in character set
///
/// Note, character sets cannot (yet) include characters that are represented by
/// more than one unicode scalar (e.g. πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦ or πŸ‡―πŸ‡΅ or πŸ‘°πŸ»). If you want to test
/// for these multi-unicode characters, you have to use the `String` rendition of
/// this method.
///
/// This will simply stop scanning if it encounters a multi-unicode character in
/// the string being scanned (because it knows the `CharacterSet` can only represent
/// single-unicode characters) and you want to avoid false positives (e.g., mistaking
/// the Jamaican flag, πŸ‡―πŸ‡², for the Japanese flag, πŸ‡―πŸ‡΅).
///
/// - Parameter characterSet: The character set to check for membership.
func skip(charactersIn characterSet: CharacterSet) {
while index < string.endIndex,
string[index].unicodeScalars.count == 1,
let character = string[index].unicodeScalars.first,
characterSet.contains(character) {
index = string.index(index, offsetBy: 1)
}
}
}
Thus, your simple example will still work:
let scanner = MyScanner("fizz buzz fizz")
scanner.skip(charactersIn: CharacterSet.alphanumerics)
scanner.skip(charactersIn: CharacterSet.whitespaces)
print(scanner.remains) // "buzz fizz"
But use the String rendition if the characters you want to skip might include multiple unicode scalars:
let family = "πŸ‘©\u{200D}πŸ‘©\u{200D}πŸ‘§\u{200D}πŸ‘¦" // πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦
let boy = "πŸ‘¦"
let charactersToSkip = family + boy
let string = boy + family + "foobar" // πŸ‘¦πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦foobar
let scanner = MyScanner(string)
scanner.skip(charactersIn: charactersToSkip)
print(scanner.remains) // foobar
As Michael Waterfall noted in the comments below, CharacterSet has a bug and doesn’t even handle 32-bit Unicode.Scalar values correctly, meaning that it doesn’t even handle single scalar characters properly if the value exceeds 0xffff (including emoji, amongst others). The String rendition, above, handles these correctly, though.

What does 'using' do in Swift methods? [duplicate]

This question already has an answer here:
What are the new "for", "at", "in" keywords in Swift3 function declarations?
(1 answer)
Closed 5 years ago.
I noticed that in I was getting an error unless I did
animateTransition(using transitionContext: UIViewControllerContextTransitioning)
However some tutorial present this method as,
animateTransition(transitionContext: UIViewControllerContextTransitioning) without the using.
It only seems to build if I include using but I'm curious as to it's role and when the change occurred.
check apple documentation
Specifying Argument Labels
You write an argument label before the parameter name, separated by a
space:
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}
Here’s a variation of the greet(person:) function that takes a
person’s name and hometown and returns a greeting:
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill! Glad you could visit from Cupertino."
The use of argument labels can allow a function to be called in an
expressive, sentence-like manner, while still providing a function
body that is readable and clear in intent.

Delete all characters after a certain character from a string in Swift [duplicate]

This question already has answers here:
What is the more elegant way to remove all characters after specific character in the String object in Swift
(6 answers)
Closed 6 years ago.
I have a textField and I would like to remove all character after a certain character.
For instance if what I have in the textField is the word Orange and I want to remove all characters after the n I would like to get Ora after the deletion.
How can I delete all characters after a certain character from a string in Swift?
Thanks
You can use StringProtocol method range(of string:), get the resulting range lowerBound, create a PartialRangeUpTo with it and subscript the original string:
Swift 4 or later
let word = "orange"
if let index = word.range(of: "n")?.lowerBound {
let substring = word[..<index] // "ora"
// or let substring = word.prefix(upTo: index) // "ora"
// (see picture below) Using the prefix(upTo:) method is equivalent to using a partial half-open range as the collection’s subscript.
// The subscript notation is preferred over prefix(upTo:).
let string = String(substring)
print(string) // "ora"
}
You could do it like this:
guard let range = text.rangeOfString("Your String or Character here") else {
return the text
}
return text.substringToIndex(range.endIndex)
// depending on if you want to delete before a certain string, you would use range.startIndex

Multiline statement in Swift

I was working on a Swift tutorial and found that Swift has a strange way to handle multi-line statement.
First, I defined some extension to the standard String class:
extension String {
func replace(target: String, withString: String) -> String {
return self.stringByReplacingOccurrencesOfString(target, withString: withString)
}
func toLowercase() -> String {
return self.lowercaseString
}
}
This works as expected:
let str = "HELLO WORLD"
let s1 = str.lowercaseString.replace("hello", withString: "goodbye") // -> goodbye world
This doesn't work:
let s2 = str
.lowercaseString
.replace("hello", withString: "goodbye")
// Error: could not find member 'lowercaseString'
If I replace the reference to the lowercaseString property with a function call, it works again:
let s3 = str
.toLowercase()
.replace("hello", withString: "goodbye") // -> goodbye world
Is there anything in the Swift language specifications that prevent a property to be broken onto its own line?
Code at Swift Stub.
This is definitely a compiler bug. Issue has been resolved in Xcode 7 beta 3.
This feels like a compiler bug, but it relates to the fact that you can define prefix, infix, and postfix operators in Swift (but not the . operator, ironically enough). I don't know why it only gives you grief on the property and not the function call, but is a combination of two things:
the whitespace before and after the . (dot) operator for properties (only)
some nuance of this ever growing language that treats properties differently than function calls (even though functions are supposed to first class types).
I would file a bug to see what comes out of it, Swift is not supposed to by pythonic this way. That said, to work around it, you can either not break the property from the type, or you can add a white space before and after the . .
let s2 = str.lowercaseString
.replace("hello", withString: "goodbye")
let s3 = str
. lowercaseString
.replace("hello", withString: "goodbye")
Using semicolons is not mandatory in swift. And I think that the problems with multiline statements in swift are because of optional semicolons.
Note that swift does not support multiline strings. Check here: Swift - Split string over multiple lines
So maybe swift cannot handle multiline statements. I am not sure about this and this could be one of the reasons so I would appreciate if anyone else can help regarding this issue.