Posted:      Updated:

Swift

애플이 만든 언어로, 오픈 소스화 되어 있어 깃허브에서 공개된 사항을 볼 수 있다.

사용 환경

macOS, Ubuntu 에서 사용할 수 있다.
swift 다운로드 페이지에서 자세한 설명을 확인할 수 있다.

Expressive

문법 표현이 매우 다양하다.
모든 경우의 수를 다 알 수는 없지만, 최소한의 핵심 문법을 통해 접근한다.

명명 규칙

기본적으로 카멜 케이스를 사용한다.
함수, 메서드, 변수, 상수는 소문자로 시작하는 카멜 케이스를 사용한다.
클래스, 구조체, 열거형, 익스텐션과 같은 타입 이름은 대문자로 시작하는 카멜 케이스를 사용한다.

✔️ 스위프트는 모든 대소문자를 구분한다.

콘솔로그

printdump 가 있다.
print 는 단순 문자열 출력에 사용하며, dump 는 인스턴스의 자세한 설명(요소들)까지 전부 출력해준다.

문자열 보간법

\()

문자열 내에 변수 또는 상수의 실질적인 값을 표현하기 위해 사용한다.

import Swift
let age: Int = 10

print("안녕하세요 저는 \(age)살입니다.")
print("\n#############\n")

class Person {
    var name: String = "better"
    var age: Int = 10
}

let better: Person = Person()

print(better)
print("\n#############\n")
dump(better)

상수와 변수

함수형 프로그래밍 패러다임을 채용하여 불변 객체를 중요하게 여긴다.
상속 표현이 많이 등장한다.
Xcode 에서는 변경되지 않는 값에 상수를 쓰지 않았다고 경고를 띄워준다.

키워드

상수: let 변수: var

선언

let 이름: 타입 = 
var 이름: 타입 = 

// 값의 타입이 명확하면 타입 생략 가능
let 이름 = 
var 이름 = 
let constant: String = "차후에 변경이 불가능한 상수 let"
var variable: String = "차후에 변경이 가능한 변수 var"

variable = "변수는 이렇게 차후에 다른 값을 할당할 수 있지만"
// constant = "상수는 차후에 값을 변경할 수 없습니다" // 오류발생


상수 선언 후 나중에 값 할당하기

타입을 미리 명시해야 한다.
상수의 경우 한 번 초기화 한 이후에는 값을 바꿀 수 없다.

let sum: Int
let inputA: Int = 100
let inputB = Int = 200

sum = inputA + inputB

// sum = 1 // 오류 발생
var nickName: String

nickName = "better"
nickName = "난정"

기본 데이터 타입

  • Bool
  • Int, UInt
  • Float, Double
  • Character, String

Bool

var someBool: Bool = true
someBool = false
// someBool = 0 // 컴파일 오류발생
// someBool = 1 // 컴파일 오류발생

Int, UInt

  • Int : 정수 타입. 현재는 기본적으로 64비트 정수형
  • UInt : 양의 정수 타입. 현재는 기본적으로 64비트 양의 정수형.
// Int
var someInt: Int = -100
// someInt = 100.1 // 컴파일 오류발생

//UInt
var someUInt: UInt = 100
// someUInt = -100 // 컴파일 오류발생
// someUInt = someInt // 컴파일 오류발생

Float, Double

// Float
var someFloat: Float = 3.14
someFloat = 3

// Double
var someDouble: Double = 3.14
someDouble = 3
// someDouble = someFloat // 컴파일 오류발생

Character, String

// Character
var someCharacter: Character = "🇰🇷"
someCharacter = "😄"
someCharacter = "가"
someCharacter = "A"
// someCharacter = "하하하" // 컴파일 오류발생
print(someCharacter)

// String
var someString: String = "하하하 😄 "
someString = someString + "웃으면 복이와요"
print(someString)

// someString = someCharacter // 컴파일 오류발생

Any, AnyObject, nil

Any

Swift 의 모든 타입을 지칭하는 키워드이다.

var someAny: Any = 100
someAny = "어떤 타입도 수용 가능합니다"
someAny = 123.12

// let someDouble: Double = someAny  // 컴파일 오류발생

Any 타입에 Double 자료를 넣어두었더라도 Any는 Double 타입이 아니기 때문에 할당할 수 없다.
명시적으로 타입을 변환해 주어야 한다.

AnyObject

모든 클래스 타입을 지칭하는 프로토콜이다.

class SomeClass {}
var someAnyObject: AnyObject = SomeClass()

// someAnyObject = 123.12    // 컴파일 오류발생

AnyObject는 클래스의 인스턴스만 수용 가능하기 때문에 클래스의 인스턴스가 아니면 할당할 수 없다.

nil

없음을 의미하는 키워드이다.
NULL, Null, null 등과 유사한 표현이다.

var someAny: Any = 100
var someAnyObject: AnyObject = SomeClass()

// someAny = nil         // 컴파일 오류발생
// someAnyObject = nil   // 컴파일 오류발생

컬렉션 타입(Array, Dictinonary, Set)

여러 값을 묶어서 하나의 변수로 표현할 수 있게 만들어준다.

Array

순서가 있고 인덱스가 있는 리스트 컬렉션 타입이다.

// 1. Array 선언 및 생성
var integers: Array<Int> = Array<Int>()

// 위와 동일한 표현
// var integers: Array<Int> = [Int]()
// var integers: Array<Int> = []
// var integers: [Int] = Array<Int>()
// var integers: [Int] = [Int]()
// var integers: [Int] = []
// var integers = [Int]()


// 2. Array 활용
integers.append(1)
integers.append(100)

print(integers)	// [1, 100]

// 멤버 포함 여부 확인
print(integers.contains(100)) // true
print(integers.contains(99)) // false

// 멤버 교체
integers[0] = 99

// 멤버 삭제
integers.remove(at: 0)
integers.removeLast()
integers.removeAll()

// 멤버 수 확인
print(integers.count)


// 3. 불변 Array: let을 사용하여 Array 선언
let immutableArray = [1, 2, 3]

// 수정이 불가능한 Array이므로 멤버를 추가하거나 삭제할 수 없음
//immutableArray.append(4)
//immutableArray.removeAll()

Dictionary

키와 값의 쌍으로 이루어진 컬렉션이다.
HashMap과 비슷하다.

// 1. Dictionary의 선언과 생성
// Key가 String 타입이고 Value가 Any인 빈 Dictionary 생성
var anyDictionary: Dictionary<String, Any> = [String: Any]()

// 위와 동일한 표현
// var anyDictionary: Dictionary <String, Any> = Dictionary<String, Any>()
// var anyDictionary: Dictionary <String, Any> = [:]
// var anyDictionary: [String: Any] = Dictionary<String, Any>()
// var anyDictionary: [String: Any] = [String: Any]()
// var anyDictionary: [String: Any] = [:]
// var anyDictionary = [String: Any]()


// 2. Dictionary 활용
// 키에 해당하는 값 할당
anyDictionary["someKey"] = "value"
anyDictionary["anotherKey"] = 100

print(anyDictionary) // ["someKey": "value", "anotherKey": 100]

// 키에 해당하는 값 변경
anyDictionary["someKey"] = "dictionary"
print(anyDictionary) ["someKey": "dictionary", "anotherKey": 100]

// 키에 해당하는 값 제거
anyDictionary.removeValue(forKey: "anotherKey")
anyDictionary["someKey"] = nil
print(anyDictionary)


// 3. 불변 Dictionary: let을 사용하여 Dictionary 선언
let emptyDictionary: [String: String] = [:]
let initalizedDictionary: [String: String] = ["name": "yagom", "gender": "male"]
// 컴파일 오류 발생
let someValue: String = initalizedDictionary["name"]

initalizedDictionary["name"] 의 값이 존재하지 않을 수도 있으므로 오류가 발생한다.

Set

순서가 없고, 멤버가 유일한 컬렉션이다.
수학에서의 집합과 유사한 개념이다.

// 1. Set 생성 및 선언
var integerSet: Set<Int> = Set<Int>()

// insert : 새로운 멤버 입력
// 동일한 값은 여러번 insert해도 한번만 저장
integerSet.insert(1)
integerSet.insert(99)
integerSet.insert(99)
integerSet.insert(99)
integerSet.insert(100)

print(intigerSet) // {100, 99, 1}

// contains: 멤버 포함 여부 확인
print(integerSet.contatins(1)) // true
print(integerSet.contains(2)) // false

// remove: 멤버 삭제
integerSet.remove(99) // {100, 1}
integerSet.removeFirst() // {1}

// count: 멤버 개수
integerSet.count // 1


// 2. Set의 활용
// 멤버의 유일성이 보장되기 때문에 집합 연산에 유용
let setA: Set<Int> = [1, 2, 3, 4, 5]
let setB: Set<Int> = [3, 4, 5, 6, 7]

// 합집합
let union: Set<Int> = setA.union(setB)
print(union) // [2, 4, 5, 6, 7, 3, 1]

// 합집합 오름차순 정렬
let sortedUnion: [Int] = union.sorted()
print(sortedUnion) // [1, 2, 3, 4, 5, 6, 7]

// 교집합
let intersection: Set<Int> = setA.intersection(setB)
print(intersection) // [5, 3, 4]

// 차집합
let subtracting: Set<Int> = setA.subtracting(setB)
print(subtracting) // [2, 1]

함수

기본 형태

func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 ...) -> 반환타입 {
    /* 함수 구현부 */
    return 반환값
}
func sum(a: Int, b: Int) -> Int {
    return a + b
}

반환값이 없는 함수

func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 ...) -> Void {
    /* 함수 구현부 */
    return
}
func printMyName(name: String) -> Void {
    print(name)
}

// 반환 타입(Void) 생략 가능
func printYourName(name: String) {
    print(name)
}

매개변수가 없는 함수

func 함수이름() -> 반환타입 {
    /* 함수 구현부 */
    return 반환값
}
func maximumIntegerValue() -> Int {
    return Int.max
}

매개변수와 반환값이 없는 함수

func 함수이름() -> Void {
    /* 함수 구현부 */
    return
}
// 줄바꿈을 하지 않고 한 줄에 표현
func hello() -> Void { print("hello") }
// 반환 값이 없는 경우 반환 타입(Void) 생략
func 함수이름() {
    /* 함수 구현부 */
    return
}
func bye() { print("bye") }

함수 호출

sum(a: 3, b: 5) // 8

printMyName(name: "better") // better

maximumIntegerValue() // Int의 최댓값

hello() // hello

bye() // bye

매개변수 기본값

func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 = 매개변수 기본값 ...) -> 반환타입 {
    /* 함수 구현부 */
    return 반환값
}

func greeting(friend: String, me: String = "yagom") {
    print("Hello \(friend)! I'm \(me)")
}

// 매개변수 기본값을 가지는 매개변수는 호출 시 생략 가능
greeting(friend: "hana") // Hello hana! I'm yagom
greeting(friend: "john", me: "eric") // Hello john! I'm eric

전달인자 레이블(Argument Label)

함수를 호출할 때 함수 사용자의 입장에서 매개변수의 역할을 좀 더 명확하게 표현하고자 할 때 사용한다.
전달인자 레이블을 변경하여 동일한 이름의 함수를 중복으로 생성할 수 있다.

func 함수이름(전달인자 레이블 매개변수1이름: 매개변수1타입, 전달인자 레이블 매개변수2이름: 매개변수2타입 ...) -> 반환타입 {
    /* 함수 구현부 */
    return
}

// 함수 내부에서 전달인자를 사용할 때에는 매개변수 이름 사용
func greeting(to friend: String, from me: String) {
    print("Hello \(friend)! I'm \(me)")
}

// 함수를 호출할 때에는 전달인자 레이블을 사용
greeting(to: "hana", from: "yagom") // Hello hana! I'm yagom

가변 매개변수

전달 받을 값의 개수를 알기 어려울 때 사용한다.
가변 매개변수는 함수당 하나만 가질 수 있다.
기본값이 있는 매개변수처럼 가변 매개변수도 매개변수 목록 중 뒤쪽에 위치하는 것이 좋다.

//func 함수이름(매개변수1이름: 매개변수1타입, 전달인자 레이블 매개변수2이름: 매개변수2타입...) -> 반환타입 {
//    /* 함수 구현부 */
//    return
//}

func sayHelloToFriends(me: String, friends: String...) -> String {
    return "Hello \(friends)! I'm \(me)!"
}
print(sayHelloToFriends(me: "yagom", friends: "hana", "eric", "wing"))
// Hello ["hana", "eric", "wing"]! I'm yagom!

print(sayHelloToFriends(me: "yagom"))
// Hello []! I'm yagom!

데이터 타입으로서의 함수

스위프트는 함수형 프로그래밍 패러다임을 포함하는 다중 패러다임 언어이므로 스위프트의 함수는 일급객체이다.
따라서 함수를 변수, 상수 등에 할당이 가능하고 매개변수를 통해 전달할 수 있다.
함수의 타입 표현에서는 반환 타입을 생략할 수 없다.

var someFunction: (String, String) -> Void = greeting(to:from:)
someFunction("eric", "yagom") // Hello eric! I'm yagom

someFunction = greeting(friend:me:)
someFunction("eric", "yagom") // Hello eric! I'm yagom

func runAnother(function: (String, String) -> Void) {
    function("jenny", "mike")
}

// Hello jenny! I'm mike
runAnother(function: greeting(friend:me:))

// Hello jenny! I'm mike
runAnother(function: someFunction)

조건문

if-else 구문

if 조건 {
     /* 실행 구문 */
} else if 조건 {
    /* 실행 구문 */
} else {
    /* 실행 구문 */
}

switch 구문

정수타입 값 뿐만 아니라 대부분의 스위프트 기본 타입을 지원한다.
따라서 다양한 패턴과 응용이 가능하다.
명시적 break 를 하지 않아도 자동으로 case 마다 break 된다.
fallthrough 키워드를 활용하면 break 를 무시할 수 있다.
쉼표(,)를 사용하여 하나의 case에 여러 패턴을 명시할 수 있다.

switch 비교값 {
case 패턴:
    /* 실행 구문 */
default:
    /* 실행 구문 */
}

반복문

for-in 구문

for-each 구문과 유사하다.
Dictionary의 경우 이터레이션 아이템으로 튜플이 들어온다.

for item in items {
    /* 실행 구문 */
}
var integers = [1, 2, 3]
let people = ["yagom": 10, "eric": 15, "mike": 12]

for integer in integers {
    print(integer)
}

// Dictionary의 item은 key와 value로 구성된 튜플 타입
for (name, age) in people {
    print("\(name): \(age)")
}

while 구문

while 조건 {
    /* 실행 구문 */
}
while integers.count > 1 {
    integers.removeLast()
}

repeat-while 구문

기존 언어의 do-while 구문과 형태/동작이 유사하다.

repeat {
    /* 실행 구문 */
} while 조건
repeat {
    integers.removeLast()
} while integers.count > 0

옵셔널

// someOptionalParm에 nil이 할당 될 수 있다.
func someFunction(someOptionalParam: Int?) {
       // ....
}

/// someOptionalParm에 nil이 할당 될 수 없다.
func someFunction(someOptionalParam: Int) {
       // ....
}

someFunction(someOptionalParam: nil)
// someFunction(someParam: nil) 

값이 있을 수도, 없을 수도 있음을 의미한다.
nil 의 가능성을 명시적으로 표현한다.

nil 가능성을 문서화하지 않아도 코드만으로 표현할 수 있어 문서/주석 작성 시간을 절약할 수 있다.

전달받은 값이 옵셔널이 아니라면 nil 체크를 하지 않더라도 안심하고 사용할 수 있어 효율적이다.

옵셔널 문법과 선언

enum + generics 이라고 할 수 있다.

enum Optional<Wrapped>: ExpressibleByNiliteral {
         case none
         case some(Wrapped)
}

let optionalValue: Optional<Int> = nil
let optionalValue: Int? =nil

옵셔널 표현

  • 느낌표(!)를 이용한 암시적 추출 옵셔널
var implicitlyUnwrappedOptionalValue: Int! = 100

switch implicitlyUnwrappedOptionalValue {
case .none:
    print("This Optional variable is nil")
case .some(let value):
    print("Value is \(value)")
}

// 기존 변수처럼 사용 가능
implicitlyUnwrappedOptionalValue = implicitlyUnwrappedOptionalValue + 1

// nil 할당 가능
implicitlyUnwrappedOptionalValue = nil
  • 물음표(?)를 이용한 옵셔널
// Optional
var optionalValue: Int? = 100

switch optionalValue {
case .none:
    print("This Optional variable is nil")
case .some(let value):
    print("Value is \(value)")
}

// nil 할당 가능
optionalValue = nil

옵셔널 추출

옵셔널에 들어있는 값을 사용하기 위해 꺼내오는 것을 옵셔널 추출이라고 한다.

옵셔널 바인딩

옵셔널 안에 값이 들어있는지 확인하고 값이 있으면 값을 꺼낸다.
if-let 방식을 사용한다.
let 으로 꺼낸 값은 if-let 구문 내에서만 사용 가능하다.

func printName(_ name: String) {
    print(name)
}

var myName: String? = nil
var yourName: String! = nil

if let name: String = myName {
    printName(name)
} else {
    print("myName == nil")
}

if let name: String = yourName {
    printName(name)
} else {
    print("yourName == nil")
}

myName = "yagom"
yourName = "hana"

if let name = myName, let friend = yourName {
    print("\(name) and \(friend)")
}
// yagom and hana

강제 추출

옵셔널에 값이 들어있는지 아닌지 확인하지 않고 강제로 값을 꺼내는 방식이다.
값이 없을 경우(nil) 런타임 오류가 발생하므로 추천되지 않는다.

var myName: String? = yagom
var youName: String! = nil


printName(myName!) // yagom
myName = nil

//print(myName!)
// 강제추출시 값이 없으므로 런타임 오류 발생
yourName = nil

//printName(yourName)
// nil 값이 전달되기 때문에 런타임 오류발생

Categories:

댓글남기기