๐ŸŒฑ 5์ฃผ์ฐจ: API ํ†ต์‹ , Codable, DispatchGroup, ๋ณต์Šต

2023. 8. 20. 23:26ยท๐ŸŒฑ SeSAC iOS 3๊ธฐ

 

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๊ธฐ ํ›„๊ธฐ  (5) 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
'๐ŸŒฑ SeSAC iOS 3๊ธฐ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [SeSAC] iOS ๊ฐœ์ธ์•ฑ <์ง๊ด€๋กœ๊ทธ> ์ถœ์‹œ ํšŒ๊ณ 
  • ๐ŸŒฑ 7์ฃผ์ฐจ: URLSession, Access Control, ARC
  • ๐ŸŒฑ 6์ฃผ์ฐจ: NotificationCenter, CLLocationManager, MKMapView, UIPageViewController
  • ๐ŸŒฑ 4์ฃผ์ฐจ: ๋„คํŠธ์›Œํฌ ํ†ต์‹ 
Callie_
Callie_
  • Callie_
    CalliOS
    Callie_
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ
      • ๐ŸŽ APPLE
      • ๐ŸŽ Dev
        • Swift
        • UIKit
        • SwiftUI
        • Issue
        • ๊ตฌํ˜„
      • ๐ŸŽ Design
        • HIG
      • โš™๏ธ CS
      • ๐Ÿ’ก ์•Œ๊ณ ๋ฆฌ์ฆ˜
        • ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค
        • ๋ฐฑ์ค€
      • ๐ŸŸ๏ธ ์ง๊ด€๋กœ๊ทธ (์ถœ์‹œ์•ฑ)
        • ์—…๋ฐ์ดํŠธ
      • ๐ŸŒฑ SeSAC iOS 3๊ธฐ
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    addTarget
    clipsToBound
    SeSAC
    DiffableDataSource
    ios
    modalPresentStyle
    Snapshot
    IBOutlet
    SwiftUI
    Swift
    DidEndOnExit
    Infoํƒญ
    assets
    CocoaTouchFramework
    stroyboard
    Enum
    CustomView
    Entry Point
    .fullScreen
    TapGestureRecognizer
    ๋„คํŠธ์›Œํฌํ†ต์‹ 
    cornerradius
    TableViewCell
    layer.shadow
    diffable
    ์ƒ๋ช…์ฃผ๊ธฐ
    IBAction
    ํ™”๋ฉด์ „ํ™˜
    .OverFullScreen
    apply
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.0
Callie_
๐ŸŒฑ 5์ฃผ์ฐจ: API ํ†ต์‹ , Codable, DispatchGroup, ๋ณต์Šต
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”