GithubHelp home page GithubHelp logo

leeari95 / github-search Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 159 KB

๐Ÿˆโ€โฌ› Github REST API๋ฅผ ํ™œ์šฉํ•œ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ ๊ฒ€์ƒ‰ ์•ฑ

Swift 100.00%

github-search's Introduction

GitHub Search App ํ”„๋กœ์ ํŠธ

  • ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ
  • ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„: 2022.05.04 - 2022.05.14

๋ชฉ์ฐจ

ํ‚ค์›Œ๋“œ

  • Architecture / Design Pattern
    • Clean Architecture MVVM
    • Coordinator
    • Delegate
    • Event Listeners
  • UIKit
    • UICollectionViewDiffableDataSource
    • UIGestureRecognizer
    • Appearance
  • GitHub Rest API
  • OAuth 2.0
  • Keychain
  • NSCache

โญ๏ธ ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

Github Rest API๋ฅผ ํ™œ์šฉํ•œ ์•ฑ์ด์—์š”.

๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์–ด์š”.

๋ณ„ํ‘œ ํ‘œ์‹œ๋ฅผ ํ„ฐ์น˜ํ•˜์—ฌ ์ฒดํฌ/ํ•ด์ œ๋ฅผ ํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด, ์ž์‹ ์˜ ํ”„๋กœํ•„ ๋ฐ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์–ด์š”.


โœจ ํ”„๋กœ์ ํŠธ ์ฃผ์š”๊ธฐ๋Šฅ

๐Ÿ” ๊ฒ€์ƒ‰์ฐฝ์—์„œ ์›ํ•˜๋Š” ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฒ€์ƒ‰ํ•ด๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

โ›”๏ธ ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ณ„ํ‘œ ํ‘œ์‹œ๋ฅผ ์ฒดํฌ/ํ•ด์ œ ํ•  ์ˆ˜ ์—†์–ด์„œ, ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ‘ค ๋กœ๊ทธ์ธ ์‹œ ํ”„๋กœํ•„ ๋ฐ ์ž์‹ ์˜ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

โญ๏ธ ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ์‹œ ์ž์‹ ์˜ ๊ธฐ์กด ๋ณ„ํ‘œ ๋ชฉ๋ก๊ณผ ๋™๊ธฐํ™” ๋ฉ๋‹ˆ๋‹ค.

โœ… ๋ณ„ํ‘œ ์ฒดํฌ ์‹œ ๋‘ ํ™”๋ฉด ๋ชจ๋‘ ๋™๊ธฐํ™” ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ‘†๐Ÿป ์ผ์ • ๊ธธ์ด ์ด์ƒ ์Šคํฌ๋กค ์‹œ ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ Prefetch ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ’ช๐Ÿป ๊ธฐ์ˆ ์  ๋„์ „

MVVM

  • ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์™€ ๋ทฐ๋Š” ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ์—ญํ• ์—๋งŒ ์ง‘์ค‘์‹œํ‚ค๊ณ  ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋ทฐ๋ชจ๋ธ์—์„œ ์ง„ํ–‰๋˜๋„๋ก ๊ตฌ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Clean Architecture

  • ๋ทฐ๋ชจ๋ธ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋“ค์„ ์œ ์ฆˆ์ผ€์ด์Šค๋กœ, ๋„คํŠธ์›Œํฌ์— ๋Œ€ํ•œ ์š”์ฒญ์€ repository๋กœ ๋ถ„๋ฆฌํ•ด ๊ฐ ๋ ˆ์ด์–ด์˜ ์—ญํ• ์„ ๋ถ„๋ช…ํ•˜๊ฒŒ ๋‚˜๋ˆ„์—ˆ์Šต๋‹ˆ๋‹ค.

Coordinator ํŒจํ„ด ์‚ฌ์šฉ

  • ํ™”๋ฉด ์ „ํ™˜์— ๋Œ€ํ•œ ๋กœ์ง์„ ViewController๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•˜๊ณ  ์˜์กด์„ฑ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ฃผ์ž…์„ ์™ธ๋ถ€์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ›  Trouble Shooting

์ค‘๋ณต๋œ ์‹๋ณ„์ž ์˜ค๋ฅ˜

Thread 7: "Fatal: supplied item identifiers are not unique. Duplicate identifiers:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Fatal: supplied item identifiers are not unique. Duplicate identifiers: {(
    GithubSearchApp.RepositoryItem(id: 190487472, name: "animal-crossing-music-sim", login: "owendaprile", description: "Animal Crossing Music Simulator", isMarkedStar: false, starredCount: 2),
    GithubSearchApp.RepositoryItem(id: 256757675, name: "Animal-Crossing-App", login: "carluqcor", description: "", isMarkedStar: false, starredCount: 2)
)}'
  • ์ƒํ™ฉ diffable datasource๋กœ ์ปฌ๋ ‰์…˜๋ทฐ๋ฅผ ์™„์„ฑํ•˜๊ณ , ์Šคํฌ๋กค ํ•˜๋Š” ๋„์ค‘ ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
  • ์ด์œ  ์…€์˜ ์‹๋ณ„์ž๊ฐ€ ๊ณ ์œ ํ•˜์ง€ ์•Š๋‹ค๋Š” ์—๋Ÿฌ์˜€๋‹ค. ์…€์˜ ์ •๋ณด๋ฅผ ๋‹ด๊ณ ์žˆ๋Š” RepositoryItem ํƒ€์ž…์ด ๋‹จ์ˆœํžˆ Hashable์„ ์ค€์ˆ˜ํ•˜๊ณ ๋Š” ์žˆ์ง€๋งŒ, ๋™์ผํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ์ƒํ™ฉ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด์˜€๋‹ค.
  • ํ•ด๊ฒฐ ๋ฐ›์•„์˜ค๋Š” ๊ฐ’์ค‘ ๊ณ ์œ ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ•ด์‹ฑํ•˜๋„๋ก hash(into:) ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ ํ•ด์ฃผ์—ˆ๋‹ค.
extension RepositoryItem: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
        hasher.combine(login)
        hasher.combine(description)
    }
    static func ==(lhs: RepositoryItem, rhs: RepositoryItem) -> Bool {
        return lhs.id == rhs.id && lhs.name == rhs.name && lhs.login == rhs.description
     }
}

์ž˜๋ชป๋œ ์ฟผ๋ฆฌ ์—๋Ÿฌ

 '*** +[NSURLComponents setPercentEncodedQueryItems:]: invalid characters in percentEncodedQueryItems'
  • ์ƒํ™ฉ ์ฟผ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฒ€์ƒ‰์„ ์‹œ๋„ํ•˜๋‹ค๊ฐ€ ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
  • ์ด์œ  URL์„ ์ธ์ฝ”๋”ฉ ํ• ๋•Œ, ๊ฒ€์ƒ‰ํ‚ค์›Œ๋“œ ์ค‘ ๋„์–ด์“ฐ๊ธฐ(๊ณต๋ฐฑ)๋Š” URL๋กœ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋Š” ์–ธ์–ด๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋Š” ์—๋Ÿฌ์˜€๋‹ค.
  • ํ•ด๊ฒฐ ๋”ฐ๋ผ์„œ URL์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์ „์— ์ฟผ๋ฆฌ๋ฅผ ์ธ์ฝ”๋”ฉํ•ด์ฃผ๋Š” ์ž‘์—…์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋‹ˆ ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค.
 var url: URL? {
    guard let url = self.baseURL?.appendingPathComponent(self.path) else {
        return nil
    }
    var urlComponents = URLComponents(string: url.absoluteString)
    let urlQuries = self.parameters.map { key, value -> URLQueryItem in
        let value = value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) // ์ธ์ฝ”๋”ฉ ์ž‘์—…์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋‹ˆ ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค.
        return URLQueryItem(name: key, value: value)
    }

    urlComponents?.percentEncodedQueryItems = urlQuries

    return urlComponents?.url
}

SearchBar๊ฐ€ ํ†ตํ†ต ํŠ€๋Š” ํ˜„์ƒ

  • ์ƒํ™ฉ ์„œ์น˜๋ฐ”๋ฅผ ํด๋ฆญํ•  ๋•Œ ๋งˆ๋‹ค ๊ฒ€์ƒ‰๋ฐ”๊ฐ€ ๋น„์ •์ƒ์ ์œผ๋กœ ํ†ตํ†ต ํŠ€๋Š” ํ˜„์ƒ์ด ๋‚˜ํƒ€๋‚ฌ๋‹ค.
  • ์‹œ๋„ searchController์˜ hidesNavigationBarDuringPresentation ์†์„ฑ์„ false๋กœ ์ค˜๋ณด์•˜๋Š”๋ฐ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๋‹ค.
  • ํ•ด๊ฒฐ ๋ญ”๊ฐ€ ์—ฌ๋Ÿฌ ์ƒํ™ฉ์—์„œ ์„œ์น˜๋ฐ”๋ฅผ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค ์ปฌ๋ ‰์…˜๋ทฐ์˜ ์…€๋“ค์ด ์ž์—ฐ์Šค๋Ÿฌ์šด ๋Š๋‚Œ์ด ์•„๋‹Œ ๋ฏธ๋ฆฌ ์ž๋ฆฌ๋ฅผ ์žก๋Š” ๋Š๋‚Œ์ด๋ผ์„œ, ViewController ์†์„ฑ ์ค‘ edgesForExtendedLayout์˜ ๊ฐ’์„ .bottom์œผ๋กœ ํ• ๋‹นํ•ด์ฃผ์—ˆ๋”๋‹ˆ ํ•ด๋‹น ํ˜„์ƒ์ด ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค.
    • ์ด๋ ‡๊ฒŒ ํ•˜๊ฒŒ๋˜๋ฉด ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”๊ฐ€ ์žˆ๋Š” ์˜์—ญ์—๋Š” ์ฝ˜ํ…์ธ ๊ฐ€ ์ž๋ฆฌ์žก์ง€ ์•Š๋Š”๋‹ค.

๐Ÿ”ฅ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ๋œ ๊ฒƒ

์•ฑ์„ ์ „์—ญ์ ์œผ๋กœ ์„ธํŒ…ํ•˜๋Š” ๋ฐฉ๋ฒ•

  • tintColor๋‚˜, appearance ๊ด€๋ จ๋œ ์†์„ฑ๊ฐ’๋“ค์„ ๋ชจ๋“  ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋™์ผํ•˜๊ฒŒ ๊ฐ€์ ธ๊ฐ”์œผ๋ฉด ํ•ด์„œ ์ฐพ์•„๋ณด์•˜๋‹ค.
  • ๋จผ์ € AppAppearance์ด๋ผ๋Š” ํด๋ž˜์Šค ํƒ€์ž…์„ ์„ ์–ธํ•ด์ค€๋‹ค.
final class AppAppearance {
    static func setUpAppearance() {
        UITabBar.appearance().tintColor = .label
        UIBarButtonItem.appearance().tintColor = .label
        UINavigationBar.appearance().prefersLargeTitles = true
    }
}
  • ๊ทธ๋ฆฌ๊ณ  AppDelegate์—์„œ ์ „์—ญ์ ์œผ๋กœ ์„ธํŒ…ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ˜ธ์ถœํ•ด์ค€๋‹ค.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        AppAppearance.setUpAppearance() // ํ˜ธ์ถœ
        return true
    }

๋””์ฝ”๋”ฉ ์—๋Ÿฌ๊ฐ€ ๋‚ฌ์„ ๋•Œ, ์ƒ์„ธํ•œ ์—๋Ÿฌ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•˜๊ธฐ

  • DTO ํƒ€์ž… ๋ฌธ์ œ๋กœ ๋””์ฝ”๋”ฉ ์‹คํŒจ๊ฐ€ ๋˜๋Š”๋ฐ, ์—๋Ÿฌ๋ฉ”์„ธ์ง€๊ฐ€ ๋”ฐ๋กœ ๋œจ์ง€ ์•Š์•„์„œ ์›์ธ์„ ์ฐพ๋Š”๊ฒŒ ์–ด๋ ต๋˜ ์™€์ค‘์— ์ฐพ์€ ๋ฐฉ๋ฒ•์ด๋‹ค.
do {
    let decoder = JSONDecoder()
    let decoded = try decoder.decode([Root].self, from: data!)
} catch {
    // print(localizedDescription) // <- โš ๏ธ Don't use this!

    print(String(describing: error)) // <- โœ… Use this for debuging!
}
  • ๋ณดํ†ต error์˜ localizedDescription์„ ํ†ตํ•ด ์—๋Ÿฌ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•ด๋ณด๋ ค๊ณ  ํ•˜๋Š”๋ฐ, ๋””์ฝ”๋”ฉ ์—๋Ÿฌ์˜ ๊ฒฝ์šฐ String(describing:) ์ด๋‹ˆ์…œ๋ผ์ด์ €๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—๋Ÿฌ๋ฉ”์„ธ์ง€๋ฅผ ๋””๋ฒ„๊น…ํ•ด๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋กœ๊ทธ์ธ ์ด๋ฒคํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ์ „๋‹ฌํ•ด์ค„๊นŒ?

  • ๋กœ๊ทธ์ธ์„ ํ–ˆ์„ ๋•Œ, ๋กœ๊ทธ์•„์›ƒ์„ ํ–ˆ์„ ๋•Œ ๋ทฐ๊ฐ€ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ํ‘œ์‹œํ•˜๋„๋ก ํ•ด์•ผํ•˜๋Š”๋ฐ, ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ์žˆ๋‚˜ ์ฐพ์•„๋ณด๋‹ค๊ฐ€ ํ•ด๋‹น ๊ธ€์„ ๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.
  • ๋ฐฉ์‹์€ ๋ฆฌ์Šค๋„ˆ๋ผ๋Š” ํ”„๋กœํ† ์ฝœ์„ ๋งŒ๋“ค๊ณ , ๋กœ๊ทธ์ธํ–ˆ๊ฑฐ๋‚˜ ๋กœ๊ทธ์•„์›ƒ ํ–ˆ์„ ๋•Œ ์‹คํ–‰ํ•  ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•ด์ค€๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์ผ์„ ํ•ด์•ผํ•˜๋Š” ๊ฐ์ฒด๋“ค์—๊ฒŒ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ค€์ˆ˜ํ•˜๋„๋ก ํ•˜๊ฒŒํ•˜๊ณ , ๋กœ๊ทธ์ธ ๋งค๋‹ˆ์ €๋ฅผ ํ™œ์šฉํ•ด ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ๋ฆฌ์Šค๋„ˆ๋“ค์ด ์ผ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ด์ฃผ์—ˆ๋‹ค.
  • ๋‚˜์˜ ๊ฒฝ์šฐ๋Š” Storage > Repository > UseCase ์ˆœ์„œ๋Œ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„คํŠธ์›Œํฌ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์‹œ์ ์„ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•  ์ˆ˜๊ฐ€ ์—†์–ด์„œ ์˜ต์ €๋ฒ„๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์Œ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ผ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ด์ฃผ์—ˆ๋‹ค.
final class LoginManager {
    static let shared = LoginManager()
    private init() {}
    
    private let apiRequest: APIProvider = DefaultAPIProvider()
    var isLogged: Bool {
        KeychainStorage.shard.load("Token") == nil ? false : true
    }
    // ๋ฆฌ์Šค๋„ˆ๋“ค์„ ๋‹ด์•„๋‘๋Š” ๋ฐฐ์—ด
    private var listeners: [WeakReference<AuthChangeListener>] = []
    
    // ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
    func addListener(_ listener: AuthChangeListener) {
        if listeners.compactMap({ $0.value?.instanceName() }).contains(listener.instanceName()) {
            return
        }
        listeners.append(WeakReference(value: listener))
    }
    
    func authorize() {
        guard let url = APIAddress.code(clientID: Secrets.clinetID, scope: "repo,user").url else {
            return
        }
        UIApplication.shared.open(url)
    }
    
    func requestToken(code: String, completion: ((Result<Bool, Error>) -> Void)?) {
        guard let url = APIAddress.token(clientID: Secrets.clinetID, clientSecret: Secrets.clinetSecret, code: code).url else {
            return
        }
        var request = URLRequest(url: url)
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        
        apiRequest.execute(request: request) { result in
            switch result {
            case .success(let data):
                data.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) as? [String: String] }
                .flatMap { $0["access_token"] }
                .flatMap {
                    if KeychainStorage.shard.save(key: "Token", value: $0) {
                        print("์‚ฌ์šฉ์ž์˜ ํ† ํฐ์„ ํ‚ค์ฒด์ธ์— ์ €์žฅํ•˜๋Š”๋ฐ ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค!")
                        // ์ฒซ๋ฒˆ์žฌ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„ ์ผ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋„๋ก ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ
                        self.listeners.first?.value?.authStateDidChange(isLogged: true)
                        completion?(.success(true))
                    } else {
                        print("ํ† ํฐ์„ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
                        completion?(.success(false))
                    }
                }
            case .failure(let error):
                completion?(.failure(error))
            }
        }
    }
    
    func logout() {
        KeychainStorage.shard.delete(key: "Token")
        listeners.forEach {
            $0.value?.authStateDidChange(isLogged: false)
        }
    }
    
    // ์ฒซ๋ฒˆ์žฌ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ผ์„ ๋ชจ๋‘ ๋งˆ์น˜๋ฉด ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์Œ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ผ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    func executeNextWork(_ order: String, isLogged: Bool = LoginManager.shared.isLogged) {
        guard let index = listeners.compactMap({ $0.value?.instanceName() }).firstIndex(of: order) else {
            return
        }
        listeners[index].value?.authStateDidChange(isLogged: isLogged)
    }
}
  • ๊ทธ๋ฆฌ๊ณ  ์ถ”ํ›„ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด WeakReference๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋ฆฌ์Šค๋„ˆ๋“ค์„ ๋‹ด์•„์ฃผ์—ˆ๋‹ค.

top

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.