일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 가상 메모리
- SwiftUI
- @state
- 오브젝트
- COLOR
- IOS
- 프로세스 스케줄링
- 인프런
- 상호배제
- 운영체제
- Algorithm
- scrollview
- core data
- async
- decode
- Apple Developer Academy
- 앨런
- deadlock
- 동시성
- UserDefaults
- struct
- Swift
- 데드락
- Codable
- 비동기
- 100 days of SwiftUI
- forEach
- Linked List
- 알고리즘
- 동기화
Archives
- Today
- Total
기어가더라도 제대로
[Swift-기초] 제네릭을 실전처럼 이용해보기 - generics 본문
사실 기초가 아니지만,
어려운 예제지만 꼭 이해해야 더 풍성한 앱을 만드는데 도움이 되는 내용이기에 짚고 넘어갑니다.
Bundle에서 파일을 올리는 예제
- 번들에 하나의 타입을 담고 있는 JSON 파일이 각각 여러개라고 가정
- 제네릭을 쓰지 않으면, 파일 하나하나당 타입을 명시해줘야해서 재사용성이 떨어짐
func decode(_ file: String) -> [String: Astronaut] { }
func decode(_ file: String) -> [Mission] { }
- 각각은 missions.json, astronauts.json 두개의 파일을 올리기 위해서 거의 같은 내용의 메서드 두개를 만들어야할 것이다.
- 천천히 하나부터 기능이 동작하게 만들고 그다음에 이것을 제네릭으로 바꿔보자.
decoding Astronauts - 특정 타입 하나만 디코딩하기
struct Astronaut: Codable, Identifiable {
let id: String
let name: String
let description: String
}
extension Bundle {
func decode(_ file: String) -> [String: Astronaut] {
// 번들에서 해당 이름을 가진 파일의 URL을 찾음
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
// URL로 Data 를 만들어냄
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
// 데이터를 특정타입으로 변환
guard let loaded = try? decoder.decode([String: Astronaut].self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
- 이렇듯 특정 유형의 파일만을 디코딩할 수 있는 메서드가 만들어졌다.
- 하지만 이 코드를 활용해서 Mission까지 디코딩할 수 있도록 제네릭을 이용해 만들어보자.
generics 로 다수의 파일 decoding 하기
struct Mission: Codable, Identifiable {
struct CrewRole: Codable {
let name: String
let role: String
}
let id: Int
let launchDate: String?
let crew: [CrewRole]
let description: String
}
- 추가하려는 타입은 이렇다.
- 위의 decode 함수를 제네릭으로 바꾸기 위해서는 꺾은 선을 활용해야한다.
func decode<T>(_ file: String) -> [String: Astronaut] {
}
- 여기서 T는 어떠한 타입이든 올 수 있는 메타몽 같은 존재다.
- 하지만 반환타입이 이것을 받을 준비가 안되어있다.
func decode<T>(_ file: String) -> T {
}
- T 라는 타입을 반환하려 한다.
- 근데 JSON 파일이 여러 인스턴스를 가질것이라고 [T] 타입을 반환하게 되면 절대 안된다.
- 좀 어렵지만, JSON 자체를 읽고 어떤 타입을 반환할지 결정을 한다.
- missions.json -> [Mission]
- astronauts.json -> [String: Astronaut]
- [Mission], [String: Astronaut] 요것들 각각이 T 라고 인식을 하기 때문에 굳이 [T] 라고 할 필요가 없는 것이다.
- 만약 한다면 이런 모습일 것이다. [T] -> [[Mission]]
- 그치만 여기까지 해도 오류가 난다. 왜와이?
- T 는 어떤 타입이든 올 수 있다고 했는데 우리가 저 T 를 가지고 할 것은 decoding 이다
- 그렇다
- T는 Codable을 만족해야 디코딩될 수 있다.
func decode<T: Codable>(_ file: String) -> T {
}
- 아주 깔끔하다.
- decode 의 최종 코드는 다음과 같다.
extension Bundle {
func decode<T: Codable>(_ file: String) -> T {
// 번들에서 해당 이름을 가진 파일의 URL을 찾음
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
// URL로 Data 를 만들어냄
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
// 데이터를 특정타입으로 변환
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
- guard let loaded 가 있는 구문 주의
- T.self로 반환하려는 타입의 T로 변경
여기서 잠깐... T는 뭔지 알고 Swift가 변환을 하는가...?
여기서 type annotation이 등장한다.
let astronauts: [String: Astronaut] = Bundle.main.decode("astronauts.json")
let missions: [Mission] = Bundle.main.decode("missions.json")
- 반환타입이 무엇인지 type annotation 에서 밝혀준다.
- 어떤 타입인지 확실히 밝히는 주석
요약
- 제네릭을 잘쓰면 코드가 많이 줄 수 있다.
- 이 포스팅은 번들 파일을 다뤘지만, 네트워크 파일 또한 마찬가지로 적용할 수 있다.
'Swift - 기초' 카테고리의 다른 글
[Swift-기초] String 타입을 JSON으로 디코딩하기(Entity, DTO) (0) | 2024.09.06 |
---|---|
[Swift - 기초] Optional - 100 days of swiftUI (0) | 2022.10.04 |
[Swift-기초] Extension, protocol Extension - 100 days of SwiftUI (0) | 2022.10.02 |
[Swift - 기초] Protocol, Opaque return type(some) (0) | 2022.10.01 |
[Swift-기초] class - 100 days of swiftUI (2) | 2022.09.30 |
Comments