diff --git a/concepts/importing/about.md b/concepts/importing/about.md index d9fff729a..6d0e44e0c 100644 --- a/concepts/importing/about.md +++ b/concepts/importing/about.md @@ -1,8 +1,10 @@ # About -While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries. These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver]. +While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries. +These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver]. -Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution. External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise. +Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution. +External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise. Importing modules is done by writing the `import` keyword followed by the name of the module. So one can import Foundation by adding the following to their program. diff --git a/concepts/importing/introduction.md b/concepts/importing/introduction.md index 054e2b7c5..131e05eb0 100644 --- a/concepts/importing/introduction.md +++ b/concepts/importing/introduction.md @@ -1,11 +1,22 @@ # Introduction -Importing modules is done by writing the `import` keyword followed by the name of the module. This allows access to all of the types, values, and functionality inside that module; in this example we are making use of the `components(separatedBy:)` method which becomes available to `String` with this import. +While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries. +These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver]. + +Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution. +External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise. + +Importing modules is done by writing the `import` keyword followed by the name of the module. So one can import Foundation by adding the following to their program. ```swift import Foundation - -let csv = "apple,pear,peach,orange,cherry,lime,goosberry" -let fruit = csv.components(separatedBy: ",") -// => ["apple", "pear", "peach", "orange", "cherry", "lime", "goosberry"] ``` + +This allows access to all of the types, values, and functionality inside that module; for example if one wishes to use the `components(separatedBy:)` String method, that method becomes available to `String` with this import. + +While they can be placed in the code anywhere before a pice of code that makes use of one the content of module, import statements are usually placed at the beginning of the file that uses them for greater readability. + +[argument-parser]: https://apple.github.io/swift-argument-parser/documentation/argumentparser/ +[network-framework]: https://developer.apple.com/documentation/network +[swifty-beaver]: https://github.com/SwiftyBeaver/SwiftyBeaver +[apple-foundation]: https://developer.apple.com/documentation/foundation diff --git a/concepts/string-components/.meta/config.json b/concepts/string-components/.meta/config.json deleted file mode 100644 index 379eaa173..000000000 --- a/concepts/string-components/.meta/config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "blurb": "Strings can be taken apart and broken into individual components.", - "authors": [ - "wneumann" - ], - "contributors": [] -} diff --git a/concepts/string-components/about.md b/concepts/string-components/about.md deleted file mode 100644 index 5a2ff43dd..000000000 --- a/concepts/string-components/about.md +++ /dev/null @@ -1,46 +0,0 @@ -# About - -We've seen how to build up strings in previous exercises. Here we will see some of the ways to break strings apart. - -## Breaking strings apart - -In addition to building up strings out of components, strings may also be taken apart and broken into individual components. This is useful for tasks such as breaking apart a list of comma separated values into an array of the individual entities. Note that not all of these functions are in Swift's standard library and will require the importing of additional libraries. In this example we are making use of the `components(separatedBy:)` method which requires importing the `Foundation` module. Once `Foundation` is imported, this method and many others become available to the `String` type. - -```swift -import Foundation - -let csv = "apple,pear,peach,orange,cherry,lime,goosberry" -let fruit = csv.components(separatedBy: ",") -// => ["apple", "pear", "peach", "orange", "cherry", "lime", "goosberry"] -``` - -## Accessing individual characters - -Accessing individual characters in strings is both like and unlike accessing individual elements of an array. The first and last elements are easily accessed using the `first` and `last` properties of the string respectively. Note that, as a string may not have a first or last character, these properties return optional Characters which will need to be unwrapped if they are to be used. - -```swift -let scal = "Supercalifragilisticexpialidocious" -let empty = "" - -scal.first -// => Optional("S") -scal.last! -// => "s" -empty.first -// => nil -``` - -## Accessing internal characters - -In Swift, strings are treated more as something that is to be traversed, rather than randomly accessed, though that can be done. There are two main reasons for this: - -1. Swift strings are built with unicode in mind, and you don't know ahead of time how much space any unicode character will take up, this is especially the case when emoji come into play, so using random-indexing is either verbose or slow or both. -2. Most real world string processing is done by traversing strings and not randomly accessing bits of them. - -Because of this, most operations on Strings are done by pulling characters off the front or back of a String using operations like `prefix(_:)`, `suffix(_:)`, `dropFirst(_:)`, `dropLast(_:)`, etc. (these methods are discussed in the [`String` documentation][string-docs]). - -So, for example, to get the first three characters of a string, rather than write `myString[0...2]`, as one might with an `Array`, they would instead write `myString.prefix(3)`. And to get the second three, they may write something like `myString.dropFirst(3).prefix(3)` or `myString.prefix(6).suffix(3)`. There are a few ways to do things operations like that and they have different benefits and downsides. - -Trying to use indices and index manipulation with strings is simply fighting against the design of Swift and often leads to more problems than it solves. - -[string-docs]: https://developer.apple.com/documentation/swift/String diff --git a/concepts/string-components/introduction.md b/concepts/string-components/introduction.md deleted file mode 100644 index d419fe7f0..000000000 --- a/concepts/string-components/introduction.md +++ /dev/null @@ -1,15 +0,0 @@ -# Introduction - -### Breaking strings apart - -In addition to building up strings out of components, strings may also be taken apart and broken into individual components. This is useful for tasks such as breaking apart a list of comma separated values into an array of the individual entities. Note that not all of these functions are in Swift's standard library and will require the importing of additional libraries. The most commonly used library is `Foundation`. - -Importing modules is done by writing the `import` keyword followed by the name of the module. This allows access to all of the types, values, and functionality inside that module; in this example we are making use of the `components(separatedBy:)` method which becomes available to `String` with this import. - -```swift -import Foundation - -let csv = "apple,pear,peach,orange,cherry,lime,goosberry" -let fruit = csv.components(separatedBy: ",") -// => ["apple", "pear", "peach", "orange", "cherry", "lime", "goosberry"] -``` diff --git a/concepts/string-components/links.json b/concepts/string-components/links.json deleted file mode 100644 index fe51488c7..000000000 --- a/concepts/string-components/links.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/concepts/string-indexing/.meta/config.json b/concepts/string-indexing/.meta/config.json index 999bef8e5..2621e9919 100644 --- a/concepts/string-indexing/.meta/config.json +++ b/concepts/string-indexing/.meta/config.json @@ -3,5 +3,7 @@ "authors": [ "wneumann" ], - "contributors": [] + "contributors": [ + "meatball133" + ] } diff --git a/concepts/string-indexing/about.md b/concepts/string-indexing/about.md index e79057e0e..693cba477 100644 --- a/concepts/string-indexing/about.md +++ b/concepts/string-indexing/about.md @@ -1,6 +1,8 @@ # About -Individual characters in a `String` can be accessed using the subscripting method used with arrays. However, the indices used for strings are _not_ integers and can not be worked with directly. Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods). +Individual characters in a `String` can be accessed using the subscripting method used with arrays. +However, the [indices][string-indices] used for strings are _not_ integers and can not be worked with directly. +Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods). For example, given the following string: @@ -8,32 +10,78 @@ For example, given the following string: let csv = "apple,pear,peach,orange,cherry,lime,goosberry" ``` -One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead. Note that these indices are not meant to be human-consumable on their own. They are what is referred to as _opaque indices_ ,as humans need not know what is inside them. +One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead. +Note that these indices are not meant to be human-consumable on their own. +They are what is referred to as _opaque indices_ ,as humans need not know what is inside them. ```swift let index = csv.index(csv.startIndex, offsetBy: 21) csv[index] -// => "g" +// "g" print(index) -// => Index(_rawBits: 1376513) +// prints Index(_rawBits: 1376513) ``` -Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program. To prevent this problem, one can specify a limiting index. This returns an optional index and it will return nil for otherwise invalid indices. +Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program. +To prevent this problem, one can specify a limiting index. +This returns an optional index and it will return nil for otherwise invalid indices. ```swift let tooFar = csv.index(csv.startIndex, offsetBy: 200) -// => error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). +// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). let tooFarButSafe = csv.index(csv.startIndex, offsetBy: 200, limitedBy: csv.endIndex) -// => nil +// nil ``` -Additionally, indices cannot be shared between strings. For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ, this is our dog: ๐Ÿถ, this is our cat: ๐Ÿฑ!"`, i.e. `fam[index]` will crash your program. +Additionally, indices cannot be shared between strings. +For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ, this is our dog: ๐Ÿถ, this is our cat: ๐Ÿฑ!"`, i.e. `fam[index]` will crash your program. There are many reasons for all of this, but they all basically boil down to "Unicode is tricky". Generally speaking if you need random access to individual characters, you likely want to be using some other data structure, like `Array`. +## Some useful methods + +Swift has the methods `first` and `last` to get the first and last character of a string. +These methods return an optional character, so if the string is empty, they will return nil. + +```swift +let str = "Hello, world!" +print(str.first) // Prints Optional("H") +print(str.last) // Prints Optional("!") +``` + +You can also use the `prefix` and `suffix` methods to get a substring of a string. +These methods take an integer as an argument and return a substring containing the first or last n characters of the string. + +```swift +let str = "Hello, world!" +print(str.prefix(5)) // Prints "Hello" +print(str.suffix(6)) // Prints "world!" +``` + +## Deep Dive: Unicode indexing + +~~~~exercism/advanced + +[Unicode][unicode] is a standard for encoding text, it features a large number of characters from many different languages and scripts. +It is a superset of ASCII, which means that all ASCII characters are also Unicode characters. +Unicode is a variable-length encoding, meaning that some characters take up more bytes than others. +And why that matters will be explained in the next section. + +So we have to go other languages (than English) to actually see the problem, let's take the character "uฬˆ" from the German language (note that here are we using the 2 byte variant there is also a 1 byte version). +It may at first look like a single character, but it is actually two characters: "u" and "ยจ", so if you printed the length of the string "uฬˆ" it would be 2 in languages like Python. +However, as a user or a programmer we would say that it is a single character and Swift actually agrees with us. +But why does Swift do that but Python does not? +The answer is that Swift uses a different representation of strings than Python. +In Python, strings are represented as a sequence of bytes, which means that the length of a string is the number of bytes it takes up in memory. +In Swift, strings are represented as a sequence of characters, which means that the length of a string is the number of characters it contains. +This is a very important distinction, because it means that in Swift, the length of a string is not necessarily the same as the number of bytes it takes up in memory. +This in turn is what makes so we can't use integers to index into strings. +~~~~ + [string-docs]: https://developer.apple.com/documentation/swift/String [nsstring-docs]: https://developer.apple.com/documentation/foundation/nsstring [string-format-specifiers]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html -[string-interpolation]: https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID292 +[string-indices]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices +[unicode]: https://en.wikipedia.org/wiki/Unicode diff --git a/concepts/string-indexing/introduction.md b/concepts/string-indexing/introduction.md index 0d8aaa604..0ecf59d40 100644 --- a/concepts/string-indexing/introduction.md +++ b/concepts/string-indexing/introduction.md @@ -1,13 +1,67 @@ # Introduction -Accessing individual characters in strings is both like and unlike accessing individual elements of an array. The first and last elements are easily accessed using the `first` and `last` properties of the string respectively. Note that, as a string may not have a first or last character, these properties return optional Characters which will need to be unwrapped if they are to be used. +Individual characters in a `String` can be accessed using the subscripting method used with arrays. +However, the [indices][string-indices] used for strings are _not_ integers and can not be worked with directly. +Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods). -Other individual characters can be accessed using the subscripting method used with arrays. However, the indices used for strings are _not_ integers and can not be worked with directly. Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string. +For example, given the following string: -For example, you cannot write `csv[21]` to get the "g" in "orange", you must instead compute a value of type `String.Index`. +```swift +let csv = "apple,pear,peach,orange,cherry,lime,goosberry" +``` + +One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead. +Note that these indices are not meant to be human-consumable on their own. +They are what is referred to as _opaque indices_ ,as humans need not know what is inside them. ```swift let index = csv.index(csv.startIndex, offsetBy: 21) csv[index] -// => "g" +// "g" +print(index) +// prints Index(_rawBits: 1376513) +``` + +Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program. +To prevent this problem, one can specify a limiting index. +This returns an optional index and it will return nil for otherwise invalid indices. + +```swift +let tooFar = csv.index(csv.startIndex, offsetBy: 200) +// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). +let tooFarButSafe = csv.index(csv.startIndex, offsetBy: 200, limitedBy: csv.endIndex) +// nil +``` + +Additionally, indices cannot be shared between strings. +For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ, this is our dog: ๐Ÿถ, this is our cat: ๐Ÿฑ!"`, i.e. `fam[index]` will crash your program. + +There are many reasons for all of this, but they all basically boil down to "Unicode is tricky". + +Generally speaking if you need random access to individual characters, you likely want to be using some other data structure, like `Array`. + +## Some useful methods + +Swift has the methods `first` and `last` to get the first and last character of a string. +These methods return an optional character, so if the string is empty, they will return nil. + +```swift +let str = "Hello, world!" +print(str.first) // Prints Optional("H") +print(str.last) // Prints Optional("!") ``` + +You can also use the `prefix` and `suffix` methods to get a substring of a string. +These methods take an integer as an argument and return a substring containing the first or last n characters of the string. + +```swift +let str = "Hello, world!" +print(str.prefix(5)) // Prints "Hello" +print(str.suffix(6)) // Prints "world!" +``` + +[string-docs]: https://developer.apple.com/documentation/swift/String +[nsstring-docs]: https://developer.apple.com/documentation/foundation/nsstring +[string-format-specifiers]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html +[string-indices]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices +[unicode]: https://en.wikipedia.org/wiki/Unicode diff --git a/concepts/string-indexing/links.json b/concepts/string-indexing/links.json index fe51488c7..8b111c325 100644 --- a/concepts/string-indexing/links.json +++ b/concepts/string-indexing/links.json @@ -1 +1,6 @@ -[] +[ + { + "url": "https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices", + "description": "Swift Book: String Indices" + } +] diff --git a/concepts/string-methods/.meta/config.json b/concepts/string-methods/.meta/config.json new file mode 100644 index 000000000..076dc129e --- /dev/null +++ b/concepts/string-methods/.meta/config.json @@ -0,0 +1,8 @@ +{ + "blurb": "Strings have a variety of methods that can be used to manipulate them.", + "authors": [ + "wneumann", + "meatball133" + ], + "contributors": [] +} diff --git a/concepts/string-methods/about.md b/concepts/string-methods/about.md new file mode 100644 index 000000000..abd4546b0 --- /dev/null +++ b/concepts/string-methods/about.md @@ -0,0 +1,71 @@ +# About + +Working with string is a common task in programming. +Therefore Swift provides a variety of methods to manipulate strings. +This document will cover some of the most common string methods and properties, some of these require the import of the Foundation framework. + +## [`components(separatedBy:)`][components] + +When you want to split a string into an array of substrings, you can use the `components(separatedBy:)` method. +So say you would want to get every word in a sentence, you can use the space character as the separator. + +```swift +let sentence = "Hello, world!" +let words = sentence.components(separatedBy: " ") +print(words) // Prints ["Hello,", "world!"] +``` + +## [`hasPrefix(_:)`][hasPrefix], [`hasSuffix(_:)`][hasSuffix] + +When you want to check if a string starts or ends with a certain substring, you can use the `hasPrefix(_:)` and `hasSuffix(_:)` methods. +Both of these methods take a string as an argument and return a boolean value indicating whether the string starts or ends with the specified substring. + +```swift +let greeting = "Hello, world!" +print(greeting.hasPrefix("Hello")) // Prints true +print(greeting.hasSuffix("world!")) // Prints true +``` + +## [`lowercased`][lowercased], [`uppercased`][uppercased], [`capitalized`][capitalized] + +When you want to change the case of a string, you can use the `lowercased`, `uppercased`, and `capitalized` properties. +The `lowercased` property returns a new string with all characters in lowercase, the `uppercased` property returns a new string with all characters in uppercase, and the `capitalized` property returns a new string with the first character of each word capitalized. + +```swift +let greeting = "hello, world!" +print(greeting.lowercased) // Prints "hello, world!" +print(greeting.uppercased) // Prints "HELLO, WORLD!" +print(greeting.capitalized) // Prints "Hello, World!" +``` + +## [`replacingOccurrences(of:with:)`][replacingOccurrences] + +When you want to replace all occurrences of a substring in a string with another substring, you can use the `replacingOccurrences(of:with:)` method. +This method takes two strings as arguments: the substring to be replaced and the substring to replace it with. It returns a new string with all occurrences of the specified substring replaced. + +```swift +let greeting = "Hello, world!" +let newGreeting = greeting.replacingOccurrences(of: "world", with: "Swift") +print(newGreeting) // Prints "Hello, Swift!" +``` + +## [`joined(separator:)`][joined] + +This isn't a method of the String type, but rather of the Array type. +When you want to join an array of strings into a single string with a specified separator, you can use the `joined(separator:)` method. +This method takes a string as an argument and returns a new string with all elements of the array joined together, separated by the specified string. + +```swift +let words = ["Hello", "world"] +let sentence = words.joined(separator: ", ") +print(sentence) // Prints "Hello, world" +``` + +[components]: https://developer.apple.com/documentation/foundation/nsstring/components(separatedby:)-238fy +[hasPrefix]: https://developer.apple.com/documentation/foundation/nsstring/hasprefix(_:) +[hasSuffix]: https://developer.apple.com/documentation/foundation/nsstring/hassuffix(_:) +[lowercased]: https://developer.apple.com/documentation/foundation/nsstring/lowercased +[uppercased]: https://developer.apple.com/documentation/foundation/nsstring/uppercased +[capitalized]: https://developer.apple.com/documentation/foundation/nsstring/capitalized +[replacingOccurrences]: https://developer.apple.com/documentation/foundation/nsstring/replacingoccurrences(of:with:) +[joined]: https://developer.apple.com/documentation/swift/array/joined(separator:)-7uber diff --git a/concepts/string-methods/introduction.md b/concepts/string-methods/introduction.md new file mode 100644 index 000000000..2c5bfc7e6 --- /dev/null +++ b/concepts/string-methods/introduction.md @@ -0,0 +1,71 @@ +# Introduction + +Working with string is a common task in programming. +Therefore Swift provides a variety of methods to manipulate strings. +This document will cover some of the most common string methods and properties, some of these require the import of the Foundation framework. + +## [`components(separatedBy:)`][components] + +When you want to split a string into an array of substrings, you can use the `components(separatedBy:)` method. +So say you would want to get every word in a sentence, you can use the space character as the separator. + +```swift +let sentence = "Hello, world!" +let words = sentence.components(separatedBy: " ") +print(words) // Prints ["Hello,", "world!"] +``` + +## [`hasPrefix(_:)`][hasPrefix], [`hasSuffix(_:)`][hasSuffix] + +When you want to check if a string starts or ends with a certain substring, you can use the `hasPrefix(_:)` and `hasSuffix(_:)` methods. +Both of these methods take a string as an argument and return a boolean value indicating whether the string starts or ends with the specified substring. + +```swift +let greeting = "Hello, world!" +print(greeting.hasPrefix("Hello")) // Prints true +print(greeting.hasSuffix("world!")) // Prints true +``` + +## [`lowercased`][lowercased], [`uppercased`][uppercased], [`capitalized`][capitalized] + +When you want to change the case of a string, you can use the `lowercased`, `uppercased`, and `capitalized` properties. +The `lowercased` property returns a new string with all characters in lowercase, the `uppercased` property returns a new string with all characters in uppercase, and the `capitalized` property returns a new string with the first character of each word capitalized. + +```swift +let greeting = "hello, world!" +print(greeting.lowercased) // Prints "hello, world!" +print(greeting.uppercased) // Prints "HELLO, WORLD!" +print(greeting.capitalized) // Prints "Hello, World!" +``` + +## [`replacingOccurrences(of:with:)`][replacingOccurrences] + +When you want to replace all occurrences of a substring in a string with another substring, you can use the `replacingOccurrences(of:with:)` method. +This method takes two strings as arguments: the substring to be replaced and the substring to replace it with. It returns a new string with all occurrences of the specified substring replaced. + +```swift +let greeting = "Hello, world!" +let newGreeting = greeting.replacingOccurrences(of: "world", with: "Swift") +print(newGreeting) // Prints "Hello, Swift!" +``` + +## [`joined(separator:)`][joined] + +This isn't a method of the String type, but rather of the Array type. +When you want to join an array of strings into a single string with a specified separator, you can use the `joined(separator:)` method. +This method takes a string as an argument and returns a new string with all elements of the array joined together, separated by the specified string. + +```swift +let words = ["Hello", "world"] +let sentence = words.joined(separator: ", ") +print(sentence) // Prints "Hello, world" +``` + +[components]: https://developer.apple.com/documentation/foundation/nsstring/components(separatedby:)-238fy +[hasPrefix]: https://developer.apple.com/documentation/foundation/nsstring/hasprefix(_:) +[hasSuffix]: https://developer.apple.com/documentation/foundation/nsstring/hassuffix(_:) +[lowercased]: https://developer.apple.com/documentation/foundation/nsstring/lowercased +[uppercased]: https://developer.apple.com/documentation/foundation/nsstring/uppercased +[capitalized]: https://developer.apple.com/documentation/foundation/nsstring/capitalized +[replacingOccurrences]: https://developer.apple.com/documentation/foundation/nsstring/replacingoccurrences(of:with:) +[joined]: https://developer.apple.com/documentation/swift/array/joined(separator:)-7uber diff --git a/concepts/string-methods/links.json b/concepts/string-methods/links.json new file mode 100644 index 000000000..c4f9c9ec9 --- /dev/null +++ b/concepts/string-methods/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://developer.apple.com/documentation/foundation/nsstring", + "description": "Swift Docs: NSString" + } +] diff --git a/config.json b/config.json index 0376ea85c..6a4c6d329 100644 --- a/config.json +++ b/config.json @@ -198,7 +198,7 @@ "uuid": "082d7aeb-ee25-4217-bd6f-82532a65797a", "concepts": [ "importing", - "string-components", + "string-methods", "string-indexing" ], "prerequisites": [ @@ -250,7 +250,7 @@ "enumerations" ], "prerequisites": [ - "string-components", + "string-methods", "structs-and-classes" ], "status": "active" @@ -1520,8 +1520,8 @@ }, { "uuid": "343255a9-9931-4887-941e-e768448bf6a4", - "slug": "string-components", - "name": "String Components" + "slug": "string-methods", + "name": "String Methods" }, { "uuid": "6b721b54-e85e-4156-af34-04f36ec37af0", diff --git a/exercises/concept/poetry-club/.docs/hints.md b/exercises/concept/poetry-club/.docs/hints.md index 7517c317e..a0a768a9a 100644 --- a/exercises/concept/poetry-club/.docs/hints.md +++ b/exercises/concept/poetry-club/.docs/hints.md @@ -4,42 +4,27 @@ - Swift has a [method][nsstring-components-docs] for splitting up a string into an array of strings using a supplied delimiter. -## 2. Get the first letter of a sentence +## 2. Front door -- Swift has a [method][string-first-docs] for retrieving the first character of a string. -- The return type of this method is `Character?`. Be sure to handle it appropriately. +- The guard will recite a poem, and you will have to split the poem into individual lines, you can use an already existing function for this. +- You can use a for loop to iterate over the lines of the poem. +- You can use the `first` property of a string to get the first character of a string. +- To store the first character of each line, you can use an array of characters or a string. -## 3. Capitalize a word +## 3. Back door -- Swift has a [property][nsstring-capitalized-docs] for returning a string formatted with _Title Case Capitalization_. +- The guard will recite a poem, and you will have to split the poem into individual lines, you can use an already existing function for this. +- You can use a for loop to iterate over the lines of the poem. +- You can use the `last` property of a string to get the last character of a string. +- To store the last character of each line, you can use an array of characters or a string. -## 4. Trim a sentence +## 4. Secret room -- Swift has a [method][nsstring-trimming-docs] for removing whitespace from around a string.. +- The guard will recite a poem, and you will have to split the poem into individual lines, you can use an already existing function for this. +- You can use a for loop that iterates over the length of the lines of the poem. +- Swift has a [method][indexing] for getting the character at a specific index in a string. +- Swift has a [method][uppercased] for converting a string to uppercase. -## 5. Get the last letter of a sentence - -- Swift has a [method][string-last-docs] for retrieving the last character of a string. -- The return type of this method is `Character?`. Be sure to handle it appropriately. - -## 6. Be polite - -- String interpolation or concatenation can be used to glue two strings together. - -## 7. Get the ith letter of a sentence - -- Swift has a [method][string-index-offset] for computing an index based that is offset from another index. -- Swift Strings have a property which returns returns an index to [the position of the first character in a nonempty string][string-startindex-docs]. - -## 8. Shout the answer - -- Swift has a [property][nsstring-uppercased-docs] for returning a string formatted into all uppercase letters. - -[nsstring-components-docs]: https://developer.apple.com/documentation/foundation/nsstring/1413214-components -[string-first-docs]: https://developer.apple.com/documentation/swift/string/2894206-first -[string-last-docs]: https://developer.apple.com/documentation/swift/string/2893970-last -[string-startindex-docs]: https://developer.apple.com/documentation/swift/string/1540930-startindex -[nsstring-trimming-docs]: https://developer.apple.com/documentation/foundation/nsstring/1415462-trimmingcharacters -[nsstring-capitalized-docs]: https://developer.apple.com/documentation/foundation/nsstring/1416784-capitalized -[string-index-offset]: https://developer.apple.com/documentation/swift/string/1786175-index -[nsstring-uppercased-docs]: https://developer.apple.com/documentation/foundation/nsstring/1409855-uppercased +[nsstring-components-docs]: https://developer.apple.com/documentation/foundation/nsstring/components(separatedby:)-238fy +[indexing]: https://developer.apple.com/documentation/swift/string/index(_:offsetby:) +[uppercased]: https://developer.apple.com/documentation/foundation/nsstring/uppercased diff --git a/exercises/concept/poetry-club/.docs/instructions.md b/exercises/concept/poetry-club/.docs/instructions.md index 9431679d9..475e2948b 100644 --- a/exercises/concept/poetry-club/.docs/instructions.md +++ b/exercises/concept/poetry-club/.docs/instructions.md @@ -6,12 +6,25 @@ Because there have been incidents in the past, the club has a very specific door There are two doors at the poetry club, both are guarded. In order to gain entry, you'll need to work out the password of that day: -## Front door +## 1. Split a string into individual lines + +The first thing you need to do is split the poem into individual lines. +The guard will recite a poem, and you will have to split it into lines. + +Implement the function `splitOnNewlines` that takes a string and splits it into an array of strings, using the newline character as the delimiter. +The function should return an array of strings, where each string is a line from the poem. + +```swift +splitOnNewlines("Hello.\nHow are you?\n\nI'm doing fine.") +// returns ["Hello." ,"How are you?", "", "I'm doing fine"]. +``` + +## 2. Front door 1. The guard will recite a poem - - You will have to split the poem into individual lines and respond with the appropriate letters. -1. The guard will tell you all the letters you've responded with at once; - - You need to format the letters as a capitalized word. +2. You will have to split the poem into individual lines. +3. You will have to get the first letter of each line. +4. You will have to ensable the letters, which will form a word. For example, one of their favorite writers is Michael Lockwood, who's written the following _acrostic_ poem, which means that the first letter of each sentence form a word: @@ -26,19 +39,26 @@ Eager to leave When the guard recites the poem, you will split it into individual lines and respond with the first letters of each line, i.e. `["S", "H", "I", "R", "E"]`. The guard will then give you the word formed by the array of letters you replied with for you to put into capitalized word form. -Finally the password you return is `"Shire"`, and you'll get in. +Finally the password you return is `"SHIRE"`, and you'll get in. + +Implement the function `frontDoorPassword` that takes a string which represents the poem. +The function should return the password that you would give to the guard. -## Back door +```swift +frontDoorPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nReins and harness\nEager to leave") +// returns "SHIRE" +``` + +## 3. Back door In the back of the club you'll find the most renowned poets, which is like the VIP area. Because this is not for everyone, the back door process is a bit more convoluted. -1. The guard will recite a poem; - - Again, you will have to split the poem into lines and respond with the appropriate letter _but - there are sometimes spaces after each sentence that will need to be removed first_. -1. The guard will tell you all the letters you've responded with at once: - - You need to format the letters as a capitalized word - - and ask nicely, by appending `", please"` +1. The guard will recite a poem. +2. You will have to split the poem into lines. +3. You will have to get the last letter of each line. + - The line will sometimes have trailing spaces, so you will need to strip them off. +4. You will have to assemble the letters and then add `", please."` to the end of the word formed by the letters. For example, the poem mentioned before is also _telestich_, which means that the last letter of each sentence form a word: @@ -53,17 +73,25 @@ Eager to leave When the guard recites the poem, you will split it into individual lines, strip off any trailing spaces, and respond with the first letters of each line, i.e. `["h", "o", "r", "s", "e"]`. The guard will then give you the word formed by the array of letters you replied with for you to put into capitalized word form and append `", please."`. -Finally the password you return is `"Horse, please."`, and you'll get in. +Finally the password you return is `"horse, please."`, and you'll get in. + +Implement the function `backDoorPassword` that takes a string which represents the poem. +The function should return the password that you would give to the guard. + +```swift +backDoorPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nReins and harness\nEager to leave") +// returns "horse, please." +``` -## Secret room +## 4. Secret room Inside the back room of the club is another door that leads to the secret room that only the very top poets may enter. -1. The guard will recite a poem; - - Again, you will have to split the poem into lines and respond with the appropriate letter _but now the appropriate letter from line number i is the ith letter of the line_. -1. The guard will tell you all the letters you've responded with at once: - - You need to format the letters as a capitalized word - - You must shout the secret phrase by returning an uppercased string with an exclamation point added to the end. +1. The guard will recite a poem. +2. You will have to split the poem into lines. +3. You will have to get the ith letter of the ith line. +4. You will have to assemble the letters and then add `"!"` to the end of the word formed by the letters. +5. You will have to put the word in uppercased form. For example, a modified version of the poem mentioned before fits this pattern: @@ -80,80 +108,10 @@ When the guard recites the poem, you will split it into individual lines, strip The guard will then give you the word formed by the array of letters you replied with for you to put into uppercased word form and append `"!"`. Finally the password you return is `SUPER!`, and you'll get in. -## 1. Split a string into individual lines - -Implement the function `splitOnNewlines(_:)` that takes a `String` as input and splits it into an array of `String`s using newlines as delimiters. - -```swift -splitOnNewlines("Hello.\nHow are you?\n\nI'm doing fine.") -// => ["Hello." ,"How are you?", "", "I'm doing fine"]. -``` - -## 2. Get the first letter of a sentence - -Implement the function `firstLetter(_:)` that returns first letter of a sentence. -If there is no first letter, return an underscore (`_`): - -```swift -firstLetter("Stands so high") -// => "S" -``` - -## 3. Capitalize a word - -Implement the function `capitalize(_:)` that correctly capitalizes a word: - -```swift -capitalize("SHIRE") -// => "Shire" - -capitalize("horse") -// => "Horse" -``` - -## 4. Trim a sentence - -Implement the function `trimSentence(_:)` that removes whitespace from the start and end of a sentence and returns the trimmed sentence: - -```swift -trimSentence("Stands so high ") -// => "Stands so high" -``` - -## 5. Get the last letter of a sentence - -Implement the function `lastLetter(_:)` that returns the last letter of a sentence. -If there is no last letter, return an underscore (`_`): - -```swift -lastLetter("Stands so high") -// => "h" -``` - -## 6. Be polite - -Implement the function `backDoorPassword(_:)` that takes a string as input and formats it in the polite manner required for the backdoor password: - -```swift -backDoorPassword("horse") -// => "Horse, please" -``` - -## 7. Get the ith letter of a sentence - -Implement the function `ithLetter(_:i:)` that returns the ith letter of a sentence. -If there is no ith letter, return a space: - -```swift -ithLetter("Impatiently waits for", i: 2) -// => "p" -``` - -## 8. Shout the answer - -Implement the function `secretRoomPassword(_:)` that takes a string as input and formats it in the shouting manner required for the secret room password: +Implement the function `secretRoomPassword` that takes a string which represents the poem. +The function should return the password that you would give to the guard. ```swift -secretRoomPassword('Super') -// => "SUPER!" +secretRoomPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nRider with harness\nEager to leave") +// returns "SUPER!" ``` diff --git a/exercises/concept/poetry-club/.docs/introduction.md b/exercises/concept/poetry-club/.docs/introduction.md index 4f4c5b9de..4a52385cc 100644 --- a/exercises/concept/poetry-club/.docs/introduction.md +++ b/exercises/concept/poetry-club/.docs/introduction.md @@ -1,33 +1,162 @@ # Introduction -## Breaking strings apart +## Importing -Strings may be taken apart and broken into individual components. This is useful for tasks such as breaking apart a list of comma separated values into an array of the individual entities. Note that not all of these functions are in Swift's standard library and will require the importing of additional libraries. The most commonly used library is `Foundation`. +While Swift includes a lot of functionality in its Standard Library, there is much more functionality available in the form of external libraries. +These libraries may be from the Swift project itself, such as the [Swift Argument Parser][argument-parser], closed source libraries from companies Apple's [Network Framework][network-framework], or third-party libraries, like [Swifty Beaver][swifty-beaver]. -Importing modules is done by writing the `import` keyword followed by the name of the module. This allows access to all of the types, values, and functionality inside that module; in this example we are making use of the `components(separatedBy:)` method which becomes available to `String` with this import. +Some of these modules, like the Network Framework or [Foundation][apple-foundation] (which is probably the most commonly used library in Swift) come with your Swift distribution. +External third party libraries need to be added to your project before they can be imported, though that is out of the scope of this exercise. + +Importing modules is done by writing the `import` keyword followed by the name of the module. So one can import Foundation by adding the following to their program. ```swift import Foundation - -let csv = "apple,pear,peach,orange,cherry,lime,goosberry" -let fruit = csv.components(separatedBy: ",") -// => ["apple", "pear", "peach", "orange", "cherry", "lime", "goosberry"] ``` -## Accessing individual characters +This allows access to all of the types, values, and functionality inside that module; for example if one wishes to use the `components(separatedBy:)` String method, that method becomes available to `String` with this import. + +While they can be placed in the code anywhere before a pice of code that makes use of one the content of module, import statements are usually placed at the beginning of the file that uses them for greater readability. -Accessing individual characters in strings is both like and unlike accessing individual elements of an array. The first and last elements are easily accessed using the `first` and `last` properties of the string respectively. Note that, as a string may not have a first or last character, these properties return optional Characters which will need to be unwrapped if they are to be used. +## String indexing -Other individual characters can be accessed using the subscripting method used with arrays. However, the indices used for strings are _not_ integers and can not be worked with directly. Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string. +Individual characters in a `String` can be accessed using the subscripting method used with arrays. +However, the [indices][string-indices] used for strings are _not_ integers and can not be worked with directly. +Instead they must be computed based off of some other known index, such as `startIndex`, which points to the position of the first character in a nonempty string, using the methods available in the [`String`][string-docs] and [`NSString`][nsstring-docs] libraries (when the `Foundation` module is imported, strings in Swift have access to all of the NSString properties and methods). -For example, you cannot write `csv[21]` to get the "g" in "orange", you must instead compute a value of type `String.Index`. +For example, given the following string: + +```swift +let csv = "apple,pear,peach,orange,cherry,lime,goosberry" +``` + +One cannot write `csv[21]` to get the "g" in "orange", they must instead compute a value of type `String.Index` and supply that index instead. +Note that these indices are not meant to be human-consumable on their own. +They are what is referred to as _opaque indices_ ,as humans need not know what is inside them. ```swift let index = csv.index(csv.startIndex, offsetBy: 21) csv[index] -// => "g" +// "g" +print(index) +// prints Index(_rawBits: 1376513) +``` + +Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program. +To prevent this problem, one can specify a limiting index. +This returns an optional index and it will return nil for otherwise invalid indices. + +```swift +let tooFar = csv.index(csv.startIndex, offsetBy: 200) +// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). +let tooFarButSafe = csv.index(csv.startIndex, offsetBy: 200, limitedBy: csv.endIndex) +// nil ``` -The most basic indices associated with strings are the start and end indices which point to the first character and the _position after_ the last character in a string. These indices are always guaranteed to be valid indices, but they may not necessarily point to valid characters in a string. For example, since `endIndex` is the index that points to the position _after_ the last character, trying to subscript the string at that position will raise an error. +Additionally, indices cannot be shared between strings. +For example, using the `index` of the "g" in "orange" computed above to index into the string `fam = "This is my family: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ, this is our dog: ๐Ÿถ, this is our cat: ๐Ÿฑ!"`, i.e. `fam[index]` will crash your program. + +There are many reasons for all of this, but they all basically boil down to "Unicode is tricky". + +Generally speaking if you need random access to individual characters, you likely want to be using some other data structure, like `Array`. + +### Some useful methods + +Swift has the methods `first` and `last` to get the first and last character of a string. +These methods return an optional character, so if the string is empty, they will return nil. + +```swift +let str = "Hello, world!" +print(str.first) // Prints Optional("H") +print(str.last) // Prints Optional("!") +``` + +You can also use the `prefix` and `suffix` methods to get a substring of a string. +These methods take an integer as an argument and return a substring containing the first or last n characters of the string. + +```swift +let str = "Hello, world!" +print(str.prefix(5)) // Prints "Hello" +print(str.suffix(6)) // Prints "world!" +``` + +## String methods + +Working with string is a common task in programming. +Therefore Swift provides a variety of methods to manipulate strings. +This document will cover some of the most common string methods and properties, some of these require the import of the Foundation framework. + +### [`components(separatedBy:)`][components] + +When you want to split a string into an array of substrings, you can use the `components(separatedBy:)` method. +So say you would want to get every word in a sentence, you can use the space character as the separator. + +```swift +let sentence = "Hello, world!" +let words = sentence.components(separatedBy: " ") +print(words) // Prints ["Hello,", "world!"] +``` + +### [`hasPrefix(_:)`][hasPrefix], [`hasSuffix(_:)`][hasSuffix] + +When you want to check if a string starts or ends with a certain substring, you can use the `hasPrefix(_:)` and `hasSuffix(_:)` methods. +Both of these methods take a string as an argument and return a boolean value indicating whether the string starts or ends with the specified substring. + +```swift +let greeting = "Hello, world!" +print(greeting.hasPrefix("Hello")) // Prints true +print(greeting.hasSuffix("world!")) // Prints true +``` + +### [`lowercased`][lowercased], [`uppercased`][uppercased], [`capitalized`][capitalized] + +When you want to change the case of a string, you can use the `lowercased`, `uppercased`, and `capitalized` properties. +The `lowercased` property returns a new string with all characters in lowercase, the `uppercased` property returns a new string with all characters in uppercase, and the `capitalized` property returns a new string with the first character of each word capitalized. + +```swift +let greeting = "hello, world!" +print(greeting.lowercased) // Prints "hello, world!" +print(greeting.uppercased) // Prints "HELLO, WORLD!" +print(greeting.capitalized) // Prints "Hello, World!" +``` + +### [`replacingOccurrences(of:with:)`][replacingOccurrences] + +When you want to replace all occurrences of a substring in a string with another substring, you can use the `replacingOccurrences(of:with:)` method. +This method takes two strings as arguments: the substring to be replaced and the substring to replace it with. It returns a new string with all occurrences of the specified substring replaced. + +```swift +let greeting = "Hello, world!" +let newGreeting = greeting.replacingOccurrences(of: "world", with: "Swift") +print(newGreeting) // Prints "Hello, Swift!" +``` + +### [`joined(separator:)`][joined] + +This isn't a method of the String type, but rather of the Array type. +When you want to join an array of strings into a single string with a specified separator, you can use the `joined(separator:)` method. +This method takes a string as an argument and returns a new string with all elements of the array joined together, separated by the specified string. + +```swift +let words = ["Hello", "world"] +let sentence = words.joined(separator: ", ") +print(sentence) // Prints "Hello, world" +``` -Note, however, that if the offset is not a valid index, i.e. if it would return the index before `startIndex` or after `endIndex` the operation will raise an error, crashing the program. To prevent this problem, one can specify a limiting index. This returns an optional index and it will return `nil` for otherwise invalid indices. +[components]: https://developer.apple.com/documentation/foundation/nsstring/components(separatedby:)-238fy +[hasPrefix]: https://developer.apple.com/documentation/foundation/nsstring/hasprefix(_:) +[hasSuffix]: https://developer.apple.com/documentation/foundation/nsstring/hassuffix(_:) +[lowercased]: https://developer.apple.com/documentation/foundation/nsstring/lowercased +[uppercased]: https://developer.apple.com/documentation/foundation/nsstring/uppercased +[capitalized]: https://developer.apple.com/documentation/foundation/nsstring/capitalized +[replacingOccurrences]: https://developer.apple.com/documentation/foundation/nsstring/replacingoccurrences(of:with:) +[joined]: https://developer.apple.com/documentation/swift/array/joined(separator:)-7uber +[string-docs]: https://developer.apple.com/documentation/swift/String +[nsstring-docs]: https://developer.apple.com/documentation/foundation/nsstring +[string-format-specifiers]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html +[string-indices]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-Indices +[unicode]: https://en.wikipedia.org/wiki/Unicode +[argument-parser]: https://apple.github.io/swift-argument-parser/documentation/argumentparser/ +[network-framework]: https://developer.apple.com/documentation/network +[swifty-beaver]: https://github.com/SwiftyBeaver/SwiftyBeaver +[apple-foundation]: https://developer.apple.com/documentation/foundation diff --git a/exercises/concept/poetry-club/.meta/Sources/PoetryClub/PoetryClubExemplar.swift b/exercises/concept/poetry-club/.meta/Sources/PoetryClub/PoetryClubExemplar.swift index af8f65a25..daacd6f9a 100644 --- a/exercises/concept/poetry-club/.meta/Sources/PoetryClub/PoetryClubExemplar.swift +++ b/exercises/concept/poetry-club/.meta/Sources/PoetryClub/PoetryClubExemplar.swift @@ -4,32 +4,34 @@ func splitOnNewlines(_ poem: String) -> [String] { return poem.components(separatedBy: "\n") } -func firstLetter(_ line: String) -> Character { - return line.first ?? "_" -} - -func capitalize(_ phrase: String) -> String { - return phrase.capitalized -} - -func trimSentence(_ line: String) -> String { - return line.trimmingCharacters(in: .whitespaces) -} - -func lastLetter(_ line: String) -> Character { - return line.last ?? "_" +func frontDoorPassword(_ phrase: String) -> String { + let letters = splitOnNewlines(phrase) + var result = "" + for letter in letters { + let character = letter.first ?? "_" + result += String(character) + } + return result } func backDoorPassword(_ phrase: String) -> String { - return "\(phrase.capitalized), please" -} - -func ithLetter(_ line: String, i: Int) -> Character { - guard let idx = line.index(line.startIndex, offsetBy: i, limitedBy: line.endIndex) - else { return " " } - return line[idx] + let letters = splitOnNewlines(phrase) + var result = "" + for letter in letters { + let clearedMessage = letter.trimmingCharacters(in: .whitespaces) + let character = clearedMessage.last ?? "_" + result += String(character) + } + return "\(result), please" } func secretRoomPassword(_ phrase: String) -> String { - return phrase.uppercased() + "!" + let letters = splitOnNewlines(phrase) + var result = "" + for i in 0.. [String] { - fatalError("splitOnNewlines not implemented -- replace this error with an implementation") + fatalError("Please implement the splitOnNewlines(_:) function") } -func firstLetter(_ line: String) -> Character { - fatalError("firstLetter not implemented -- replace this error with an implementation") -} - -func capitalize(_ phrase: String) -> String { - fatalError("capitalize not implemented -- replace this error with an implementation") -} - -func trimSentence(_ line: String) -> String { - fatalError("trimSentence not implemented -- replace this error with an implementation") -} - -func lastLetter(_ line: String) -> Character { - fatalError("lastLetter not implemented -- replace this error with an implementation") +func frontDoorPassword(_ phrase: String) -> String { + fatalError("Please implement the frontDoorPassword(_:) function") } func backDoorPassword(_ phrase: String) -> String { - fatalError("backDoorPassword not implemented -- replace this error with an implementation") -} - -func ithLetter(_ line: String, i: Int) -> Character { - fatalError("ithLetter not implemented -- replace this error with an implementation") + fatalError("Please implement the backDoorPassword(_:) function") } func secretRoomPassword(_ phrase: String) -> String { - fatalError("secretRoomPassword not implemented -- replace this error with an implementation") + fatalError("Please implement the secretRoomPassword(_:) function") } diff --git a/exercises/concept/poetry-club/Tests/PoetryClubTests/PoetryClubTests.swift b/exercises/concept/poetry-club/Tests/PoetryClubTests/PoetryClubTests.swift index 433168cdb..d30c4eedf 100644 --- a/exercises/concept/poetry-club/Tests/PoetryClubTests/PoetryClubTests.swift +++ b/exercises/concept/poetry-club/Tests/PoetryClubTests/PoetryClubTests.swift @@ -1,82 +1,108 @@ -import XCTest +import Testing +import Foundation @testable import PoetryClub -final class PoetryClubTests: XCTestCase { - let runAll = - Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) - ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false - func testSplitNewlines() { - XCTAssertEqual( - splitOnNewlines("Winken.\nBlinken\n\nAnd Nod."), +@Suite struct PoetryClubTests { + @Test("splitOnNewlines") + func testSplitOnNewlines() { + #expect( + splitOnNewlines("Winken.\nBlinken\n\nAnd Nod.") == ["Winken.", "Blinken", "", "And Nod."] ) } - func testSplitNoNewlines() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(splitOnNewlines("Hello."), ["Hello."]) + @Test("splitOnNewlines with only one Line", .enabled(if: RUNALL)) + func testSplitNoNewlines() { + #expect( + splitOnNewlines("Hello.") == ["Hello."] + ) } - func testFirstLetter() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(firstLetter("Lorem ipsum"), "L") + @Test("splitOnNewlines with empty string", .enabled(if: RUNALL)) + func testSplitEmptyString() { + #expect( + splitOnNewlines("") == [""] + ) } - func testFirstLetterEmpty() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(firstLetter(""), "_") + @Test("frontDoorPassword") + func testFrontDoorPassword() { + #expect( + frontDoorPassword("Winken.\nBlinken\nAnd Nod.") == "WBA" + ) } - func testCapitalize() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(capitalize("HORSES for CoUrSeS!"), "Horses For Courses!") + @Test("frontDoorPassword with another string", .enabled(if: RUNALL)) + func testFrontDoorPasswordAnotherString() { + #expect( + frontDoorPassword("Hello.\nWorld") == "HW" + ) } - func testTrimWithWhitespace() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual( - trimSentence("Is all the whitespace gone? \t \t"), - "Is all the whitespace gone?" + @Test("frontDoorPassword with only one Line", .enabled(if: RUNALL)) + func testFrontDoorPasswordNoNewlines() { + #expect( + frontDoorPassword("Hello.") == "H" ) } - func testTrimWithoutWhitespace() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual( - trimSentence("Is all the whitespace gone?"), - "Is all the whitespace gone?" + @Test("frontDoorPassword with empty string", .enabled(if: RUNALL)) + func testFrontDoorPasswordEmptyString() { + #expect( + frontDoorPassword("") == "_" ) } - func testLastLetter() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(lastLetter("Lorem ipsum"), "m") + @Test("backDoorPassword", .enabled(if: RUNALL)) + func testBackDoorPassword() { + #expect( + backDoorPassword("Allow\nForumla\nIn\nAcid") + == "wand, please" + ) } - func testLastLetterEmpty() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(lastLetter(""), "_") + @Test("backDoorPassword trailing whitespace", .enabled(if: RUNALL)) + func testBackDoorPasswordAnotherString() { + #expect( + backDoorPassword("Hello \nWorld ") == "od, please" + ) } - func testBackdoorPassword() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(backDoorPassword("scoobyDOO!"), "Scoobydoo!, please") + @Test("backDoorPassword with only one Line", .enabled(if: RUNALL)) + func testBackDoorPasswordNoNewlines() { + #expect( + backDoorPassword("Hello ") == "o, please" + ) } - func testIthLetter() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(ithLetter("Inquisitive", i: 2), "q") + @Test("backDoorPassword with only last line having trailing space", .enabled(if: RUNALL)) + func testBackDoorPasswordLastLineTrailingSpace() { + #expect( + backDoorPassword("Hello\nWorld ") == "od, please" + ) + } + + @Test("secretRoomPassword", .enabled(if: RUNALL)) + func testSecretRoomPassword() { + #expect( + secretRoomPassword("Winken.\nBlinken\nWhisper") == "WLI!" + ) } - func testIthLetterInvalid() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(ithLetter("Inquisitive", i: 100), " ") + @Test("secretRoomPassword with another string", .enabled(if: RUNALL)) + func testSecretRoomPasswordAnotherString() { + #expect( + secretRoomPassword("Hello\nWorld\nHappy\nHere") == "HOPE!" + ) } - func testSecretRoomPassword() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(secretRoomPassword("Open Sesame"), "OPEN SESAME!") + @Test("secretRoomPassword with only one Line", .enabled(if: RUNALL)) + func testsecretRoomPasswordWithOnlyOneLine() { + #expect( + secretRoomPassword("Hello") == "H!" + ) } }