스위프트 (프로그래밍 언어)

스위프트()는 2010년 크리스 라트너가 애플을 위해 만들고 오픈 소스 커뮤니티에서 유지 관리하는 고급 범용, 다중 패러다임, 컴파일 프로그래밍 언어다. 스위프트는 기계어로 컴파일되며 LLVM 기반 컴파일러를 사용한다. 스위프트는 2014년 6월에 처음 출시되었으며 스위프트 툴체인은 2014년 9월 출시된 6 버전부터 엑스코드에 포함되어 제공되고 있다.

애플은 스위프트가 오브젝티브-C와 관련된 많은 핵심 개념, 특히 동적 디스패치, 광범위한 지연 바인딩, 확장 가능한 프로그래밍 및 유사한 기능들을 지원하되, 소프트웨어 버그를 더 쉽게 잡을 수 있는 "더 안전한" 방식으로 지원하도록 의도했다. 스위프트는 널 포인터 역참조와 같은 몇 가지 일반적인 프로그래밍 오류를 해결하는 기능을 갖추고 있으며, 신택틱 슈거를 제공하여 피라미드 오브 둠을 피하도록 돕는다. 스위프트는 타입, 구조체 및 클래스에 적용할 수 있는 확장 시스템인 프로토콜 확장성 개념을 지원하는데, 애플은 이를 "프로토콜 지향 프로그래밍"이라 부르며 프로그래밍 패러다임의 실질적인 변화로 홍보하고 있다 (이는 트레이트 및 타입 클래스와 유사하다).

스위프트는 애플의 2014년 세계 개발자 회의(WWDC)에서 소개되었다. 2014년 동안 1.2 버전으로 업그레이드되었고, WWDC 2015에서 스위프트 2로 메이저 업그레이드가 이루어졌다. 초기에는 폐쇄적인 언어였으나, 2015년 12월 3일 애플 플랫폼과 리눅스를 대상으로 아파치 라이선스 2.0 하에 2.2 버전이 오픈 소스 소프트웨어로 공개되었다.

 역사 

스위프트의 개발은 2010년 7월 크리스 라트너에 의해 시작되었으며, 이후 애플의 다른 많은 프로그래머들의 협력을 통해 진행되었다. 스위프트는 1980년대 초반 이후 거의 변하지 않았고 현대적인 언어 기능이 부족했던 애플의 이전 프로그래밍 언어인 오브젝티브-C를 대체할 필요성에 의해 추진되었다. 스위프트는 "오브젝티브-C, 러스트, 하스켈, 루비, 파이썬, C 샤프, CLU 및 나열하기에 너무 많은 다른 언어들"로부터 언어적 아이디어를 가져왔다. 2014년 6월 2일, 애플 세계 개발자 회의(WWDC) 애플리케이션은 스위프트로 작성되어 공개적으로 출시된 첫 번째 앱이 되었다. 이 회의에서 등록된 애플 개발자들에게 베타 버전의 프로그래밍 언어가 공개되었으나, 회사는 스위프트의 최종 버전이 테스트 버전과 소스 코드 호환성을 가질 것이라고 약속하지는 않았다. 애플은 정식 출시 시 필요한 경우 소스 코드 변환기를 제공할 계획을 세웠다.

WWDC에서는 500페이지 분량의 무료 매뉴얼인 'The Swift Programming Language'도 공개되었으며, 애플 북스 스토어와 공식 웹사이트에서 이용 가능하다.

스위프트는 2014년 9월 9일 iOS용 엑스코드 6.0의 골드 마스터와 함께 1.0 이정표에 도달했다. 스위프트 1.1은 2014년 10월 22일 엑스코드 6.1 출시와 함께 발표되었다. 스위프트 1.2는 2015년 4월 8일 엑스코드 6.3과 함께 출시되었다. 스위프트 2.0은 WWDC 2015에서 발표되었으며, 2015년 9월 21일부터 앱 스토어에 앱을 게시할 수 있게 되었다.

스위프트 3.0은 2016년 9월 13일에 출시되었다. 3.0 버전까지 스위프트의 구문은 상당한 진화를 거쳤으며, 핵심 팀은 이후 버전에서 소스 안정성에 중점을 두었다.

2017년 9월 19일에 출시된 스위프트 4.0은 일부 내장 클래스 및 구조체에 몇 가지 변경 사항을 도입했다. 이전 버전의 스위프트로 작성된 코드는 엑스코드에 내장된 마이그레이션 기능을 사용하여 업데이트할 수 있다. 스위프트 4.1은 2018년 3월 29일에 출시되었다. 2018년 1분기에 스위프트는 측정된 인기 면에서 오브젝티브-C를 추월했다.

2019년 3월에 출시된 스위프트 5는 애플 플랫폼에서 안정적인 바이너리 인터페이스를 도입하여 스위프트 런타임이 애플 운영 체제에 통합될 수 있도록 했다. 이는 스위프트 4와 소스 호환이 가능하다.

스위프트 5.1은 2019년 9월에 공식 출시되었다. 스위프트 5.1은 모듈 안정성 도입을 통해 언어의 안정적인 기능을 컴파일 타임까지 확장함으로써 이전의 스위프트 5 버전을 기반으로 구축되었다. 모듈 안정성 도입으로 향후 스위프트 릴리스와 호환되는 바이너리 프레임워크를 만들고 공유할 수 있게 되었다. 애플이 2021년 WWDC에서 공식 발표한 스위프트 5.5는 병행성 및 비동기 코드에 대한 언어 지원을 크게 확장했으며, 특히 독특한 버전의 행위자 모델을 도입했다. 스위프트 5.9는 2023년 9월에 출시되었으며 매크로 시스템, 제네릭 파라미터 팩, 그리고 새로운 consume 연산자와 같은 소유권 기능을 포함한다. 2024년 3월에 출시된 스위프트 5.10은 언어의 병행성 모델을 개선하여 데이터 경쟁을 방지하기 위한 완전한 데이터 격리를 가능하게 했다.

스위프트 6은 2024년 9월에 출시되었다. 스위프트 6.1은 2025년 3월에 출시되었다. 여기에는 "생산성을 향상시키기 위한 새로운 언어 강화, 진단 개선, 패키지 트레이트, 그리고 데이터 경쟁 안전성 사용성 및 컴파일 시간 개선을 위한 지속적인 작업"이 포함되어 있다.

스위프트는 스택 오버플로 개발자 설문 조사 2015에서 가장 사랑받는 프로그래밍 언어 1위를 차지했으며 2016년에는 2위를 차지했다.

2015년 12월 3일, 스위프트 언어, 지원 라이브러리, 디버거 및 패키지 관리자가 런타임 라이브러리 예외 사항을 포함한 아파치 2.0 라이선스 하에 오픈 소스로 공개되었으며, 프로젝트를 호스팅하기 위해 Swift.org가 만들어졌다. 소스 코드는 깃허브에 호스팅되어 누구나 쉽게 코드를 얻고 직접 빌드할 수 있으며, 프로젝트에 코드를 다시 기여하기 위한 풀 리퀘스트를 생성할 수도 있다.

2015년 12월, IBM은 개발자가 한 창에서 스위프트 코드를 작성하고 다른 창에서 출력을 표시할 수 있는 스위프트 샌드박스 웹사이트를 발표했다. 스위프트 샌드박스는 2018년 1월에 중단되었다.

WWDC 2016 기간 동안 애플은 사람들에게 스위프트 코딩 방법을 가르치기 위한 아이패드 전용 모바일 앱, 스위프트 플레이그라운드(Swift Playgrounds)를 발표했다. 이 앱은 3D 비디오 게임과 유사한 인터페이스로 제공되며 코드 라인이 특정 순서로 배치되어 실행될 때 피드백을 제공한다.

2017년 1월, 크리스 라트너는 테슬라로 이직하기 위해 애플을 떠난다고 발표했으며, 스위프트 프로젝트 리더 역할은 팀의 베테랑인 테드 크레메넥이 맡게 되었다.

WWDC 2019 기간 동안 애플은 모든 애플 플랫폼에서 선언형 UI 구조 설계를 위한 프레임워크를 제공하는 엑스코드 11과 함께 SwiftUI를 발표했다.

리눅스 우분투 배포판을 위한 SDK 및 툴체인의 공식 다운로드는 스위프트 2.2부터 가능했으며, 스위프트 5.2.4부터 CentOS 및 아마존 리눅스를 포함한 더 많은 배포판이 추가되었다. 안드로이드를 위한 비공식 SDK 및 네이티브 툴체인 패키지도 존재하며, 2025년 10월 스위프트 안드로이드 작업 그룹은 안드로이드용 공식 스위프트 SDK의 프리뷰 릴리스를 발표했다.

 플랫폼 
스위프트가 지원하는 플랫폼은 애플의 운영 체제(다윈, iOS, iPadOS, macOS, tvOS, watchOS), 리눅스, 마이크로소프트 윈도우, 웹어셈블리, 그리고 안드로이드다.

스위프트 설계의 핵심적인 측면은 코코아 및 코코아 터치 프레임워크와 같이 지난 수십 년 동안 애플 제품을 위해 개발된 방대한 기존 오브젝티브-C 코드와 상호 운용할 수 있는 능력이다. 애플 플랫폼에서 스위프트는 오브젝티브-C 런타임 라이브러리와 연결되어 C, 오브젝티브-C, C++ 및 스위프트 코드가 하나의 프로그램 내에서 실행될 수 있도록 한다.

 버전 역사 

 특징 

스위프트는 현대적인 프로그래밍 언어 이론 개념을 채택하고 단순하면서도 강력한 구문을 제시하기 위해 노력하는 범용 프로그래밍 언어다. 스위프트는 다양한 프로그래밍 언어의 혁신과 관행을 통합했으며, 특히 애플 플랫폼의 주요 개발 언어로서 대체하게 된 오브젝티브-C로부터 많은 영감을 받았다.

스위프트는 속도를 희생하지 않으면서도 새로운 프로그래머들에게 안전하고 친숙하도록 설계되었다. 기본적으로 스위프트는 모든 메모리를 자동으로 관리하며 변수가 사용되기 전에 항상 초기화되도록 보장한다. 배열 액세스는 경계 외 오류를 검사하고 정수 연산은 오버플로를 검사한다. 파라미터 이름은 명확한 API를 만들 수 있게 해준다. 프로토콜은 타입이 채택할 수 있는 인터페이스를 정의하며, 확장은 개발자가 기존 타입에 더 많은 기능을 추가할 수 있게 한다. 스위프트는 클래스, 서브타이핑, 메소드 오버라이딩 지원을 통해 객체 지향 프로그래밍을 가능하게 한다. 옵셔널은 nil 값을 명시적이고 안전하게 처리할 수 있도록 한다. 병행 프로그램은 async/await 구문을 사용하여 작성할 수 있으며, 액터는 데이터 경쟁을 제거하기 위해 공유된 가변 상태를 격리한다.

 기본 구문 
스위프트의 구문은 C 스타일 언어와 유사하다. 코드는 기본적으로 전역 범위에서 실행을 시작한다. 대안적으로 속성을 구조체, 클래스 또는 열거형 선언에 적용하여 해당 선언에 프로그램의 진입점이 포함되어 있음을 나타낼 수 있다.

스위프트의 "Hello, World!" 프로그램은 다음과 같다.
print("Hello, world!")여기에 사용된 함수는 외부 모듈을 가져올 필요 없이 모든 프로그램에서 사용할 수 있는 스위프트 표준 라이브러리에 포함되어 있다. 스위프트의 문장은 세미콜론으로 끝날 필요가 없으나, 한 줄에 여러 문장을 작성할 때는 세미콜론으로 구분해야 한다. 단일 행 주석은 로 시작하여 현재 행의 끝까지 이어진다. 다행 주석은 와 문자로 둘러싸인다.

상수(변수 중 수정할 수 없는 것)는 키워드로 선언하며, 키워드를 사용하여 선언된 변수는 변경될 수 있다. 값은 읽기 전에 초기화되어야 한다. 변수는 제공된 초기 값의 타입을 기반으로 타입을 추론할 수 있다. 초기 값이 변수 선언 이후에 설정되는 경우 타입을 명시적으로 선언해야 한다.let highScoreThreshold = 1000 // Int 타입의 상수. 제공된 값을 바탕으로 타입이 추론됨.

var currentScore = 980 // Int 타입의 변수.
currentScore = 1200 // 변수의 값은 시간이 지나면서 변할 수 있음.

let playerMessage: String // 명시적 String 타입을 가진 상수.
if currentScore > highScoreThreshold {
 playerMessage = "You are a top player!"
} else {
 playerMessage = "Better luck next time."
}

print(playerMessage) // "You are a top player!"를 출력함.스위프트의 제어 흐름은 if-else, guard, switch 문과 함께 while 및 for-in 루프를 사용하여 관리된다.

 문은 불리언 파라미터를 받아 조건이 참이면 문의 본문을 실행하고, 그렇지 않으면 선택적인 본문을 실행한다. 구문은 옵셔널 값의 존재 여부를 확인하고 동시에 압축을 푸는(unwrapping) 과정을 위한 신택틱 슈거를 제공한다.let someNumber = 42
if someNumber % 2 == 0 { // 나머지 연산자를 사용하여 someNumber를 2로 나눈 나머지를 구함.
 print("\(someNumber) is even.")
} else {
 print("\(someNumber) is odd.")
}

// "42 is even."을 출력함.함수는 키워드로 정의된다. 함수 파라미터는 함수 호출이 문구처럼 읽힐 수 있도록 하는 이름을 가질 수 있다. 파라미터 이름 앞의 언더바는 호출 시 인자 레이블을 생략할 수 있게 해준다. 튜플은 함수가 한 번에 여러 데이터를 반환하는 데 사용될 수 있다.func constructGreeting(for name: String) -> String {
 return "Hello \(name)!"
}

let greeting = constructGreeting(for: "Craig")

print(greeting) // "Hello Craig!"를 출력함.함수와 클로저라고 알려진 익명 함수는 다른 값들처럼 속성에 할당되거나 프로그램 전체에 전달될 수 있다.func divideByTwo(_ aNum: Int) -> Int {
 return aNum / 2
}

func multiplyByTwo(_ aNum: Int) -> Int {
 return aNum * 2
}

let mathOperation = multiplyByTwo

print(mathOperation(21)) // "42"를 출력함. 문은 문을 지나치기 전에 주어진 조건이 참일 것을 요구하며, 그렇지 않으면 제공된 절의 본문이 실행된다. 절은 문이 나타나는 코드 블록의 제어를 종료해야 한다. 문은 프로그램 실행을 계속하기 전에 특정 요구 사항이 충족되었는지 확인하는 데 유용하다. 특히 감싸고 있는 범위의 나머지 부분에서 nil이 아님이 보장되는 옵셔널 값의 언랩된 버전을 만드는 데 사용될 수 있다.func divide(numerator: Int?, byDenominator denominator: Int) -> Int? {
 guard denominator != 0 else {
 print("Can't divide by 0.")
 return nil
 }

 guard let numerator else {
 print("The provided numerator is nil.")
 return nil
 }

 return numerator / denominator
}

let result = divide(numerator: 3, byDenominator: 0)
print("Division result is: \(result)")

// 출력 결과:
// "Can't divide by 0."
// "Division result is: nil."switch 문은 값을 여러 잠재적 값과 비교한 다음 연관된 코드 블록을 실행한다. switch 문은 모든 가능한 값에 대한 케이스를 포함하거나, 제공된 값이 다른 케이스와 일치하지 않을 때 실행되는 default 케이스를 포함함으로써 철저해야(exhaustive) 한다. switch 케이스는 암시적으로 아래로 떨어지지 않지만(fall through), fallthrough 키워드를 사용하여 명시적으로 그렇게 할 수 있다. 패턴 매칭은 switch 문 내부에서 다양한 방식으로 사용될 수 있다. 다음은 정수가 여러 잠재적 범위와 대조되는 예시다.
let someNumber = 42

switch someNumber {
case ..<0:
 print("\(someNumber) negative.")
case 0:
 print("\(someNumber) is 0.")
case 1...9:
 print("\(someNumber) greater than 0, but less than 10.")
default:
 print("\(someNumber) is greater than 9.")
}

// "42 is greater than 9."를 출력함.for-in 루프는 일련의 값을 반복한다.
let names = ["Will", "Anna", "Bart"]
for name in names {
 print(name)
}
// 출력 결과:
// Will
// Anna
// Bartwhile 루프는 주어진 불리언 조건이 true로 평가되는 동안 반복한다.
// 1부터 5까지의 모든 숫자를 더함.
var i = 1
var result = 0

while i <= 5 { // i가 5보다 작거나 같은 동안 본문을 실행함.
 result += i // 현재 결과에 i를 더함.
 i += 1 // i를 1만큼 증가시킴.
}

print(result) // "15"를 출력함.

 클로저 지원 
스위프트는 코드에서 전달하고 사용할 수 있는 독립적인 기능 블록인 클로저를 지원하며, 익명 함수로도 사용될 수 있다. 예시는 다음과 같다.

// 입력 및 출력 값으로 정의되는 클로저 타입은 클로저 외부에서 지정할 수 있음.
let closure1: (Int, Int) -> Int = { arg1, arg2 in
 return arg1 + arg2
}

// …또는 내부에서 지정할 수 있음.
let closure2 = { (arg1: Int, arg2: Int) -> Int in
 return arg1 + arg2
}

// 대부분의 경우, 클로저의 반환 타입은 컴파일러에 의해 자동으로 추론될 수 있음.
let closure3 = { arg1: Int, arg2: Int in
 return arg1 + arg2
}
클로저는 변수와 상수에 할당될 수 있으며, 다른 함수나 클로저에 파라미터로 전달될 수 있다. 단일 표현식 클로저는 키워드를 생략할 수 있다.

스위프트는 또한 후행 클로저(trailing closure) 구문을 가지고 있는데, 이는 클로저를 함수의 파라미터 리스트 내부가 아닌 함수 호출이 끝난 뒤에 작성할 수 있게 해준다. 클로저가 함수의 유일한 파라미터인 경우 괄호를 완전히 생략할 수 있다.
// 이 함수는 입력 파라미터가 없고 정수를 반환하는 클로저를 받아
// 이를 평가하고 클로저의 반환 값(Int)을 함수의 반환 값으로 사용함.
func foo(closure bar: () -> Int) -> Int {
 return bar()
}

// 후행 클로저 구문을 사용하지 않는 경우:
foo(closure: { return 1 })

// 후행 클로저 구문과 암시적 반환을 사용하는 경우:
foo { 1 } 

버전 5.3부터 스위프트는 다중 후행 클로저를 지원한다.

// 이 함수는 첫 번째 클로저의 반환값을 두 번째 클로저의 파라미터로 전달하고,
// 두 번째 클로저의 결과를 반환함.
func foo(bar: () -> Int, baz: (Int) -> Int) -> Int {
 return baz(bar())
}

// 후행 클로저를 사용하지 않는 경우:
foo(bar: { return 1 }, baz: { x in return x + 1 })

// 1개의 후행 클로저를 사용하는 경우:
foo(bar: { return 1 }) { x in return x + 1 }

// 2개의 후행 클로저를 사용하는 경우 (첫 번째 클로저의 인자 이름만 생략됨):
foo { return 1 } baz: { x in return x + 1 }스위프트는 인라인 클로저에 대해 약식 인자 이름을 제공하여 모든 클로저 파라미터의 이름을 명시적으로 지정할 필요를 없애준다. 인자는 $0, $1, $2 등의 이름으로 참조될 수 있다.
let names = ["Josephine", "Steve", "Chris", "Barbara"]

// filter는 names의 각 값에 대해 주어진 클로저를 호출함.
// 글자 수가 6자 미만인 값은 유지되고 나머지는 버려짐.
let shortNames = names.filter { $0.count < 6 }

print(shortNames) // "["Steve", "Chris"]"를 출력함.클로저는 주변 범위에서 값을 캡처할 수 있다. 클로저는 클로저가 존재하는 동안 이 캡처된 값을 참조한다.
func makeMultiplier(withMultiple multiple: Int) -> (Int) -> (Int) {
 // Int를 받아 입력값에 multiple을 곱한 값을 반환하는 클로저를 생성하여 반환함.
 return {
 $0 * multiple
 }
}

let multiplier = makeMultiplier(withMultiple: 3)
print(multiplier(3)) // "9"를 출력함.
print(multiplier(10)) // "30"를 출력함.

 문자열 지원 
스위프트 표준 라이브러리는 유니코드를 준수하는 및 타입을 포함한다. 문자열 값은 큰따옴표로 둘러싸인 문자 시퀀스인 문자열 리터럴로 초기화할 수 있다. 문자열은 연산자로 연결할 수 있다.

var someString = "Hello,"
someString += " world!"문자열 보간법(interpolation)을 사용하면 다른 값과 표현식으로부터 새로운 문자열을 생성할 수 있다. 뒤에 오는 괄호 사이에 작성된 값은 감싸고 있는 문자열 리터럴에 삽입된다.

var currentScore = 980
print("Your score is \(currentScore).")

// "Your score is 980."을 출력함.for-in 루프를 사용하여 문자열에 포함된 문자들을 반복할 수 있다.

for character in "Swift" {
 print(character)
}
// S
// w
// i
// f
// tFoundation 프레임워크를 가져오면 스위프트는 String 타입을 NSString(오브젝티브-C에서 일반적으로 사용되는 문자열 클래스)으로 보이지 않게 연결한다.

 호출 가능한 객체 
스위프트에서 호출 가능한 객체(Callable object)는 callAsFunction 메소드를 구현하여 함수처럼 호출할 수 있는 클래스, 구조체 또는 열거형의 인스턴스를 의미한다.

 접근 제어 
스위프트는 심볼에 대해 다섯 가지 접근 제어 레벨을 지원한다: , , , , 그리고 . 많은 객체 지향 언어와 달리, 이러한 접근 제어는 상속 계층 구조를 무시한다. 는 심볼이 즉각적인 범위 내에서만 접근 가능함을 나타내고, 는 파일 내에서만 접근 가능함을, 는 포함된 모듈 내에서 접근 가능함을, 는 모든 모듈에서 접근 가능함을 나타내며, (클래스 및 해당 메소드에만 해당)는 모듈 외부에서 클래스가 서브클래싱될 수 있음을 나타낸다.

 옵셔널과 체이닝 
스위프트의 중요한 특징은 옵션 타입으로, 이는 C에서의 일반적인 패턴과 유사한 방식으로 참조나 값이 작동하도록 허용한다. 여기서 포인터는 특정 값을 참조하거나 전혀 참조하지 않을 수 있다. 이는 비옵셔널 타입이 널 포인터 오류를 발생시킬 수 없음을 의미하며, 컴파일러는 이것이 불가능함을 보장할 수 있다.

옵셔널 타입은 Optional 열거형으로 생성된다. nullable한 정수를 만들려면 var optionalInteger: Optional
와 유사한 선언을 사용한다. C#과 마찬가지로 스위프트도 이를 위한 신택틱 슈거를 포함하고 있어 타입 이름 뒤에 물음표를 붙여 변수가 옵셔널임을 나타낼 수 있다(var optionalInteger: Int?). 옵셔널로 표시된 변수나 상수는 기본 타입의 값을 갖거나 nil이다. 옵셔널 타입은 기본 타입을 감싸며 다른 인스턴스를 생성한다. String과 String?은 근본적으로 다른 타입이며, 전자는 String 타입인 반면 후자는 어떤 String 값을 보유하고 있을 수도 있는 Optional이다.

내부의 값에 접근하려면(nil이 아니라고 가정할 때), 내부의 인스턴스를 노출시키기 위해 압축을 풀어야(unwrapped) 한다. 이는 ! 연산자로 수행된다.

let myValue = anOptionalInstance!.someMethod()

이 경우 ! 연산자는 anOptionalInstance를 언랩하여 내부의 인스턴스를 노출시키고 그에 대해 메소드 호출이 이루어지게 한다. anOptionalInstance가 nil이면 널 포인터 오류가 발생하여 프로그램이 종료된다. 이를 강제 언래핑(force unwrapping)이라고 한다. 옵셔널은 옵셔널 체이닝을 사용하여 안전하게 언랩할 수 있는데, 이는 먼저 인스턴스가 nil인지 테스트한 다음 nil이 아닌 경우에만 언랩한다.

let myValue = anOptionalInstance?.someMethod()

이 경우 런타임은 anOptionalInstance가 nil이 아닌 경우에만 someMethod를 호출하여 오류를 억제한다. 모든 옵셔널 속성 뒤에 ?를 붙여야 한다. 이러한 속성 중 하나라도 nil이면 전체 표현식은 nil로 평가된다. 체이닝이라는 용어의 기원은 여러 메소드 호출/게터가 함께 연결되는 더 일반적인 사례에서 유래했다. 예를 들어 다음과 같다.

let aTenant = aBuilding.tenantList[5]
let theirLease = aTenant.leaseDetails
let leaseStart = theirLease?.startDate

위 코드는 다음과 같이 줄일 수 있다.

let leaseStart = aBuilding.tenantList[5].leaseDetails?.startDate

스위프트에서 옵셔널을 사용하는 것은 컴파일러가 정적 디스패치를 사용할 수 있게 하는데, 이는 언래핑 동작이 런타임 디스패치 시스템에서 발생하는 것이 아니라 정의된 인스턴스(래퍼)에서 호출되기 때문이다.

 값 타입 
많은 객체 지향 언어에서 객체는 내부적으로 두 부분으로 표현된다. 객체는 힙에 배치된 데이터 블록으로 저장되는 반면, 해당 객체에 대한 이름(또는 "핸들")은 포인터로 표현된다. 객체는 포인터의 값을 복사함으로써 메소드 간에 전달되며, 이를 통해 포인터 사본을 가진 모든 사람이 힙에 있는 동일한 기본 데이터에 접근할 수 있게 된다. 반면에 정수나 부동 소수점 값과 같은 기본 타입은 직접 표현된다. 핸들이 포인터가 아닌 데이터를 포함하며, 그 데이터는 복사를 통해 메소드에 직접 전달된다. 이러한 액세스 스타일은 객체의 경우 참조에 의한 전달(pass-by-reference), 기본 타입의 경우 값에 의한 전달(pass-by-value)이라 불린다.

두 개념 모두 장단점이 있다. 객체는 창의 설명이나 문서 내용과 같이 데이터가 클 때 유용하다. 이러한 경우 데이터에 대한 액세스는 전체 데이터 구조를 복사하는 대신 32비트 또는 64비트 값을 복사함으로써 제공된다. 그러나 정수와 같은 작은 값은 포인터와 크기가 같으므로(일반적으로 둘 다 하나의 워드임), 값을 직접 전달하는 대신 포인터를 전달하는 이점이 없다.

스위프트는 class 선언을 사용하는 참조에 의한 전달 또는 struct를 사용하는 값에 의한 전달 시맨틱 중 하나를 사용하여 객체에 대한 기본 지원을 제공한다. 스위프트의 구조체는 클래스와 거의 동일한 기능(메소드, 프로토콜 구현 및 확장 메커니즘 사용)을 가지고 있다. 이러한 이유로 애플은 객체나 값 대신 모든 데이터를 통칭하여 인스턴스라고 부른다. 그러나 구조체는 상속을 지원하지 않는다.

프로그래머는 애플리케이션의 각 데이터 구조에 대해 어떤 시맨틱이 더 적절한지 자유롭게 선택할 수 있다. 창과 같은 대형 구조체는 클래스로 정의되어 포인터로 전달될 수 있다. 2D 포인트와 같은 소형 구조체는 구조체로 정의될 수 있으며, 이는 값에 의한 전달이 되어 간접 참조나 참조 횟수 계산 없이 내부 데이터에 직접 접근할 수 있게 한다. 값에 의한 전달 개념에 내재된 성능 향상은 스위프트가 Int 및 Double을 포함한 거의 모든 공통 데이터 타입과 String 및 Array와 같이 일반적으로 객체로 표현되는 타입에 이러한 타입을 사용하게 할 정도다. 값 타입을 사용하면 사용자 애플리케이션에서도 상당한 성능 향상을 이끌어낼 수 있다.

Array, Dictionary, 및 Set은 모두 쓰기 시 복사(copy on write)를 활용하므로 프로그램이 해당 값을 변경하려고 시도할 때만 데이터가 복사된다. 이는 다양한 접근자들이 사실상 동일한 데이터 저장소에 대한 포인터를 가지고 있음을 의미한다. 따라서 데이터가 물리적으로는 메모리의 한 인스턴스로 저장되지만, 애플리케이션 레벨에서는 이러한 값들이 분리되어 있으며 물리적 분리는 필요한 경우에만 쓰기 시 복사에 의해 강제된다.

 확장 
확장은 서브클래싱을 하거나 원래 소스 코드에 접근할 필요 없이 기존 타입에 새로운 기능을 추가한다. 확장은 새로운 메소드, 초기화 구문, 계산된 속성, 서브스크립트 및 프로토콜 준수성을 추가할 수 있다. 예를 들어 기본 String 타입에 맞춤법 검사기를 추가할 수 있으며, 이는 프로그램의 모든 String 인스턴스가 맞춤법 검사 능력을 갖게 됨을 의미한다. 이 시스템은 또한 관련 코드를 라이브러리와 같은 확장으로 모을 수 있게 하는 조직화 기법으로 널리 사용된다.

확장은 extension 키워드로 선언된다.
struct Rectangle {
 let width: Double
 let height: Double
}

extension Rectangle {
 var area: Double {
 return height * width
 }
}

 프로토콜 지향 프로그래밍 
프로토콜은 특정 타입이 일련의 메소드나 속성을 구현할 것임을 약속하며, 이는 시스템의 다른 인스턴스들이 해당 프로토콜을 구현하는 모든 인스턴스에 대해 해당 메소드를 호출할 수 있음을 의미한다. 이는 기능 집합이 완전히 동일하지는 않지만 현대 객체 지향 언어에서 다중 상속의 대안으로 자주 사용된다.

오브젝티브-C 및 프로토콜 개념을 구현하는 대부분의 다른 언어에서는 각 클래스에서 필수 메소드가 구현되도록 보장하는 것이 프로그래머의 몫이다. 스위프트는 확장을 사용하여 이러한 메소드를 추가하고, 이를 구현하기 위해 제네릭 프로그래밍을 사용하는 능력을 추가한다. 이를 결합하면 프로토콜을 한 번만 작성하고 광범위한 인스턴스를 지원할 수 있게 된다. 또한 확장 메커니즘을 사용하여 프로토콜을 정의에 나열하지 않은 객체에도 프로토콜 준수성을 추가할 수 있다.

예를 들어 Printable이라는 프로토콜을 선언하여 이를 준수하는 인스턴스가 description 속성과 printDetails() 메소드 요구 사항을 구현하도록 보장할 수 있다.
// Printable이라는 이름의 프로토콜 정의
protocol Printable {
 var description: String { get } // 읽기 전용 속성 요구 사항
 func printDetails() // 메소드 요구 사항
}
이 프로토콜은 이제 다른 타입에 의해 채택될 수 있다.
// 클래스에서 Printable 프로토콜 채택
class MyClass: Printable {
 var description: String {
 return "An instance of MyClass"
 }

 func printDetails() {
 print(description)
 }
}

확장은 타입에 프로토콜 준수성을 추가하는 데 사용될 수 있다. 프로토콜 자체도 확장되어 요구 사항의 기본 구현을 제공할 수 있다. 채택자는 자신만의 구현을 정의하거나 기본 구현을 사용할 수 있다.
extension Printable { // 모든 Printable 인스턴스는 이 구현을 받거나 자신만의 구현을 정의할 수 있음.
 func printDetails() {
 print(description)
 }
}

// Bool은 이제 Printable을 준수하며 위의 printDetails() 구현을 상속함.
extension Bool: Printable {
 var description: String {
 return "An instance of Bool with value: \(self)"
 }

}

인터페이스를 지원하는 많은 현대 언어와 마찬가지로 스위프트에서 프로토콜은 타입으로 사용될 수 있으며, 이는 변수와 메소드가 특정 타입 대신 프로토콜에 의해 정의될 수 있음을 의미한다.

func getSomethingPrintable() -> any Printable {
 return true
}

var someSortOfPrintableInstance = getSomethingPrintable()
print(someSortOfPrintableInstance.description)

// "An instance of Bool with value: true"를 출력함

someSortOfPrintableInstance의 구체적인 타입이 무엇인지는 중요하지 않으며, 컴파일러는 그것이 프로토콜을 준수함을 보장하므로 이 코드는 안전하다. 이 구문은 또한 컬렉션이 프로토콜을 기반으로 할 수 있음을 의미한다(예: let printableArray = [any Printable]).

확장과 프로토콜은 모두 스위프트 표준 라이브러리에서 광범위하게 사용된다. 스위프트 5.9 기준으로 표준 라이브러리 내의 모든 심볼 중 약 1.2%가 프로토콜이었으며, 또 다른 12.3%가 프로토콜 요구 사항이나 기본 구현이었다. 예를 들어 스위프트는 확장을 사용하여 String 및 Array와 같은 많은 기본 타입에 Equatable 프로토콜을 추가함으로써 == 연산자로 비교할 수 있게 한다. Equatable 프로토콜은 또한 다음과 같은 기본 구현을 정의한다.
func !=
(lhs: T, rhs: T) -> Bool
이 함수는 Equatable을 준수하는 모든 인스턴스에서 작동하는 메소드를 정의하여 '같지 않음' 연산자를 제공한다. 클래스든 구조체든 모든 인스턴스는 단순히 Equatable을 준수하기만 하면 이 구현을 자동으로 얻게 된다.

프로토콜, 확장 및 제네릭을 결합하여 정교한 API를 만들 수 있다. 예를 들어 제약 조건(constraints)은 채택하는 타입의 특성에 따라 타입이 조건부로 프로토콜이나 메소드를 채택할 수 있게 한다. 일반적인 유스케이스는 컬렉션 내에 포함된 요소들이 Equatable인 경우에만 컬렉션 타입에 메소드를 추가하는 것이다.
extension Array where Element: Equatable {

 // allEqual은 Equatable 요소를 포함하는 Array 인스턴스에서만 사용 가능함.
 func allEqual() -> Bool {
 for element in self {
 if element != self.first {
 return false
 }
 }
 return true
 }
}

 병행성 
스위프트 5.5는 언어에 구조화된 병행성을 도입했다. 구조화된 병행성은 코틀린, 자바스크립트 및 러스트와 유사한 Async/await 구문을 사용한다. 비동기 함수는 파라미터 리스트 뒤에 async 키워드로 정의된다. 비동기 함수를 호출할 때 함수를 호출하는 동안 실행이 잠재적으로 중단될 수 있음을 나타내기 위해 함수 앞에 await 키워드를 작성해야 한다. 함수가 중단된 동안 프로그램은 동일한 프로그램 내의 다른 병행 함수를 실행할 수 있다. 이 구문은 프로그램이 잠재적인 중단 지점을 명확하게 호출할 수 있게 하며, 이전의 광범위한 클로저 콜백 사용으로 인해 발생했던 피라미드 오브 둠의 변종을 피할 수 있게 해준다. func downloadText(name: String) async -> String {
 let result = // ... 비동기 다운로드 코드 ...
 return result
}

let text = await downloadText("text1")async let 구문은 여러 함수를 병렬로 실행할 수 있게 한다. 이전에 호출된 async 함수의 완료를 기다리기 위해 프로그램이 중단될 지점을 표시하는 데 다시 await가 사용된다.// 각 downloadText 호출은 병렬로 실행됨.
async let text1 = downloadText(name: "text1")
async let text2 = downloadText(name: "text2")
async let text3 = downloadText(name: "text3")

let textToPrint = await [text1, text2, text3] // 세 개의 downloadText 호출이 모두 반환될 때까지 중단됨.
print(textToPrint)런타임 중에 동적인 수의 자식 태스크를 생성하기 위해 Task와 TaskGroup을 명시적으로 생성할 수 있다.
let taskHandle = Task {
 await downloadText(name: "someText")
}

let result = await taskHandle.value스위프트는 행위자 모델을 사용하여 가변 상태를 격리함으로써 서로 다른 태스크가 공유 상태를 안전하게 변경할 수 있도록 한다. 액터는 actor 키워드로 선언되며 클래스와 같은 참조 타입이다. 오직 하나의 태스크만이 동시에 액터의 가변 상태에 접근할 수 있다. 액터는 자신의 내부 상태에 자유롭게 접근하고 변경할 수 있지만, 별도의 태스크에서 실행되는 코드는 다른 태스크가 액터의 상태에 대한 접근을 마칠 때까지 코드가 중단될 수 있음을 나타내기 위해 각 접근 시 await 키워드를 표시해야 한다.
actor Directory {
 var names: [String] = []

 func add(name: String) {
 names.append(name)
 }
}

let directory = Directory()

// 다른 태스크가 액터에 대한 접근을 마칠 때까지 코드가 중단됨.
await directory.add(name: "Tucker")
print(await directory.names)

 라이브러리, 런타임, 개발 
애플 시스템에서 스위프트는 현존하는 오브젝티브-C 시스템과 동일한 런타임을 사용하지만 iOS 7 또는 macOS 10.9 이상이 필요하다. 또한 Grand Central Dispatch에 의존한다. 스위프트와 오브젝티브-C 코드는 하나의 프로그램에서 함께 사용될 수 있으며, 확장하여 C와 C++도 가능하다. 스위프트 5.9부터 C++ 코드를 스위프트 코드에서 직접 사용할 수 있다. 오브젝티브-C의 경우 스위프트는 객체 모델에 상당한 접근 권한을 가지며, 이를 통해 오브젝티브-C 코드를 서브클래싱하고 확장하며 프로토콜 지원을 제공하는 데 사용할 수 있다. 그 반대는 불가능한데, 스위프트 클래스는 오브젝티브-C에서 서브클래싱될 수 없다.

이러한 프로그램의 개발과 기존 코드의 재사용을 돕기 위해 엑스코드 6 이상에서는 오브젝티브-C 코드를 스위프트에 노출시키기 위한 브리징 헤더를 구축하고 유지 관리하는 반자동 시스템을 제공한다. 이는 프로젝트의 스위프트 코드에 필요한 모든 오브젝티브-C 심볼을 단순히 정의하거나 가져오는 추가적인 헤더 파일의 형태를 취한다. 그 지점에서 스위프트는 해당 가져오기에서 선언된 타입, 함수 및 변수가 마치 스위프트로 작성된 것처럼 참조할 수 있다. 오브젝티브-C 코드도 프로젝트의 스위프트 심볼에 대한 오브젝티브-C 선언이 담긴 자동으로 유지 관리되는 헤더 파일을 가져옴으로써 스위프트 코드를 직접 사용할 수 있다. 예를 들어 "MyApp"이라는 혼합 프로젝트의 오브젝티브-C 파일은 #import "MyApp-Swift.h" 코드로 스위프트 클래스나 함수에 접근할 수 있다. 그러나 모든 심볼이 이 메커니즘을 통해 제공되는 것은 아니다. 제네릭 타입, 객체가 아닌 옵셔널 타입, 정교한 열거형 또는 유니코드 식별자와 같은 스위프트 전용 기능을 사용하면 오브젝티브-C에서 심볼에 접근하지 못할 수 있다.

스위프트는 또한 개발 환경에서 읽히는 메타데이터인 속성(attributes)을 제한적으로 지원하며, 이는 반드시 컴파일된 코드의 일부일 필요는 없다. 오브젝티브-C와 마찬가지로 속성은 @ 구문을 사용하지만 현재 사용 가능한 세트는 적다. 한 예로 @IBOutlet 속성이 있는데, 이는 코드 내의 주어진 값을 인터페이스 빌더(IB) 내에서 사용할 수 있는 아웃렛으로 표시한다. 아웃렛은 화면 상의 디스플레이 값을 코드의 객체에 바인딩하는 장치다.

애플 이외의 시스템에서 스위프트는 오브젝티브-C 런타임이나 다른 애플 시스템 라이브러리에 의존하지 않으며, 일련의 스위프트 "Corelib" 구현이 이를 대체한다. 여기에는 Foundation Kit을 대신하는 "swift-corelibs-foundation", Grand Central Dispatch를 대신하는 "swift-corelibs-libdispatch", 그리고 엑스코드의 XCTest API를 대신하는 "swift-corelibs-xctest"가 포함된다.

2019년부터 엑스코드 11과 함께 애플은 SwiftUI라는 주요 새로운 UI 패러다임을 추가했다. SwiftUI는 이전의 인터페이스 빌더 패러다임을 새로운 선언적 개발 패러다임으로 대체한다.

 메모리 관리 
스위프트는 메모리 관리를 위해 참조 횟수 계산 방식(ARC)을 사용한다. 클래스나 클로저의 모든 인스턴스는 프로그램이 보유하고 있는 참조의 수를 추적하는 참조 횟수를 유지한다. 이 횟수가 0에 도달하면 인스턴스는 할당 해제된다. 이러한 자동 할당 해제는 인스턴스가 더 이상 필요하지 않게 되는 즉시 할당 해제되므로 가비지 컬렉터의 필요성을 없애준다.

두 인스턴스가 각각 서로를 강하게 참조하는 경우(예: A가 B를 참조하고, B가 A를 참조함) 강한 참조 사이클이 발생할 수 있다. 두 인스턴스 모두 참조 횟수가 0에 도달할 수 없으므로 둘 다 할당 해제되지 않아 메모리 누수가 발생한다. 스위프트는 강한 참조 사이클을 방지하기 위해 weak 및 unowned 키워드를 제공한다. 이러한 키워드는 인스턴스의 참조 횟수를 증가시키지 않고 인스턴스를 참조할 수 있게 한다. weak 참조는 값이 변하고 nil이 될 수 있으므로 옵셔널 변수여야 한다. 이미 할당 해제된 unowned 값에 접근하려고 시도하면 런타임 오류가 발생한다.

클래스 내의 클로저도 self 참조를 캡처함으로써 강한 참조 사이클을 생성할 수 있다. 약하게 또는 미소유로 처리될 self 참조는 캡처 리스트를 사용하여 표시할 수 있다.
class Person {
 let name: String
 weak var home: Home? // 참조 사이클을 깨기 위해 약한 참조로 정의됨. 약한 참조는 참조하는 인스턴스의 참조 횟수를 증가시키지 않음.

 init(name: String) {
 self.name = name
 }

 deinit { print("De-initialized \(name)") }
}

class Home {
 let address: String
 var owner: Person?

 init(address: String, owner: Person?) {
 self.address = address
 self.owner = owner
 }

 deinit { print("De-initialized \(address)") }
}

var stacy: Person? = Person(name: "Stacy")
var house21b: Home? = Home(address: "21b Baker Street", owner: stacy)

stacy?.home = house21b // stacy와 house21b가 이제 서로를 참조함.

stacy = nil // house21b가 여전히 stacy에 대한 참조를 가지고 있으므로 stacy의 참조 횟수는 이제 1임.
house21b = nil // house21b의 참조 횟수가 0으로 떨어지고, house21b가 stacy를 강하게 참조하는 마지막 인스턴스였으므로 이어서 stacy의 횟수도 0으로 떨어짐.

// 출력 결과:
// De-initialized 21b Baker Street
// De-initialized Stacy

 디버깅 
스위프트 시스템의 핵심 요소는 읽기-평가-출력 루프(REPL)를 사용하여 개발 환경 내에서 깨끗하게 디버깅되고 실행될 수 있는 능력이며, 이는 전통적인 시스템 프로그래밍 언어보다 파이썬의 스크립팅 능력과 더 유사한 대화형 특성을 부여한다. REPL은 엑스코드 환경 내에서 실행되는 대화형 뷰인 플레이그라운드나 코드 또는 디버거 변경 사항에 즉시 반응하는 Playgrounds 앱을 통해 더욱 향상된다. 플레이그라운드를 사용하면 프로그래머가 마크다운 문서와 함께 스위프트 코드를 추가할 수 있다. 프로그래머는 콘솔이나 엑스코드와 같은 통합 개발 환경에서 LLDB를 사용하여 코드를 단계별로 실행하고 중단점을 추가할 수 있다.

 다른 언어와의 비교 
스위프트는 C 계열 프로그래밍 언어로 간주되며 여러 면에서 C와 유사하다.
 C의 대부분의 연산자는 스위프트에도 나타나지만 +와 같은 일부 연산자는 약간 다른 동작을 한다. 예를 들어 스위프트에서 +는 오버플로 시 트랩을 발생시키는 반면, &+는 오버플로 시 래핑되는 C와 유사한 동작을 나타내는 데 사용된다.
 문장을 그룹화하는 데 중괄호가 사용된다.
 변수는 등호 하나를 사용하여 할당되지만, 두 개의 연속된 등호를 사용하여 비교된다. 두 데이터 요소가 동일한 객체를 참조하는지 확인하기 위해 새로운 식별 연산자인 ===가 제공된다.
 제어문 while, if 및 switch는 유사하지만 확장된 기능을 갖는다. 예를 들어 정수가 아닌 케이스를 받는 switch, 패턴 매칭과 조건부 옵셔널 언래핑을 지원하는 while 및 if가 있으며, for는 구문을 사용한다.
 배열을 선언하고 배열 내의 주어진 인덱스에서 값을 가져오는 데 모두 대괄호가 사용된다.

오브젝티브-C와도 유사점이 있다.
 기본 숫자 타입: Int, UInt, Float, Double
 클래스 메소드는 인스턴스 메소드처럼 상속된다. 클래스 메소드에서의 self는 메소드가 호출된 클래스다.
 유사한 for...in 열거 구문.

오브젝티브-C와의 차이점은 다음과 같다.
 문장이 세미콜론(;)으로 끝날 필요가 없으나, 한 줄에 두 개 이상의 문장을 허용하려면 사용해야 한다.
 헤더 파일이 없다.
 자료형 추론을 사용한다.
 제네릭 프로그래밍.
 함수는 일급 함수 객체다.
 열거형 케이스는 연관 데이터(대수적 자료형)를 가질 수 있다.
 클래스에 대해 연산자를 재정의할 수 있으며(연산자 오버로딩), 새로운 연산자를 정의할 수 있다.
 문자열이 유니코드를 완전히 지원한다. 대부분의 유니코드 문자는 식별자나 연산자에 모두 사용될 수 있다.
 예외 처리가 없다. 스위프트 2는 이전과 호환되지 않는 다른 오류 처리 모델을 도입했다.
 잘못 사용하기 쉬운 이전 C 계열 언어의 여러 기능이 제거되었다.
 포인터가 기본적으로 노출되지 않는다. 프로그래머가 참조 또는 역참조를 위해 이름을 추적하고 표시할 필요가 없다.
 할당은 값을 반환하지 않는다. 이는 i == 0 대신 i = 0이라고 쓰는 일반적인 오류를 방지한다(컴파일 타임 오류 발생).
 switch 블록에서 break 문을 사용할 필요가 없다. fallthrough 문을 사용하지 않는 한 개별 케이스는 다음 케이스로 넘어가지 않는다.
 변수와 상수는 항상 초기화되며 배열 경계는 항상 검사된다.
 C에서 부호 있는 정수에 대해 미정의 동작을 초래하는 정수 오버플로는 스위프트에서 런타임 오류로 트랩된다. 프로그래머는 특수 산술 연산자 &+, &-, &*, &/ 및 &%를 사용하여 오버플로를 허용하도록 선택할 수 있다. min 및 max 속성은 스위프트의 모든 정수 타입에 정의되어 있어, 외부 라이브러리에 각 타입별로 정의된 상수에 의존하는 대신 잠재적인 오버플로를 안전하게 확인하는 데 사용될 수 있다.
 문장 주위의 중괄호 생략을 허용하는 if 및 while의 단일 문장 형태는 지원되지 않는다.
 오프 바이 원 오류가 발생하기 쉬운 C 스타일 열거 for (int i = 0; i < c; i++)는 지원되지 않는다(스위프트 3부터).
 전위 및 후위 증감 연산자(i++, --i ...)는 지원되지 않는다(스위프트 3부터). C 스타일 for 문 역시 스위프트 3부터 지원되지 않기 때문이다.

 개발 및 기타 구현체 
스위프트는 리눅스에서 실행될 수 있기 때문에 때때로 서버 측 언어로도 사용된다. IBM의 Kitura(현재 중단됨), Perfect, Vapor, 그리고 Hummingbird와 같은 일부 웹 프레임워크가 개발되었다.

애플에 의해 공식 "서버 API" 작업 그룹도 시작되었으며, 스위프트 개발자 커뮤니티 회원들이 중심적인 역할을 하고 있다.

코코아, 마이크로소프트의 공통 언어 기반(닷넷 프레임워크, 현재 닷넷), 그리고 자바 및 안드로이드 플랫폼을 대상으로 하는 스위프트의 두 번째 무료 구현체는 RemObjects Software의 Elements Compiler의 일부로 존재한다.

스위프트의 하위 집합이 아두이노 및 맥 OS 9와 같은 추가 플랫폼으로 이식되었다.

 같이 보기 

 Xcode
 오브젝티브-C
 파이썬

 각주 

 외부 링크 
 
 스위프트 언어 애플 레퍼런스 페이지
 Online Swift Compiler

분류:애플
분류:객체 지향 프로그래밍 언어
분류:프로그래밍 언어
분류:2014년 개발된 프로그래밍 언어
분류:시스템 프로그래밍 언어
분류:아파치 라이선스 소프트웨어
분류:선언형 프로그래밍
분류:패턴 매칭 프로그래밍 언어
분류:정적 타입 프로그래밍 언어