async
를 사용한다.async
를 function
키워드 앞에 붙이는 점이 Javascript
와 다르다.func fetchUserID(from server : String) async -> Int{
// 전달인자레이블을 from으로 지정
// 반환형 Int, 비동기 함수 async
if server == "primary"{
return 97
}
return 501
}
await
을 작성해서 비동기 함수를 호출하는 것을 나타낸다.func fetchUserName(from server : String) async -> String{
let userID = await fetchUserId(from : server)
if userID == 501{
return "John Appleseed"
}
return "Guest"
}
async let
을 사용해서 다른 비동기 코드와 병렬로 실행할 수 있다. await
을 작성하여 반환된 값을 사용한다.func connectUser(to server : String) async{
async let userID = fetchUserId(from : server)
async let userName = fetchUsername(from : server)
let greeting = await "Hello \(userName), user ID \(userID)"
}
swift
에서의 do-catch
는 javascript
에서의 try-catch
와 비슷한 동작을 수행한다.
잠재적으로 예외를 발생할 수 있는 코드를 do 코드블록 안에 삽입하고 catch에서 이를 처리한다.
기본구조의 예시는 다음과 같다.
do{
try 잠재적으로 오류를 발생시킬 수 있는 코드
}catch{
오류를 처리하는 코드
}
enum NetworkError : Error {
case invalidURL
case noData
case decodingError
}
struct User : Codable{
}
중간에 알아야 할 내용들을 먼저 살펴보자면,
1. JSON
- SWIFT object
2. Codable
을 먼저 살펴보겠다.
JSON
과 SWIFT 객체
는 디코딩
/인코딩
을 거치지 않고서는 서로 호환되지 않는다.
디코딩
/인코딩
에 들어가기 전 Codable
을 알아야하는데 이는 데이터 인코딩과 디코딩을 쉽게 처리할 수 있게 해주는 프로토콜이다.
Codable
은 실제로 Encodable
과 Decodable
프로토콜의 type alias
이다. 주요특징은 다음과 같다.
ㄴ 1. 자동구현
대부분의 경우에 swift 컴파일러가 자동으로 인코딩 / 디코딩 로직을 생성한다.
ㄴ 2. JSON 변환
JSON과 swift 객체 간의 변환을 쉽게 할 수 있다.
ㄴ 3. 사용자 정의
필요한 경우 인코딩 / 디코딩 프로세스를 커스터마이징할 수 있다.
ㄴ 4. 다양한 데이터 형식 지원
JSON 뿐만 아니라 PropertyList 등 다양한 형식을 지원한다.
Codable
예시코드// Codable
struct User : Codable {
let id : Int
let name : String
let email : String
}
let jsonString = """
{
"id" : 1,
"name" : "John Doe",
"email" : "john@example.com"
}
// JSON -> swift object
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder()
"""
do{
let user = try decoder.decode(User.self, from : jsonData)
print("User ID : \(user.id), Name : \(user.name), Email : \(user.email)")
}catch{
print("Decoding Error : \(error)")
}
struct User : Codable {
let id : Int
let name : String
let email : String
}
let user = User(id : 2, name : "John Doe", email : "jane@example.com")
let endcoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do{
let jsonData = try encoder.encode(user)
if let jsonString = String(data : jsonData, encoding : .utf8){
print("JSON : \(jsonString)")
}
}catch{
print("Encoding error : \(error)")
}
swift
에서는 catch
절에서 오류를 암시적으로 바인딩할 수 있다.catch
catch{
print("Decoding error : \(error)")
// error는 암시적으로 바인딩된 오류 객체이다.
}
catch
catch let decodingError as DecodingError{
print("Specific decoding error : \(decodingError)")
}
catch DecodingError.keyNotFound(let key, _){
print("Missing key : \(key)")
}
DecodingError
라는 에러 객체를 볼 수 잇는데, 이는 열거형 enum
이다. 이를 대상으로 다시 세부적인 예시코드를 보자. do{
}catch DecodingError.keyNotFound(let key, let context){
// JSON에서 필요한 키를 찾지 못했을 때
// key : 찾지 못한 키의 이름, context : 오류에 대한 추가정보
}catch DecodingError.valueNotFound(let type, let context){
// 예상된 값이 JSON에 없을 때 발생
// type : 찾지 못한 값의 예상타입
}catch DecodingError.typeMismatch(let type, let context){
// JSON의 값 타입이 예상과 다를 때 발생
// type : 예상된 타입
}catch{
print("ERROR: \(error)")
}
if let
와 guard let
은 옵셔널 값을 확인하고 해당 값을 일시적으로 변수
나 상수
에 바인딩
하는 기능을 수행한다.
그럼 옵셔널
이란
Swift
에서 옵셔널
은 값이 존재할 수도, 존재하지 않을 수도 있는 상황을 표현하는 타입이다.
예시로 Int?
를 들 수 있음
옵셔널을 선언할 때 초기값을 지정하지 않으면 자동으로 nil
이 할당된다.
nil
이 할당된 값에 접근하는 경우에 런타임 에러가 발생하기 때문에 옵셔널 바인딩
, 옵셔널 체이닝
, 옵셔널기본값
사용 등을 통해 nil
값에 대한 처리가 필요하다.
옵셔널 바인딩
언래핑
하는 방법이다.!
를 써서 강제로 옵셔널 추출if let
, guard let
을 써서 옵셔널 추출 if let
또는 if var
를 사용하여 옵셔널의 값이 존재하는지 검사하고, 존재하면 그 값을 다른 변수에 대입한다. 만약에 해당 옵셔널의 값이 nil
이면 그냥 넘어가고, nil
이 아니면 코드 블럭 안의 구문을 실행한다. 옵셔널 바인딩
의 예시코드는 다음과 같다. let x : String? = "TEST"
let y : String? = nil
// 초기화해주지 않으면 자동으로 nil로 초기화된다.
if let xx = x{
print("\(xx)는 nil이 아니었음")
}
if let yy = y{
print("\(yy)는 nil이 아니었음")
}
// 출력 : TEST는 nil이 아니었음
강제로 실행
의 예시코드는 다음과 같다.let x : String? = "TEST"
let y : String? = nil
print("\(x!)")
print("\(y!)")
// 첫번째 x는 출력되지만 Y는 널참조로 런타임 에러가 발생한다.
let name1 : String?
let name2 : String?
// 둘다 자동으로 nil로 초기화 된다.
if let name11 = name1,
let name22 = name2{
print(name11, name22)
}
옵셔널 체이닝
언래핑
없이 옵셔널 값
에 접근한다.struct TestStruct{
wrapper : TestInnerStruct?
}
struct TestInnerStruct{
innerValue : Int? = 10
}
라고 한다.
이제 여기에서
let result = TestStruct?.wrapper?.innerValue;
를 했다고 쳐보자.
1. result의 type은 일단 옵셔널<맨 뒤의 타입이다. 여기서는 innerValue
>
2. 만약에 중간에 wrapper가 nil
이었다. 그러면?
ㄴ 런타임에러
는 발생하지 않고, result
는 nil
이 된다.
ㄴ 이어지는 innerValue는 평가하지 않는다
옵셔널 기본값
let testValue1 : Int? = 10
let testValue2 : Int?
// testValue2는 자동으로 nil
let testValue3 = testValue2 ?? 20 // testValue2가 nil이라면 20
let testValue4 :Int?
let testValue5 = testValue2 ?? testValue4 ?? 30 // 둘다 nil이라면 30
if let
과 비슷하지만, guard let
에서는 else
인 부분만 작성이 가능하다.nil
이어서 옵셔널 추출이 되지 않을 때만 어떠한 행동을 취할 수 있다.nil
값이 아닐 걸 확인하고 옵셔널을 성공적으로 추출했다면, guard let
문을 통과하게 된다. (유의)
guard let문을 통과하게 되면 저장된 상수는 전역변수
로써 사용이 가능하다. (유의)
guard let 문의 else 안에는 항상 return
아니면 throw
문이 와야한다.예시코드를 살펴보면
let value1 : Int? = 10
let value2 : Int?
guard let test1 = value1 else{
return print("value1는 optional")
}
print(test1)
guard let test2 = value2 else{
return print("value2는 optional")
}
print(test2)
// 이렇게 되면 출력이 10, value2는 optional이라고 뜨는데
// 여기서 test1은 전역변수로써 사용이 가능하다.
swift
에서 익명함수
를 만들기 위해서 다음과 같은 절차를 따른다.
ex) 예시 함수 (인자가 없는 경우)
func exampleFunc() {
self.Something.x += 20
}
위와 동일한 기능을 하는 익명함수 (이상하게 생김.. 진짜로.. 아이폰은 이쁜데 얘는 왜 이렇게 생겼지..)
{
() -> () in
self.Something.x += 20
}
ex) 예시 함수 ( 인자가 있는 경우 )
func exampleFunc(testValue : Int){
self.Something.x += testValue
}
위와 동일한 기능을 하는 익명함수
{
(testValue : Int) -> () in
print("test value : \(testValue)")
}
UIView.animateWithDuration(0.4, animation : whatToAnimate, completion : whatToDoLater)
위 코드를 익명함수를 사용한다면
UIView.animateWithDuration(0.4,
animation :
{() -> () in
self.myButton.frame.origin.y += 20
}
completion :
{
(finished : Bool) -> () in
println("finished : \(finished)")
}
UIView.animateWithDuration(0.4, animation : {
() in
~~~본문
}, completion : {})
UIView.animateWithDuration(0.4, animation : {
~~~본문
}, completion : {})
UIView.animateWithDuration(0.4, animation : {
(testValue) -> () in
~~~본문
}, completion : {})
// 파라미터 타입과 반환값 생략
UIView.animateWithDuration(0.4, animation : {
(testValue) in
~~~본문
}, completion : {})
UIView.animateWithDuration(0.4, animation : {
testValue in
~~~ 본문
}, completion : {})
UIView.animateWithDuration(0.4, animation : {
print("first param : \($0)")
}, completion : {})
UIView.animateWithDuration(0.4, animation : {
_ in
print(~~)
}, completion : {})
let arr = [2,4,6,8]
func doubleMe(i : Int) -> Int{
return i*2
}
let arr2 = arr.map(doubleMe)
// 익명함수로 축약하면 다음과 같이 가능하다.
let arr3 = arr.map{
element -> Int in
return element * 2
}
let arr4 = arr.map{
return $0 * 2
}
let arr5 = arr.map{ $0 * 2 }
`