Swift는 표준 라이브러리에서 풍부한 기능을 제공하지만, 외부 라이브러리와 프레임워크를 가져오면 훨씬 다양한 기능을 사용할 수 있어요. 이런 모듈은 Swift 프로젝트에서 직접 제공되기도 하고(Swift Argument Parser처럼), Apple 플랫폼 프레임워크에서 오기도 하며(Foundation과 Network Framework처럼), 서드파티 오픈 소스 라이브러리에서 올 수도 있어요(SwiftyBeaver처럼).
Foundation처럼 Swift에서 가장 널리 쓰이는 라이브러리 중 하나인 핵심 모듈은 Swift 툴체인에 함께 포함되어 있어요. 서드파티 라이브러리는 프로젝트 설정에 추가해야 가져올 수 있어요 (예를 들어 Swift Package Manager를 사용해서요).
모듈을 가져올 때는 import 키워드 뒤에 모듈 이름을 써요.
예를 들어 Foundation은 프로그램에 다음을 추가해서 가져올 수 있어요.
import Foundation
모듈을 가져오면 그 모듈의 타입과 메서드, 함수를 코드에서 사용할 수 있어요.
예를 들어 Foundation을 가져오면 String에 components(separatedBy:) 같은 유용한 유틸리티 메서드가 추가돼요.
관례적으로 import 문은 Swift 파일 맨 위에 두어서, 코드를 읽는 사람이 의존성을 바로 확인할 수 있게 해요.
String의 개별 문자는 배열에서 쓰는 것과 같은 서브스크립트 방식으로 접근할 수 있어요.
하지만 문자열에 쓰이는 인덱스는 정수가 아니어서 정수처럼 직접 다룰 수 없어요.
대신 비어 있지 않은 문자열에서 첫 번째 문자의 위치를 가리키는 startIndex처럼 이미 알고 있는 다른 인덱스를 기준으로, String과 NSString 라이브러리에서 제공하는 메서드를 사용해 계산해야 해요 (Foundation 모듈을 가져오면 Swift의 문자열에서 모든 NSString 속성과 메서드에 접근할 수 있어요).
예를 들어 다음 문자열이 있다고 해봐요.
let csv = "apple,pear,peach,orange,cherry,lime,goosberry"
"orange"의 "g"를 얻으려고 csv[21]이라고 쓸 수는 없어요. 대신 String.Index 타입의 값을 계산해서 그 인덱스를 넘겨야 해요.
이 인덱스들은 그 자체로 사람이 읽고 쓰도록 만든 값이 아니에요.
사람이 그 안에 무엇이 들어 있는지 알 필요가 없는, 이른바 _불투명 인덱스_예요.
let index = csv.index(csv.startIndex, offsetBy: 21)
csv[index]
// "g"
print(index)
// prints Index(_rawBits: 1376513)
그런데 오프셋이 유효한 인덱스가 아니면, 그러니까 startIndex보다 앞이거나 endIndex보다 뒤의 인덱스를 가리키면 오류가 발생해서 프로그램이 중단돼요.
이 문제를 막으려면 제한 인덱스를 지정할 수 있어요.
그러면 옵셔널 인덱스가 반환되고, 유효하지 않은 인덱스에는 nil이 반환돼요.
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
또한 인덱스는 서로 다른 문자열끼리 공유할 수 없어요.
예를 들어 위에서 계산한 "orange"의 "g"에 대한 index로 fam = "This is my family: 👨👩👦👦, this is our dog: 🐶, this is our cat: 🐱!" 문자열을 인덱싱하면, 즉 fam[index]라고 쓰면 프로그램이 중단돼요.
이런 것에는 여러 이유가 있지만, 기본적으로는 모두 "유니코드는 까다롭다"로 귀결돼요.
일반적으로 개별 문자에 임의 접근이 필요하다면 Array<Character> 같은 다른 자료 구조를 쓰는 게 좋아요.
Swift에는 문자열의 첫 문자와 마지막 문자를 가져오는 first와 last 메서드가 있어요.
이 메서드들은 옵셔널 문자를 반환해서, 문자열이 비어 있으면 nil을 반환해요.
let str = "Hello, world!"
print(str.first) // Prints Optional("H")
print(str.last) // Prints Optional("!")
prefix와 suffix 메서드로 문자열의 일부를 얻을 수도 있어요.
이 메서드들은 정수를 인자로 받아서 문자열의 앞이나 뒤 n개 문자를 담은 부분 문자열을 반환해요.
let str = "Hello, world!"
print(str.prefix(5)) // Prints "Hello"
print(str.suffix(6)) // Prints "world!"
텍스트를 다루는 일은 프로그래밍에서 흔한 작업이에요. Swift는 Foundation 프레임워크와 함께 문자열을 살펴보고, 변환하고, 조작하는 데 편리한 메서드와 속성을 많이 제공해요.
components(separatedBy:)로 문자열 나누기구분자를 기준으로 문자열을 부분 문자열의 배열로 나누려면 components(separatedBy:)를 사용해요 (Foundation을 가져와야 해요):
import Foundation
let sentence = "Hello, world!"
let words = sentence.components(separatedBy: " ")
print(words) // Prints ["Hello,", "world!"]
hasPrefix(_:)와 hasSuffix(_:)로 접두사와 접미사 확인하기문자열이 특정 문자 시퀀스로 시작하거나 끝나는지 확인하려면 hasPrefix(_:)와 hasSuffix(_:)를 사용해요:
let greeting = "Hello, world!"
print(greeting.hasPrefix("Hello")) // Prints true
print(greeting.hasSuffix("world!")) // Prints true
다음 표준 메서드와 속성으로 문자열의 대소문자를 바꿀 수 있어요:
lowercased(): 문자열의 소문자 복사본을 반환해요.uppercased(): 문자열의 대문자 복사본을 반환해요.capitalized: 각 단어의 첫 글자를 대문자로 바꾼 복사본을 반환해요 (Foundation에서 제공해요).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(of:with:)를 사용해요 (Foundation에서 제공해요):
let greeting = "Hello, world!"
let updated = greeting.replacingOccurrences(of: "world", with: "Swift")
print(updated) // Prints "Hello, Swift!"
joined(separator:)로 문자열 합치기joined(separator:) 메서드는 [String] 같은 문자열 컬렉션에 속해요.
구분자를 사용해서 원소들을 하나의 문자열로 합쳐요:
let words = ["Hello", "world"]
let sentence = words.joined(separator: ", ")
print(sentence) // Prints "Hello, world"
동네에 새로운 시 동아리가 문을 열었고, 가 볼까 생각 중이에요. 예전에 몇 번 사건이 있었던 터라, 이 동아리에는 아주 특별한 출입 규칙이 있어서 들어가기 전에 먼저 익혀 두어야 해요.
시 동아리에는 문이 두 개 있는데, 둘 다 문지기가 지키고 있어요. 들어가려면 그날의 비밀번호를 알아내야 해요.
가장 먼저 할 일은 시를 개별 줄로 나누는 거예요. 문지기가 시를 읊어 주면, 그 시를 줄 단위로 나눠야 해요.
문자열을 받아서 줄 바꿈 문자를 구분자로 사용해 문자열의 배열로 나누는 splitOnNewlines 함수를 구현해요.
이 함수는 문자열의 배열을 반환해야 하는데, 각 문자열은 시의 한 줄이에요.
splitOnNewlines("Hello.\nHow are you?\n\nI'm doing fine.")
// returns ["Hello." ,"How are you?", "", "I'm doing fine"].
_를 사용해요.예를 들어, 그들이 가장 좋아하는 작가 중 한 명인 Michael Lockwood는 다음과 같은 아크로스틱 시를 썼는데, 아크로스틱이란 각 문장의 첫 글자를 모으면 단어가 되는 형식을 말해요:
Stands so high
Huge hooves too
Impatiently waits for
Reins and harness
Eager to leave
문지기가 시를 읊으면, 시를 개별 줄로 나누고 각 줄의 첫 글자로 답해요. 즉, ["S", "H", "I", "R", "E"]예요.
그다음 문지기는 답한 글자 배열로 만들어진 단어를 주는데, 그 단어를 첫 글자만 대문자로 바꾼 형태로 만들어야 해요.
마지막으로 반환하는 비밀번호가 "SHIRE"면 들어갈 수 있어요.
시를 나타내는 문자열을 받는 frontDoorPassword 함수를 구현해요.
이 함수는 문지기에게 말할 비밀번호를 반환해야 해요.
frontDoorPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nReins and harness\nEager to leave")
// returns "SHIRE"
동아리 안쪽에는 가장 유명한 시인들이 있는데, 꼭 VIP 구역 같아요. 아무나 갈 수 있는 곳이 아니라서, 뒷문 절차는 조금 더 복잡해요.
", please"를 붙여야 해요.예를 들어, 앞에서 나온 시는 _텔레스틱_이기도 한데, 텔레스틱이란 각 문장의 마지막 글자를 모으면 단어가 되는 형식을 말해요:
Stands so high
Huge hooves too
Impatiently waits for
Reins and harness
Eager to leave
문지기가 시를 읊으면, 시를 개별 줄로 나누고 줄 끝의 공백을 없앤 다음, 각 줄의 마지막 글자로 답해요. 즉, ["h", "o", "r", "s", "e"]예요.
그다음 문지기는 답한 글자 배열로 만들어진 단어를 주는데, 그 단어를 첫 글자만 대문자로 바꾸고 ", please"를 붙여야 해요.
마지막으로 반환하는 비밀번호가 "horse, please"면 들어갈 수 있어요.
시를 나타내는 문자열을 받는 backDoorPassword 함수를 구현해요.
이 함수는 문지기에게 말할 비밀번호를 반환해야 해요.
backDoorPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nReins and harness\nEager to leave")
// returns "horse, please"
동아리 안쪽 방에는 또 다른 문이 있는데, 이 문은 최고의 시인들만 들어갈 수 있는 비밀의 방으로 이어져요.
"!"를 붙여야 해요.예를 들어, 앞에서 나온 시를 살짝 바꾼 버전이 이 패턴에 맞아요:
Stands so high
Huge hooves too
Impatiently waits for
Rider with harness
Eager to leave
문지기가 시를 읊으면, 시를 개별 줄로 나누고 줄 끝의 공백을 없앤 다음, 각 줄의 i번째 글자로 답해요. 즉, ["S", "u", "p", "e", "r"]예요.
그다음 문지기는 답한 글자 배열로 만들어진 단어를 주는데, 그 단어를 모두 대문자로 바꾸고 "!"를 붙여야 해요.
마지막으로 반환하는 비밀번호가 SUPER!면 들어갈 수 있어요.
시를 나타내는 문자열을 받는 secretRoomPassword 함수를 구현해요.
이 함수는 문지기에게 말할 비밀번호를 반환해야 해요.
secretRoomPassword("Stands so high\nHuge hooves too\nImpatiently waits for\nRider with harness\nEager to leave")
// returns "SUPER!"
Exercism에 가입하고 Swift 트랙을 개념 35개연습 문제 116개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.