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절에서 오류를 암시적으로 바인딩할 수 있다.catchcatch{
print("Decoding error : \(error)")
// error는 암시적으로 바인딩된 오류 객체이다.
}
catchcatch 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 }
`