5์ฃผ์ฐจ ๊ธฐ๋ก: API ํต์ , Codable, DispatchGroup
๐ง ๋ฌด์์ ๋ฐฐ์ ๋?
- codable (from JSON)
- API ๋คํธ์ํฌ ํต์ ์ฌํ
- DispatchGroup (APIํต์ ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌ)
- UNUserNotificationCenter (์๋ฆผ ๋์ฐ๊ธฐ)
- ์ธ๋ถ ํฐํธ ์ฝ์
๐จ ์์
- NASA sync / async๋ก ์ฌ์ง ๋ฐ์ผ๋ฉฐ ์์ ํ์ธํด๋ณด๊ธฐ
- PosterViewController
๐ ๊ณผ์
- 4์ฃผ์ฐจ API ํต์ (JSON -> Codable) ํ๋ก์ ํธ ๊ฐ์ ex.์นด์นด์คAPI
- Media Project ๊ฐ์ + ์์ฉ + ์ฌํ
- ๋ค๋ง๊ณ ์น ํ๋ก์ ํธ ๋ฆฌํฉํ ๋ง (์์ ๋ ๋ฐฐ์ด ์์๋ค๋ก ์ถ๊ฐ ๋ฐ ๊ฐ์ )
๐ฆ ๋ณต๊ธฐ
โ๏ธ Concurrency Programming
GCD : Grand Central Dispatch ์ด๋ฆ ๊ทธ๋๋ก ์ฐ๋ ๋๋ฅผ ๊ด๋ฆฌํ๋ dispatch๋ฅผ ๋ด๋นํ๋ ๊ธฐ์
Dispatch, also known as Grand Central Dispatch (GCD), contains language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in macOS, iOS, watchOS, and tvOS. ์ถ์ฒ: https://developer.apple.com/documentation/DISPATCH |
์ ํ๊ณต์ ๋ฌธ์๋ฅผ ์ฐพ์๋ณด๋ฉด, dispatch์ ์ญํ ์ด ์์คํ ์ ์คํํ์ ๋ ๋ค์ํ ์ญํ ๋ค์ด ์ ์๋๋๋๋ก ๊ฐ์ ํ๋ ์์๋ค์ ๋ชจ์์์ ์ ์ ์๋ค. ์ฆ, ์ฑ์์ ๊ฐ์์ ์ญํ ์ด ์ถฉ๋์์ด ์ ๊ตด๋ฌ๊ฐ๊ฒ ํ๋ ค๋ฉด dispatch์ ์ญํ ์ด ์ค์ํ๋ค๋ ๊ฒ.
[sync,async -> main(์ ์ผ ์์) ๊ธฐ์ค]
[serial, concurrent -> queue (์ ์ผ ์ํค๋) ๊ธฐ์ค]
*sync: ํ์ ์์ ์ ๋๊ฒจ์ฃผ๋ฉด ๊ทธ ์ผ์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ์ํ (๋๋์ผ ๋ค์ task๋ฅผ ์์ํจ)
*async: ํ์๊ฒ ์์ ์ ๋๊ฒจ์ฃผ๊ณ ๋ฐ๋ก ๋ค์ ์ผ์ *๋์์* ์์ํ ์ํ
Serial/Main
- DispatchQueue.main.sync ๋ฅผ ์ ์ฌ์ฉํ์ง ์๋ ์ด์ ๋ ๋ฌดํ๊ต์ฐฉ ์ํ์ธ ๋ฐ๋๋ฝ(deadLock) ํ์์ด ์ผ์ด๋ ์ ์๊ธฐ ๋๋ฌธ.
์ผ๋จ ์ฝ๊ฒ ์ดํดํ๊ธฐ ์ํด ํ์ด์ฐ์๋ฉด "main์ด ํ๋"์ "sync๋ต๊ฒ ํ ๋๊ฐ ๋จผ์ ์ผ์ ๋๋ด"์ ๋ฌดํ ๋ฐ๋ณต์ ์ํฉ์ด๋ ๊ฒ. ๋ฉ์ธ์ด ์์๊ฐ ๊ผฌ์ฌ์ blockํ ์ผ์ ์ฒ๋ฆฌํ๋ ค๊ณ ํ๋ ๋ฑ์ ์ฐ๋ ๋๋ค์ด ์ ์์ ์ผ๋ก ์ผ์ ์ฒ๋ฆฌํ ์๊ฐ ์๊ฒ ๋์ด ์ฌ์ฉ์ ์ง์ํ๋ค.
[๋ฐ์ ์ค๋ฅ]
- ๊ต์ฐฉ์ํ์ ๋น ์ก๊ธฐ ๋๋ฌธ์ ์คํ์ ์์ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
- DispatchQueue.main.async
DispatchQueue.main.async { //์ด๊ฑธ ํ์ ๋ณด๋ด๋์ ๊ฑด๋ฐ ๋ฐ์ ์์
๋๋ด๋ฉด ๋ฉ์ธ์ด ๋ค์ ์์ํ ๊ฒ.
for i in 1...50 {
sleep(1)
print(i, terminator: " ")
}
}
for i in 50...100 {
sleep(1)
print(i, terminator: " ")
}
์คํ์ํค๋ฉด 50๋ถํฐ 100๊น์ง์ ์ซ์๊ฐ ๋จผ์ ์ฐํ๊ณ , ๊ทธ๋ค์์์ผ 1๋ถํฐ 50์ ์ซ์๊ฐ ์ฐํ๋ ๊ฑธ ํ์ธํ ์ ์๋ค. ์ ์ฝ๋ ๊ธฐ์ค์ผ๋ก ๋น๋๊ธฐ๋ก ์์ ์ ํ๋ฉด์ task 2๊ฐ๋ฅผ ํ ์ ์๋ค๋ ์ฅ์ ์ด ์์.
Concurrent/global
- DispatchQueue.global().sync ์ญ์ ์ ์ฌ์ฉํ์ง ์๋๋ค. ๋ค๋ฅธ ์ฐ๋ ๋์๊ฒ ์ผ์ ๋๊ฒจ์ฃผ์์ง๋ง, sync ๋๋ฌธ์ ๊ทธ ์ผ์ด ๋๋๊ธฐ ์ ๊น์ง main์ด ์ผ์ ์์ํ ์ ์๊ธฐ ๋๋ฌธ์ ํจ์จ์ ์ด์ง ๋ชป ํจ. ๋ํ, ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋๊ธฐ/๋น๋๊ธฐ ๊ตฌ๋ถ ์์ด ์ฝ๋๋ฅผ ์งฐ์ ๋๋ ์์ ์์๊ฐ ๋๊ฐ์์ ๊ตณ์ด ์ฌ์ฉํ ์ด์ ๊ฐ ์์.
- DispatchQueue.global().async
DispatchQueue.global().async {
for i in 1...50 {
sleep(1)
print(i, terminator: " ")
}
}
for i in 50...100 {
sleep(1)
print(i, terminator: " ")
}
์ ์ฝ๋๋ฅผ ์คํ์ํค๋ฉด 50 1 51 2 ์ฒ๋ผ ์ ์๋์ ์์ด ๋์์ ๋์ค๋ ๊ฒ์ฒ๋ผ, ๋ชจ๋ ์ฐ๋ ๋๊ฐ ๋์์ ์ผ์ ์ฒ๋ฆฌํ๊ณ ์์์ ์ ์ ์๋ค. ์ผ์ฒ๋ฆฌ ์๋๋ ๋น ๋ฆ. ๊ทธ๋์ ์ฃผ๋ก ๋คํธ์ํฌ ํต์ ์ ํ ๋ ์ฌ์ฉํ๋ ํธ.
โ๏ธ codable
- SwiftyJSON ๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ฐ์ดํธ์ ๋ฌธ์ (์ค์ ๋ก SwfityJSON์ alamofire์ ๋นํด ์ ๋ฐ์ดํธ๊ฐ ๋ง~์ด ๋ฐ๋ฆฐ ์ํ), ๋์ด๋๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์๊ฐ ๋ฑ ๋ณตํฉ์ ์ธ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ณด์ํ๋ ๋ฐฉ์์ด codable.
- codable์๋ decodable๊ณผ encodable์ด ์๋ค.
decodable : ๊ตฌ์กฐ์ฒด -> JSON ํ์
encodable: JSON -> Struct ๊ตฌ์กฐ
*๋ณํ ๊ณผ์ * + โ๏ธ 8/16์ผ ์์ฑ
//codable
let json = """
{
"quote": "The will of man is his happiness.",
"author": "Friedrich Schiller",
"category": "happiness"
}
"""
// String => Data => Quote (๋์ฝ๋ฉ, ์ญ์ง๋ ฌํ)
struct Quote: Decodable { //caseIterable ๊ฐ์ด.
let quote : String
let author : String
let category : String
}
// String => Data (๋ฌธ์์ด์ ๋ฐ์ดํฐํ์์ผ๋ก ๋ฐํ)
guard let result = json.data(using: .utf8) else {
fatalError("ERROR")
}
print(result)
//Data => Quote
//Error handling, Do Try Catch, Meta Type
do {
let value = try JSONDecoder().decode(Quote.self, from: result)
print(value.author)
print(value)
} catch { // ์ค๋ฅ๊ฐ ์๊ธฐ๋ฉด ์๊ฐ ์คํ๋จ.
print(error)
}
*์์2 : json์ ๊ตฌ์กฐ์ฒด๊ฐ, struct ๊ตฌ์กฐ์ฒด๋ ์ผ์น ํ์ง ์์ผ๋ฉด?(ํค ๊ฐ์ด ์ผ์นํ์ง ์๋ค๋ฉด?) custom ํด์ฃผ๋ฉด ๋๋ค*
let json = """
{
"quote_content": "The will of man is his happiness.",
"author_name": "Friedrich Schiller",
}
"""
// String => Data => Quote
struct Quote: Decodable {
let quoteContent : String
let authorName : String
}
// String => Data
guard let result = json.data(using: .utf8) else {
fatalError("ERROR")
}
print(result)
//Data => Quote
//Error handling, Do Try Catch, Meta Type
//๋์ฝ๋ฉ ์ ๋ต
let decoder = JSONDecoder() //์์๋ก ๋ง๋ค์ด์ ๋์ฝ๋ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ
decoder.keyDecodingStrategy = .convertFromSnakeCase //์ค๋ค์ดํฌ์ผ์ด์ค์ ๋๋นํจ.
do {
let value = try decoder.decode(Quote.self, from: result)
print(value)
} catch {
print(error)
}
- https://app.quicktype.io/ : JSON์ ํ์์ ๋ฐ๋ผ struct ๊ตฌ์กฐ์ฒด๋ก ๋ง๋ค์ด์ฃผ๋ ์ฌ์ดํธ. JSON ๋งํฌ๋ฅผ ๋ฃ์ผ๋ฉด ๊ตฌ์กฐ์ฒด ํ์์ผ๋ก ๋ง๋ค์ด์ค๋ค.
//MARK: ์ด ๋ถ๋ถ์ ๋ํดํธ๊ฐ์ด welcome์ธ๋ฐ, ์์ฒ๋ผ ์ค์ ์ ๋ฐ๊ฟ ์๋ ์๋ค.
*๋ ์จ API JSON ์ฝ๋๋ฅผ ์ฝ๋๋ธ ๋ณํํ๋ฉฐ ์ดํดํ๊ธฐ*
- ์ด์ฌ์ ํน. ๊ตฌ์กฐ์ฒด ๋ง์์ง๊ณ ํ์ผ ๋๋๋ฉด ํ๋ฆ์ ๋ชป ์ฝ์. ๊ทธ๋์ ์์๋ก APIํต์ ์ผ๋ก ์ผ๋ weather ๋ก ์ฝ๋๋ธ ๊ตฌํ ํด๋ณด๊ธฐ๋ก.
a. SwiftJSON + Alamofire๋ก ์์ฑํ ์ฝ๋
let url = "https://api.openweathermap.org/data/2.5/weather?lat=44.34&lon=10.99&appid=\(APIKey.weatherKey)"
AF.request(url, method: .get).validate().responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
let temp = json["main"]["temp"].doubleValue - 273.15
let humidity = json["main"]["humidity"].intValue
let id = json["weather"][0]["id"].intValue//803
switch id {
case 800: print("๋งค์ฐ ๋ง์")
case 801...899:
self.weatherLabel.text = "๊ตฌ๋ฆ์ด ๋ ๋ ์จ์
๋๋ค"
self.view.backgroundColor = .lightGray
default: print("๋๋จธ์ง๋ ์๋ต...!!!")
}
self.tempLabel.text = "\(temp)๋ ์
๋๋ค"
self.humidityLabel.text = "\(humidity)% ์
๋๋ค"
case .failure(let error):
print(error)
}
}
b. ์ฝ๋๋ธ๋ก ๋ฐ๊ฟ์ฃผ๊ธฐ ์ ์, JSON ๊ฐ์ quicktype ํตํด ๊ตฌ์กฐ์ฒด๋ก ๊ฐ์ ธ์๋ค.
// MARK: - Weather
struct Weather: Codable {
let timezone: Int
let name: String
let sys: Sys
let wind: Wind
let base: String
let main: Main
let visibility: Int
let coord: Coord
let dt: Int
let clouds: Clouds
let id, cod: Int
let weather: [WeatherElement]
}
// MARK: - Clouds
struct Clouds: Codable {
let all: Int
}
// MARK: - Coord
struct Coord: Codable {
let lon, lat: Double
}
// MARK: - Main
struct Main: Codable {
let pressure: Int
let feelsLike, tempMin: Double
let humidity: Int
let temp: Double
let seaLevel, grndLevel: Int
let tempMax: Double
enum CodingKeys: String, CodingKey {
case pressure
case feelsLike = "feels_like"
case tempMin = "temp_min"
case humidity, temp
case seaLevel = "sea_level"
case grndLevel = "grnd_level"
case tempMax = "temp_max"
}
}
// MARK: - Sys
struct Sys: Codable {
let sunset, type, id: Int
let country: String
let sunrise: Int
}
// MARK: - WeatherElement
struct WeatherElement: Codable {
let icon, main, description: String
let id: Int
}
// MARK: - Wind
struct Wind: Codable {
let gust: Double
let deg: Int
let speed: Double
}
c. ์ฝ๋๋ธ๋ก ๋ฐ๊ฟ์ฃผ๊ธฐ
์ฐ์ , ์ฝ๋๋ธ ์ ์ฉ์ด ์ ์ดํด๊ฐ ๋์ง ์์์ ์์ ๋ ์ด ๋ก๋ ์ฝ๋๋ธ ์ฝ๋๋ฅผ ์ฐธ๊ณ ํ๋ค.
AF.request(url, method: .get).validate() // ์ฝ๋๋ธ๋ง๋ค ๋ ์ฌ์ฉ.
.responseDecodable(of: Lotto.self) { response in //์ฝ๋๋ธ๋ก response ๋ฐ์๋ (๋ก๋๋ผ๋ ๊ตฌ์กฐ์ฒด๋ก ๋ง๋ค๊ป)
guard let value = response.value else { return }
print("responseDecodable:", value)
print(value.bnusNo, value.drwtNo3) //self.text = value.bnusNo ๊ฐ์ด ์ฐ๋ฉด ๋จ.
}
๋ญ๊ฐ ๋ฐ๋์๋๊ฐ - ๋ฅผ ๋ณด์ํ๋,
1. validate().responseJSON์ด .resposeDecodable(of: ๊ฐ์ฅํฐ๊ตฌ์กฐ์ฒด.self)๋ก ๋ฐ๋์ด ์์๋ค.
JSON์ผ๋ก ๊ตฌํํ๋ ๊ฐ์ ์ด์ JSON์ด ์๋ ๊ตฌ์กฐ์ฒด๋ก ๊ฐ์ ๋ฐ๊ฒ ๋ค! ์ ๋๋ก ์ดํดํ๋ค.
2. value ๋ ๋์ฒด ๋ญ๊น? (์ ์ฝ๋ ์์๊ธฐ์ค) ๋ก๋์ ๊ฐ์ฒด๋ฅผ value๋ก ํ๊ฒ ๋ค ์ ๋.
d. JSON -> codable
let url = "https://api.openweathermap.org/data/2.5/weather?lat=44.34&lon=10.99&appid=\(APIKey.weatherKey)"
AF.request(url, method: .get).validate()
.responseDecodable(of: Weather.self) { response in
guard let value = response.value else { return }
switch response.result {
case .success(let value):
let temp = value.main.temp - 273.15
let humidity = value.main.humidity
let WeatherId = value.weather.first?.id ?? 0
switch WeatherId {
case 800:
print("๋งค์ฐ ๋ง์")
case 801, 802, 803, 804:
self.weatherLabel.text = "๊ตฌ๋ฆ์ด ๋ ๋ ์จ์
๋๋ค"
self.view.backgroundColor = .lightGray
default: print("๋๋จธ์ง๋ ์๋ต...!!!")
}
self.tempLabel.text = "\(temp)๋ ์
๋๋ค"
self.humidityLabel.text = "\(humidity)% ์
๋๋ค"
case .failure(let error):
print(error)
}
}
- ์ด๊ฒ ์ด์ฐ์ ์ฐ ๋ณํ์ ํด๋ณธ ๊ฑด๋ฐ, ๊ตฌ์กฐ์ฒด๋ฅผ ์ฝ์ด๋ผ ์ค ์๋๊ฒ ์ ๋ง ์ค์ํ ๊ฒ ๊ฐ๋ค.
- ๊ธฐ์จ(temp)๋ value(Weather)์ ์๊ณ , Weather - main - temp์ ์๊ธฐ ๋๋ฌธ์ value.main.temp ์ด๋ฐ ์์ผ๋ก ๊ฐ์ ์ฐพ์๋ด์ผํ๊ธฐ ๋๋ฌธ์ด๋ค. (๊ทธ๋ฆฌ๊ณ value. ํ๋ฉด ์๋์์ฑ์ด ์ ๋์ด์ ๋ด๊ฐ ์ง์ง ์ฐพ์์จ์ผํจ)
-์ ์ผ๋๋ 8/17 ์์ ์์ weather๋ก ์ฝ๋๋ธ ์ฐ์ต์ ํ๋ค. ๋ด๊ฐ ํ๊ฑธ ๋ณด๊ณ ์, ๋ด๊ฐ ๋ ๋ณต์กํ๊ฒ ํ๋ค... ์ถ์๋ค. ๋ด๊ฐ ํ๊ฑด ์ฌ์ค JSON์ ๋ง์ถ ๊ฑธ ๊ฑฐ์ ๋ผ์๋ง์ถ๋ฏ ์ฝ๋๋ธ๋ก ๋ฐ๊พผ ๊ฐ์ผ๋ก ๊ต์ฒด ์ธ๋ฏ.
๐ 5์ฃผ์ฐจ ๊ธฐ์ค ์ฐcodable ๋ฐฉ์_(์๋ง๋)์ต์ข
a. JSON -> Codable ์์
1. { } ๋ฅผ ์ด์ฉํ ๊ตฌ์กฐ์ฒด ์์ฑ. ํตํ์ ์ด์ฉํ๋ฉด ํธํ๋ค. (+) ํตํ์ ์ด ๋ง๋ค์ด์ค ๊ตฌ์กฐ์ฒด๊ฐ 100% ์๋ ๋๋ ๊ฑด ์๋๋ผ์ ๋๋ฒ๊น ์์ญ ํ์ธ ํด์ฃผ์ด์ผํ๋ค. ex) ์ํ Poster ์ด๋ฏธ์ง ๊ฐ์ ๊ฒฝ์ฐ ์ค๋ ๋ ์ํ๋ ์ข ์ข ์ด๋ฏธ์ง๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋๊ฐ์ ๋ฐ์ ์ ์๋๋ก String? ๊ฐ์ด ๋ฐ๊ฟ์ฃผ์ด์ผํจ.
2. ๊ตฌ์กฐ์ฒด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก alamofire๋ฅผ ํ์ฉํด ์ฝ๋๋ธ์ ์งํ.
์์*
class DummyManager {
static let shared = DummyManager()
func callRequestDummy(id: Int, page: Int, competionHandler: @escaping (Dummy) -> Void, failure: @escaping () -> Void ) {
let header: HTTPHeaders = [
"Authorization" : "Bearer \(APIKey.dumAPI)",
"accept" : "application/json"
]
let url = "https://api.dummy.org/dumdum/\(id)/dummy?api_key=\(APIKey.dumAPI)&language=ko-KO&page=\(page)"
AF.request(url, headers: header).validate(statusCode: 200...500).responseDecodable(of: Dummy.self) { response in
switch response.result {
case .success(let value):
competionHandler(value)
case .failure(let error):
failure()
print(error, "๋๋ฏธ ํต์ ์ค๋ฅ")
}
}
}
}
- ๋ณดํต .get์ด ๋ํดํธ๋ก ์ ์ฉ๋๊ธฐ ๋๋ฌธ์ get์ผ๋ก API๋ฅผ ์ฌ์ฉํ ๋ ์๋ต์ด ๊ฐ๋ฅ.
- competionHandler: @escaping (Dummy) -> Void, failure: @escaping () -> Void : ํต์ ์ด ์ ๋๋ฉด Dummy ๋ด์ฉ์ ๊ฐ์ ธ์
3. ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ ํด์ฃผ๊ธฐ
3-1. ํด๋นํ๋ ๋ฉ์ธ ๋ทฐ์ปจํธ๋กค๋ฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค ๊ทธ๋ฆ ์์ฑ.
var dummyrList : Dummy = Dummy(page: 0, results: [], totalPages: 0, totalResults: 0)
3-2. ์๋์ ๊ฐ์ด ์ฌํ ํ ๋น๋์ ํจ์๋ค์ ํ์ฉํ๋ฉด ์ปฌ๋ ์ ๋ทฐ์ ๋คํธ์ํฌ ํต์ ์ ํตํด์ ์ป๋ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋๋ค.
func fetchDataDummy() {
DummyManager.shared.callRequestUpcoming(page: page) { data in
print(data)
self.DummyList = data
self.DummyCollectionView.reloadData()
} failure: {
print("error")
}
}
3-3. ํ์์ ๋ฐ๋ผ dispatchGroup์ ์ด์ฉํด์ ํจ์จ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ธฐ๋ ํด์ผํจ. ๊ทธ ๊ณผ์ ์.. ์ฌ๊ธฐ์ ์๋ต.
โ๏ธ Do-Try-Catch
- when? ๋คํธ์ํฌ ํต์ ์ด ์๋๋ค๊ฑฐ๋, ํ์ผ ๋ค์ด๋ก๋๊ฐ ์ํํ์ง ์๋ค๊ฑฐ๋ ํ๋ ์ ๋งคํ ์ํฉ ๋ฐ์ (์์ธ์ฒ๋ฆฌ)๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด ์ฌ์ฉํ๋ค. ์ ์์ ์ธ ํ์์์ ๋ฒ์ด๋ ์ ํ์ด ์ด์ฉ ์ ์์ด ์ผ์ด๋ ๋๋ฅผ ์ํ ๋์.
- catch ๋ถ๋ถ์ if-else์ else ์ญํ ๊ณผ ๋น์ทํ๋ค.
๊ทธ๋ ๋ค๋ฉด ์์ฑ๋ฒ์ ์ด๋ป๊ฒ ๋ ๊น? ์์ ์ฝ๋๋ฅผ ์กฐ๊ธ ๋ง์ด...ใ ใ ๊ฐ์ ธ์๋ค.
a. Error ํ๋กํ ์ฝ ๋ถ๋ฌ์ค๊ธฐ
enum ValidationError : Error { //error ํ๋กํ ์ฝ ์ํ. 401๊ฐ์ ๋ณดํธ์ ์๋ฌ๊ฐ ์๋ ์๋ฌ๊ธฐ๋ฅ์ ํ์ฅํ ๋.
case emptyString
case isNotInt
case inNotDate
}
enum์ CaseIterable - allCases ๊ฐ์ด Error ํ๋กํ ์ฝ์ ๋ถ๋ฌ์์ผํ๋ค.
b. ์ค๋ฅ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฒ์ธ์ง ์ ํด์ฃผ๋ ํจ์ ์์ฑ
func validateUserInputError(text: String) throws -> Bool {
//๋น ์นธ์ผ ๊ฒฝ์ฐ
guard !(text.isEmpty) else{
print("๋น ๊ฐ")
throw ValidationError.emptyString
}
//์ซ์ ์ฌ๋ถ
guard Int(text) != nil else {
print("์ซ์ ์๋")
throw ValidationError.isNotInt
}
//๋ ์ง ํ์์ผ๋ก ๋ณํ์ด ๋๋ ์ง
guard checkDateFormat(text: text) else {
print("์๋ชป๋ ๋ ์ง ํ์")
throw ValidationError.inNotDate
}
return true
}
- ์ค๋ฅ๋ throws ๋ฅผ ์ฌ์ฉํ๋๋ฐ, "๋์ง๋ค"๊ณ ํํํ๋ค. throw = return ๊ฐ์ ์ญํ ์ธ๋ฐ, return์ ์ฌ์ฉํ์ง ์์ ์ ์๋ ์ด์ ๋ ๋ช ์์ ์ผ๋ก ์ค๋ฅ๋ฅผ throw๊ฐ ์ธ์ ์ํฌ ์ ์์ด์์ด๋ค.
- bool ๊ฐ์ผ๋ก ๋ฐ์์ค๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ์๋ ๊ฒฝ์ฐ์ ๋ํด์๋ return true๋ก ๋ฐํํ๋ ๊ฒ๊น์ง ์จ์ฃผ์ด์ผํจ. (์ ์จ์ค๋ ์์ค์ฝ๋๊ฐ ์์์ ๋นจ๊ฐ์ ์ค๋ฅ๋ฅผ ๋์์ค๋ค. ์ด์ฐจํผ ์จ์ผํจ.)
c. ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋ ํจ์ ์์ฑ
@IBAction func checkButtonClicked(_ sender: UIButton) {
guard let text = dateTextField.text else { return }
do {
let result = try validateUserInputError(text: text)
} catch {
print("Error")
}
โ๏ธ 8/16์ผ ๊ธฐ๋ก
- ์ฌ์ค ์ด๋ถ๋ถ์ ์ง๊ธ (8/16) ๋ค๋ง๊ณ ์น ํ๋ก์ ํธ์ ๋ฆฌํฉํ ๋ง์ผ๋ก ์จ๋ณด๋ฉฐ ์ค์ ์ ์ฉ์ด ์ด๋ป๊ฒ ๋์ด์ผ ์ฌ๋ฐ๋ฅด๊ฒ ๋๋ ๊ฑด์ง ๊ฐ์ด ์ ์กํ๊ธฐ๋ ํ๋ฐ, ์ค๋ ์์ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ ๋ฆฌ.
- ๋งค๊ฐ๋ณ์๊ฐ ์์๊ธฐ ๋๋ฌธ์ ๋งค๊ฐ๋ณ์ ๊ฐ์ ์ง์ ํด์ฃผ๊ณ , do { try }์ ์๋ฏธ๋ ๊ฐ๋จํ๊ฒ "์ผ๋จ ์ค๋ฅ๊ฐ ์๋์ง result ์์ ๊ฐ์ ๋ฃ์ด์ ํ์ธํด๋ณผ๊ฒ." ์ด๋ค. try = ์ค๋ฅ ํ ์คํธ ์์.
- callRequest ํจ์ ํธ์ถ์ Do ํจ์ ์์ ์คํํ๊ธฐ๋.
- catch๋ ์ค๋ฅ๊ฐ ๋๋ฉด ์ด๋ค ์ก์ ์ด ๋์ค๋์ง (์ด๋ป๊ฒ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ ๊ฒ์ธ์ง) ๋ํ๋ด๋, ์กฐ๊ฑด๋ฌธ์ if-else์์ else ์ญํ . ๋ณดํต ์ผ๋ฟ์ด๋ rawValue๊ฐ์ผ๋ก ๋ํ๋ธ๋ค.
- catch isNotInt { } catch emptyString {} ์์ผ๋ก ์ธ ์ ์์ง๋ง, ์ฒ๋ฆฌ๊ฐ์ด ๋๊ฐ์ ๊ฒฝ์ฐ์ ๊ทธ๋ฅ ํ๋์ catch { } ์์ผ๋ก ์จ๋ ๋๋ค.
โ๏ธ CollectionReusableView
tableview๋ collectionview๋ ๋ณดํต class์ด๋ฆ์ด ์์ฑ์ด๋ฆ์ผ๋ก ์๋ ์์ฑ๋์ด์์ด์ identifier ์ด๋ฆ๋ง ์ค์ ํด์ฃผ๋ฉด ๋์๋๋ฐ, ์ด๋ฆ์ collection์ด ๋ค์ด๊ฐ์์์๋ ๋ถ๊ตฌํ๊ณ collectionReusableView๋ ์๋์์ฑ์ด ์๋๋ค.
โ๏ธ group - enter - leave
- API ๋คํธ์ํฌ ํต์ ์ ํ๋ ๋ทฐ๊ฐฑ์ ์ ๊ณ๋จ์์ผ๋ก ํด๋ก์ ์์ ํด๋ก์ ์์ ... ์์ ํต์ ํ๋ฏ dispatchGroup ์์ด ํ๋ฉด, ํต์ ์์๋ฅผ ์๊ธฐ ์ด๋ ต๊ณ ์์ ์ฑ์ด ๋ณด์ฅ๋์ง ์์. dispatchGroup์ ํ์ฉํ๋ฉด ๋์์ ์ผ์ด๋๋ ์ฌ๋ฌ ๋คํธ์ํฌ ํต์ ์ ์ฝ๋์ ์ผ๋ก๋ ์ค์ง์ ์ผ๋ก๋ ๊ด๋ฆฌํ๊ธฐ ํธ๋ฆฌํด์ง.
- DispatchGroup: ์ฌ๋ฌ task๋ฅผ ๊ทธ๋ฃนํ ํด์, ์ผ์ฒ๋ฆฌ๊ฐ ๋๋๋ฉด notify๋ก ์๋ฆผ์ ๋ฐ๊ฒ ํ๋ ๊ธฐ๋ฅ.
- ๋น๋๊ธฐ ํจ์๊ฐ ๊ทธ๋ฃน์ผ๋ก ๋ฌถ์ด๋ฉด ์ผ์ฒ๋ฆฌ ๊ณผ์ ์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ฐ๋ก notify๋ฅผ ๋์ธ ์ ์์. (ex. ์์ ์๋ฃ ์์๊ฐ notify - task 1 - task 3- task4)
- ์์ ๊ฐ์ ํธ์ถ ๋ฌธ์ [๋ชจ๋ task๊ฐ ๋๋๋ฉด notify ์๋ํ๊ธฐ]๋ฅผ ์ ๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉํ๋๊ฒ group - enter - leave.
- let group = DispatchGroup์ผ๋ก ์ธํ ์์.
- enter - leave์ ๊ฐฏ์๋ ์ง์ด ๋ง์์ผํจ. (๊ฐ๊ฐ +1, -1์ธ ๊ด๊ณ์ฌ์, 0์ด ๋ ๋ ์ ํธ๋ฅผ notify๊ฐ ์ฒ๋ฆฌํด์ค๋ค.
let group = DispatchGroup ()
group.enter() //+1
callData(id: id) { data in
self.list = data //๋ฐ์ดํฐ ์
๋ฐ์ดํธ
print("==1===")
group.leave() // -1
}
group.notify(queue: .main){ //(enter+leave = 0)์ด ๋๋ ์๊ฐ notify๊ฐ ๋๋ค.
print("===END")
self.Collectionview.reloadData()
}
๐ช ๋ณต์ต
- ํ ์์ ์์ ๋ด๊ฐ ์์ฑ ํ ๋๋ง๋ค ์ต๊ด์ ์ผ๋ก ๋ฏธ๋ฃจ๋ ค๊ณ ํ ๋งํผ ์ด์ํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ ๋ ๊ผญ ํ๋์ค์ฉ ๋นผ๋จน์ด์ ์ด๋ ค์์ ๋๋ผ๋ ๋ถ๋ถ ๋ชจ์. ์๋ง ํํ๋ ๋~์ถฉ ์ดํดํ ๊ฒ ๊ฐ์๋ฐ ์ ํํ ํ๋ฆ์ ๋ชจ๋ฅด๋ ๊ฒ ๊ฐ๋ค๊ณ ๋๊ผ๋ค.
โ๏ธ ๋ทฐ์ปจํธ๋กค๋ฌ๋ผ๋ฆฌ ๋ฐ์ดํฐ ์ ๋ฌํ๊ธฐ w/ tableView
- ๊ณผ์ ํ๋ฉด์ ์ ๋ง ๋ง์ด ์์ฑํ๊ฒ ๋๋ ๊ตฌ์กฐ์ธ๋ฐ, ์์ฑํ ๋๋ง๋ค ์ค๊ฐ๋ถํฐ ํค๋งจ๋ค. ์ง์ง ๊ผญ ํ ๋์ค์ ๋ชป ์จ์ ์ ๋ฌ์ด ์๋ ๐ ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ ์กฐ๊ธ์ฉ ๋ฐ๋๋ ๋ถ๋ถ์ด๋ผ๋๊ฒ ... ๊ฒฐ๊ตญ ๋ด๊ฐ ์ดํด ๋ชปํ๋ค๋ ๋ป์ด๊ฒ ์ง. ํนํ ์์ฑํ ๋๋ง๋ค ์ ์ ๋๋ฉด ๊ณผ๊ฑฐ ๊ณผ์ ๋ค์ ์ฐธ๊ณ ํด์ ๋ ์์๊ฐ ๊ผฌ์ด๋ ๊ฒ ๊ฐ๋ค.
a. A viewController์ B viewController ์ฐ๊ฒฐ.
struct Team {
var name : String
var description : String
}
class A ViewController: UIViewController {
var teamList : [Team] = []
}
///////////////
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let B = self.storyboard?.instantiateViewController(withIdentifier: "BViewController") as? BviewController else { return }
self.navigationController?.pushViewController(B, animated: true)
}
A viewController์์ tableview cell์ ๋๋ฅผ ๋ B viewController๋ก ํ๋ฉด์ ํ์ด ์ผ์ด๋๋ค๋ฉด, ๋น์ฐํ B viewController๋ฅผ A viewController์ tableView - didSelectRowAt ํจ์์ ์ฐ๊ฒฐ.
b. B viewController์์ A viewController๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ๋ฐ์ ๋น ๊ณต๊ฐ์ ๋ง๋ค์ด์ค๋ค.
var teamName = ""
var teamDescription = ""
(*๋ด๊ฐ* ์ฝ๋ ์ง๋ ๊ฒ์ ์ฌ๋ฏธ๋ฅผ ๋๋ผ๊ธฐ ์ํด ๋ธ๋ก๊ทธ๋ด ๋ชจ๋ ์ฝ๋์์ฑ์ ๊ทธ๋ฅ ์ผ๊ตฌ ๊ด๋ จ์ผ๋ก ์ ๋ ์ค. ๋์ถฉ ํ ์ด๋ฆ๊ณผ ๋ก๊ณ ๊ฐ ์๋ ์ ์ ์ ํํ๋ฉด ํ ์ด๋ฆ๊ณผ ํ ์ค๋ช ์ด ์๋ ํ์ด์ง๋ก ๋์ด๊ฐ๋ ๊ฐ์ .)
๋ณดํต viewDidLoad ์์ ์ ์ด์ค.
c. B viewController๋ฅผ ์ฐ๊ฒฐํด์ค A viewControleller-didSelecteRowAt ์ index.row์ ๋ง์ถฐ ๋ฐ์ดํฐ ์ ๋ฌํด์ค ์ค๋นํ๊ธฐ.
A viewController์ ์ ๋ณ๋ก ๋์จ ๋ฐ์ดํฐ์ B viewController์์ ์์ธํ์ด์ง๋ก ๋ฐ์๋ณผ ์ ๋ณด๊ฐ ๊ฐ์์ผํ๊ธฐ ๋๋ฌธ์ index.row ์ธํ ์ ํด์ฃผ์ด์ผ ํจ.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let B = self.storyboard?.instantiateViewController(withIdentifier: "BViewController") as? CreditViewController else { return }
self.navigationController?.pushViewController(B, animated: true)
let selectTeam = teamList[indexPath.row] // ํ ๋ฆฌ์คํธ ์ธ๋ฑ์ค์ ๋ง์ถฐ์ ๋งค์นญ ์ธํ
B.selectTeam = teamList[indexPath.row] // ํ ๋ฆฌ์คํธ๋ BviewController๋ ๋ง์ถค
}
์ฌ๊ธฐ๊น์ง ์ธํ ์ ํด์ฃผ๋ฉด ์ด์ B viewController์ ๋ง๋ค์ด์ค ๋น๊ณต๊ฐ๋ค์ ๊ฐ์ ๋ฃ์ด์ค๋ค.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let B = self.storyboard?.instantiateViewController(withIdentifier: "BViewController") as? BViewController else { return }
self.navigationController?.pushViewController(B, animated: true)
let selectTeam = teamList[indexPath.row]
B.selectTeam = teamList[indexPath.row]
B.teamName = teamList[indexPath.row].name
B.teamDescription = teamList[indexPath.row].description
}
๋์ถฉ ์ฝ๋๋ฅผ ์ดํดํ์๋ฉด :
- A viewController์์ ์ ์ ๋๋ฅด๋ฉด,
(1) B viewController๋ก pushํํ๋ก ํ๋ฉด์ด ์ ํ
(2) selectTeam์ teamList๋ฅผ A์ index.row์ ๋ง์ถ ๊ณต๊ฐ์ ๋ง๋ค์์ด
(3) B viewController์์ selectTeam์ ์ด์ฉํด์ index.row๋ก ๊ฐ์ ๋งค์นญํ ๊ฒ
(4) B viewController์ ๋ง๋ค์ด๋ ๋น๊ณต๊ฐ์ teamList๊ฐ์ผ๋ก ๋ฐ์๊ฒ. (ํต๋ก๊ฐ ๋์ด์ค๊ฒ)
d. ๋น๊ณต๊ฐ์ ๋ฃ์ ๊ฐ์ ์ด์ B viewController ์์ธ๋ ์ ๋งค์นญํด์ฃผ๊ธฐ.
โญ๏ธ ๊ผญ ์ด๋ถ๋ถ์ ๊น๋จน๋๋ค. ๊ณต๊ฐ์ ๊ฐ์ ๋ฐ์์ผ๋๊น ๊ทธ ๊ฐ์ ๋ณด์ฌ์ค ๊ณณ์๋ ๋ฃ๊ธฐ.
var selectedTeam : Team? //viewDidLoad, outlet์ฐ๊ฒฐ๋ณด๋ค ์์ ์ ์ด๋ .
///////////
@IBOutlet var : UILabel!
@IBOutlet var descriptionLabel: UILabel!
//////////
func getData() {
guard let selectedTeam = selectedTeam else {
return
}
teamLabel.text = "\(teamName)"
descriptionLabel.text = "\(teamDescription)"
}
์ด๋ ๊ฒ ์์ฑํ getDataํจ์๋ฅผ ํธ์ถํด์ฃผ๋ฉด ๊ฐ์ด ํ ์ด๋ธ๋ทฐ ์์์ ์๋ง์ ๋ฐ์ดํฐ๊ฐ ๋งค์นญ๋์ด ์์ธํ์ด์ง๋ก ๋์ด๊ฐ์ ํ์ธํ ์ ์๋ค.
โ๏ธ Pagenation
- ์นด์นด์ค API ํต์ ์ฝ๋ ๊ฐ์ ํ๋ค๊ฐ ๊นจ๋ฌ์ ๊ฒ, ์ฌํ pagenation ์ด ํ์ํ๋ฉด ์์ ๋ ํ๋ฒ ์ด ์ฝ๋๋ฅผ ๊ณ์ ๋๊ฐ์ด ์ฐ๊ณ ์์๋ค. ๋๋ถ์ pagenation ํ๋ผ๋๊น ๊ทธ๊ฑฐ ๊ทธ๋ฅ page 1์ด๊ณ .... ์ ์ฒด ํ์ด์ง 15๋ก ๋ง์ถฐ์ ์ฐ๋ ๊ทธ๋ฐ ๊ฑฐ ์๋์ผ? ํ๊ฒ ๋๋ ์ํฉ์ ๋๋ฌ. ์ง๊ธ ์๊ฐํ๋ ๊ทธ ์ฉ๊ธฐ์ ํฉ๋นํจ.
- ํ์ด์ง๋ค์ด์ ์ ํ๋ ์ด์ : ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ๋ ๋๋ฌด ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ ์๋ฒ(๋ฉ๋ชจ๋ฆฌ)๋ฅผ ๋ถ์์ ํ๊ฒ ๋ง๋ค๊ฑฐ๋, ์ ์ ๊ฐ ๊ทธ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ํ์ง ์์ ์๋ ์๊ธฐ ๋๋ฌธ์ ํจ์จ์ ์ด์ง ๋ชปํจ. ์ ์ ๋ 12๊น์ง๋ง ์๊ณ ์ถ์๋ฐ ๋ฐ์ดํฐ๋ฅผ 20๊น์ง ๋ฐ์์จ๋ค๋ฉด? ๊ฐ์ ์ํฉ.
- ํ์ด์ง๋ค์ด์ ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ UITableViewDataSourcePrefetching ํ๋กํ ์ฝ ํ์ฉ
- prefetchRowsAt
- ๋ง์ง๋ง ์คํฌ๋กค ์์ ์ ํ์ธํด์ ๋คํธ์ํฌ ํต์ ์ ์ ๋ฐ์ดํธ ํด์ฃผ์ด์ผํจ,
- ๋ฐ์ดํฐ๋ฅผ ๋ด์์จ list์ ์ธ๋ฑ์ค ์๋ฅผ ๋ง์ถ ๊ฒ,
- cellForRowAt์ด ํธ์ถ ๋๊ธฐ ์ ํ์ํ ๋ฐ์ดํฐ๊ฐ ๋จผ์ ๋ก๋ฉ ๋จ,
- ํด๋น ํ๋กํ ์ฝ์ ํ์ ๋ฉ์๋์.
-์ทจ์ํ๋ ๊ธฐ๋ฅ(cancelPrefetchingForRowsAt)์ ๋ณ๋๋ก ์ ๋ ฅํด์ฃผ์ด์ผํจ. (ํ์ X)
- ์ ์ ๊ฐ ์คํฌ๋กค์ ๋น ๋ฅด๊ฒ ํ๋ ๋ฑ ๋ณด์ฌ์ค ๋ฐ์ดํฐ๊ฐ ๋์ด์ ์์ ๋ ์ด ๋ฉ์๋๊ฐ ํธ์ถ๋๊ณ , ์ถ๊ฐ์ ์ธ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ๋ฅผ ์ทจ์์ํด.
- offsetPagenation : ํ์ด์ง์ ๋ช ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค์ง ์ ํ๋ ๋ฐฉ์์ผ๋ก, ๋ฐ์ดํฐ ๋ณํ์ ์ ์ฐํ์ง ๋ชปํจ (ํ ํ์ด์ง์ ์ค๋ณต ๋ฐ์ดํฐ ๋ ธ์ถ)
- cursorPagenation : ์ ์ ์ ๋ง์ง๋ง ๋ฐ์ดํฐ ๊ธฐ์ค์ผ๋ก ๋ค์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๋ฐฉ์, ํน์ ๋ฐ์ดํฐ(ํ์ด์ง)๋ฅผ ๋ณด๊ธฐ๊ฐ ์ด๋ ค์.
โ๏ธ userDefaults
- ๋ค๋ง๊ณ ์น ํ๋ก์ ํธ ๋ฆฌํฉํ ๋ง์ ์งํํ๊ณ , ํ์ ์์ ์์ฃผ ์ฐ์ธ๋ค๋ ํน๊ฐ์ ๋ฃ๊ณ ๋ ํ userDefaults์ ๊ธฐ๋ณธ์ ์ง๊ธ ๊ฝ ์ก์ง์์ผ๋ฉด ํฐ์ผ ๋๊ฒ ๊ตฌ๋ ์ถ์๋ค. ํนํ ๋ ์ง๊ธ.. ์ฝ๊ฐ ์ ์๊ณ ์ธ์๊ฐ ๊ณ ์น๋ ์คํ์ผ๋ก ์ง๋๋ฅผ ์ซ์๊ฐ๊ณ ์๊ธฐ ๋๋ฌธ์.
- [ํค์ ๊ฐ์ ์ ์ฅํ๊ณ , ๋ถ๋ฌ์ค๊ณ , ์คํํ๋ค]๋ ์ด ๋จ์ํ ๊ณผ์ ์ด ๋จธ๋ฆฌ๋ก๋ ์ดํด๊ฐ ๋๋๋ฐ, ์ด๋ป๊ฒ ์์ฑํ๋์ง๋ ์๊ฒ ๋๋ฐ, ๊ตฌํ์ด... ์ผ๋ ๋ฑ๋ ์ด๋ผ ์ฌ์ฉํด์ผํ ๋๋ง๋ค ์ ๋ฆฌ๊ฐ ๋์ง ์์๋ค. ์ค๊ตฌ๋๋ฐฉ์ผ๋ก userDefaults๋ฅผ ์์ฑํ๋ ๋.... ๋๋ถ์ ์จ๋๊ณ ๊ตฌํ์ ์ํด๋๋ค๊ฑฐ๋ ํ๋ ์ํฉ์ด ๋๋ฌด!! ์์ฃผ!! ๋ฐ์ํจ (ex. ๋ค๋ง๊ณ ์น ์ด๋ฆ ๋ณ๊ฒฝํ๊ธฐ).
- UserDefualts๋ ์ ์ ๊ฐ ์ ํํ๊ฑฐ๋ ๋ณ๊ฒฝํ ์ค์ (๋๋ค์, ์ด๋ฉ์ผ, ์ฑ๋ณ, ๊ธฐ๋ฅ์ค์ ๋ฑ)๋ด์ฉ์ ์๊ตฌํ ์ ์ฅํ๋ ๊ธฐ๋ฅ์ ์ํํ๋ฉฐ, ์ ์ฅํ ๊ฐ์ ์ผ๋ช sandbox๋ผ ๋ถ๋ฆฌ์ฐ๋ ์ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ณด๊ดํ๋ค. ์ ์ฅ๋ ์ ๋ณด๋ฅผ ์ง์ฐ๋ ค๋ฉด ์ฑ์ ์ง์ฐ๊ฑฐ๋ ์ด๊ธฐํ๋ฅผ ์ ์ ๊ฐ ๋ณ๋๋ก ํด์ฃผ์ด์ผํ๋ค.
- key-value ํ์์ผ๋ก ์ฌ์ฉ๋๋ฉฐ, ํ๋ฒ ์์ฑํ๋ฉด ๋ค๋ฅธ ๊ณณ์์๋ ๊ทธ ๊ฐ์ ๋ถ๋ฌ์ ์ธ ์ ์์. (A Viewcontroller์ ์์ฑํ ์ ์ ๋ํดํธ๊ฐ์ B viewController์ ๋ถ๋ฌ์ ์์ ํ ์ ์์.)
*์ ์ฅ : set
userDefaults.standard.set ( ๋ฐธ๋ฅ๊ฐ์ string, Int, bool ๋ชจ๋ ๊ฐ๋ฅ(๋ฐธ๋ฅ์ ๋ค์ด๊ฐ ๋ด์ฉ์ ๋ด๋น), "ํค๊ฐ์ ์คํธ๋ง")
- ์ ์ฅํ ๋ set์ ์ฌ์ฉํด์ ๊ฐ์ ์ ์ฅํจ.
*๋ถ๋ฌ์ค๊ธฐ : let ์ผ๋ก ์ํ
์์)
userDefaults.standard.set(textField.text,"teamName")
if let myTeam = UserDefaults.standard.string(forKey: "teamName") {
self.title = "\(myTeam) ์์ํ๊ธฐ"
}
- set์ ์ ์ฅํ ๊ฐ์ด string, Integer, bool์ธ์ง์ ๋ฐ๋ผ ๊ฐ์ ธ์ฌ ๋ ์์/๋ณ์ ์ ์ธ ํ ๊ฑฐ๊ธฐ์ ๋ง๊ฒ ๊ฐ์ ๋ถ๋ฌ์์ ์ฌ์ฉํ๋ฉด ๋จ.
*์ด๊ธฐํ : removeObject & synchronize()
์์)
func resetUserData() { //์ด๊ธฐํ ํจ์ ๋ง๋ค๊ธฐ
UserDefaults.standard.removeObject(forKey: "teamName")
UserDefaults.standard.synchronize()
}
- ์ด๊ธฐํ๊ฐ userDefaults ์์ ๊ฐ์ฅ ์ฌ์ด๋ฐ, ์ด๊ธฐํํ๊ณ ์ ํ๋ ๋ด์ฉ์ ํค๋ก ๋ถ๋ฌ๋ด์ ์ด๊ธฐํ ํ๋ ํจ์๋ฅผ ์์ฑ, ๊ทธ๋ฆฌ๊ณ ์ด๊ธฐํ ์ก์ ์ด ์ผ์ด๋๋ ํจ์์ ์ด๊ธฐํ ํจ์๋ฅผ ๋ถ๋ฌ๋ด๋ฉด ๋๋ค.
โ๏ธ didSelectRowAt์์ tableview.reloadData()๊ฐ ๋จนํ์ง ์์ ๋
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.selectionStyle = .none
return cell
}
๋ชจ์ข ์ ์ด์ ๋ก tableView.reloadData()๊ฐ ์ํ๋ ์๋๋๋ก ๋์ง ์๋๋ค๋ฉด, cellForRowAt์์ ์ค์ ์ ์ง์ ํด์ฃผ๊ธฐ.
selectionStyle = .none์ผ๋ก ํด์ฃผ๋ฉด ๋๋ค.
โป๏ธ 5์ฃผ์ฐจ ๊ฐ์ ํ ๋ถ๋ถ ๋ชจ์
- ๋ค๋ง๊ณ ์น ํ๋ก์ ํธ : (???: ๊ทธ๋ฅ ๋ค์ ๋ง๋ ๊ฑฐ ์๋์์?)
- ์ผ๋ถ ์กฐ๊ฑด๋ฌธ -> switch๋ฌธ์ผ๋ก ์์ ,
- ์ ๋ฐ์ ์ธ ๋ํ ์ผ ์ค์ ์์
- ์ค๋ฅ ์๋ฆผ ๋์ฐ๊ธฐ (๊ธด๊ฐ๋ฏผ๊ฐ..),
- ์ผ๋ฐ ์๋ฆผ ๋์ฐ๊ธฐ (notificationCenter ํ์ฉ)
๊ทธ๋ ๊ทธ๋ ์์ ์ ๋ฐฐ์ด ๊ฑฐ ์ด์คํผ๋ ์ผ๋จ ์ ์ฉํด๋ณด๊ธฐ + ์๊ฐ์ด ์ง๋ ์์ /๊ณผ์ ๋ก ๋ช ๋ฒ ์ฌ์ฉํด๋ด์ ์ดํด๊ฐ ์กฐ๊ธ ๋ ์ฝ๋๋ก ์์ ํ๋ ๋ฐฉ์์ผ๋ก ์์ ํ๊ณ ์๋ค. ๊ทธ๋๋ ใ ใ ์ ํ ์ฝ๋๊ฐ ๋ฉ๋ฏธ ๋๊ฒ ๋๋ฌด ๊ธธ์ด์ ์๊ธด...^^...
- ์นด์นด์คAPI ์ฝ๋๋ธ:
- JSON ํ์์ ์ฝ๋๋ธ๋ก ๊ฐ์
'๐ฑ SeSAC iOS 3๊ธฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๐ฑ ์์น ์๋ฑํฌ iOS ์ฑ๊ฐ๋ฐ์ ๋ฐ๋ท๊ณผ์ 3๊ธฐ ํ๊ธฐ (3) | 2024.06.25 |
---|---|
[SeSAC] iOS ๊ฐ์ธ์ฑ <์ง๊ด๋ก๊ทธ> ์ถ์ ํ๊ณ (10) | 2023.10.24 |
๐ฑ 7์ฃผ์ฐจ: URLSession, Access Control, ARC (4) | 2023.09.04 |
๐ฑ 6์ฃผ์ฐจ: NotificationCenter, CLLocationManager, MKMapView, UIPageViewController (1) | 2023.08.27 |
๐ฑ 4์ฃผ์ฐจ: ๋คํธ์ํฌ ํต์ (0) | 2023.08.13 |