Skip to content

Latest commit

ย 

History

History
348 lines (258 loc) ยท 13.5 KB

220217_Dropbox,_RxSwift,_SPM,_DispatchGroup.md

File metadata and controls

348 lines (258 loc) ยท 13.5 KB

220217 Dropbox, RxSwift, SPM, DispatchGroup

TIL (Today I Learned)

2์›” 17์ผ (๋ชฉ)

ํ•™์Šต ๋‚ด์šฉ

  • ๋™๊ธฐํ™” ๋ฉ”๋ชจ์žฅ STEP3 ์ง„ํ–‰
    • SwiftyDropbox ์‚ฌ์šฉํ•ด๋ณด๊ธฐ
  • RxSwift - ๊ฐœ๋…์žก๊ธฐ

ย 

๊ณ ๋ฏผํ•œ ์  / ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

[RxSwift - ๊ฐœ๋…์žก๊ธฐ]

  • ๋น„๋™๊ธฐ๋กœ ์ƒ๊ธด ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ๋งŒ๋“ค๊นŒ?
    • ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

  • RxSwift๋Š”?

    • ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ƒ๊ธฐ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ completion ๊ฐ™์€ ํด๋กœ์ €๋ฅผ ํ†ตํ•ด์„œ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ์œ ํ‹ธ๋ฆฌํ‹ฐ์ด๋‹ค.

  • Observable ํƒ€์ž…์œผ๋กœ ๊ฐ์‹ธ์„œ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋‚˜์ค‘์— ์ƒ๊ธฐ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋œ๋‹ค.
  • ๋‚˜์ค‘์— ์ƒ๊ธฐ๋Š” ๋ฐ์ดํ„ฐ(Observable)์„ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” subscribe๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ค.
  • ๊ทธ๋Ÿผ ๊ฑฐ๊ธฐ์— ์ด๋ฒคํŠธ๊ฐ€ ์˜ค๋Š”๋ฐ, ์ข…๋ฅ˜๋Š” next, completed, error ์ด 3๊ฐ€์ง€์ด๋‹ค.
  • ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌ๋˜์—ˆ์„ ๋•Œ๋Š” next ์ผ€์ด์Šค๋กœ ์˜ค๊ณ , ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌ๋˜๊ณ  ๋๋‚ฌ์„ ๋•Œ๋Š” completed ์ผ€์ด์Šค๋กœ ์˜จ๋‹ค.

subscribe

  • subscribe๋Š” disposable์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด๊ฒŒ ๋ญ๋ƒ๋ฉด ๋™์ž‘์„ ์ค‘๊ฐ„์— ์ทจ์†Œ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” dispose()๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์œ„์ฒ˜๋Ÿผ ํ˜ธ์ถœํ•ด์ค€๋‹ค๋ฉด, ๋‹ค์šด๋กœ๋“œ๋ฅผ ํ•˜๋ผ๊ณ  ์‹œ์ผœ๋†“๊ณ  dispose๋ฅผ ํ•˜๊ฒŒ๋˜๋‹ˆ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ activityIndicator๋งŒ ๋Œ์•„๊ฐ€๊ณ , ๋‹ค์šด๋กœ๋“œ๋Š” ์ทจ์†Œ๋˜์–ด ๋™์ž‘ํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค.

  • โญ๏ธ ์—ฌ๊ธฐ์„œ ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, subscribe ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” completed๋‚˜ error์—์„œ ํด๋กœ์ €๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ž‘์—…ํ›„ ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ๋‹ค๊ณ  ์•Œ๋ ค์ฃผ๊ฒŒ ๋˜๋ฉด(onCompleted()) ํ•ด๋‹น ๋ฌธ์ œ๋Š” ํ•ด๊ฒฐ๋œ๋‹ค.

[๋ฐฐ์šธ ๊ฒƒ]

  • ๋น„๋™๊ธฐ๋กœ ์ƒ๊ธฐ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ Observable๋กœ ๊ฐ์‹ธ์„œ ๋ฆฌํ„ดํ•˜๋Š” ๋ฐฉ๋ฒ•

func rxswiftLoadImage(from imageUrl: String) -> Observable<UIImage?> {
        return Observable.create { seal in
            asyncLoadImage(from: imageUrl) { image in
                seal.onNext(image)
                seal.onCompleted()
            }
            return Disposables.create()
        }
    }

  • Observable.create() ๋งŒ๋“ค๊ณ  ๋“ค์–ด๊ฐ€๋Š” ์ธ์ž๋กœ ํด๋กœ์ €๊ฐ€ ํ•˜๋‚˜ ๋“ค์–ด๊ฐ€๋Š”๋ฐ, ๋ญ”๊ฐ€(emitter)๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ ํด๋กœ์ € ๋‚ด๋ถ€์— onNext() ๋ฉ”์†Œ๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋Š” ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์ดํ›„ onCompleted()๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๋๋‚ฌ๋‹ค๊ณ  ์•Œ๋ฆฐ ํ›„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ Disposables.create()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋œ๋‹ค.

  • Observabel.create()๋ฅผ ํ•œ๋‹ค.
    • task๋ฅผ ๋งŒ๋“ค์–ด์„œ dataTask๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด onError๋กœ ์—๋Ÿฌ๋ฅผ ์ „๋‹ฌํ•˜๊ณ , data๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ฐ”์ธ๋”ฉ ๋œ๋‹ค๋ฉด onNext๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  onCompleted()๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
    • ์ดํ›„ return์—์„œ Disposables.create()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ task.cancel()์„ ํ˜ธ์ถœํ•ด์ค€๋‹ค.
      • ์•„๋งˆ dispose๋ฅผ ํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ ๋˜๋Š” ๊ตฌํ˜„๋ถ€์ธ ๋“ฏ ํ•˜๋‹ค.
  • Observable์˜ ์ƒ๋ช…์ฃผ๊ธฐ
    • create
    • subscribe <- ์ด๋•Œ Observable๋กœ createํ•œ๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.
    • onNext
    • onCompleted / onError
    • Disposed
  • ์ด๋ ‡๊ฒŒ ๋™์ž‘์ด ๋๋‚œ Observable์€ ๋‹ค์‹œ โœจ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.โœจ

  • ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋Š” ์ค‘๊ฐ„์— debug๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Observable๋กœ ์˜ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•

  • subscribe๋Š” ํด๋กœ์ €๋ฅผ ๊ฐœํ–‰ํ•˜๋ฉด ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , subscribe() ํ˜ธ์ถœ๋งŒ ํ•œ๋‹ค๋ฉด ๊ฐ’๋งŒ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜๋„ ์žˆ๋‹ค.

[SPM์œผ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ํ•˜๊ธฐ]

Targets -> General -> Frameworks, Libraries, and Embedded Content -> +

Add Package Dependency... ๋ฅผ ํด๋ฆญ

์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ฃผ์†Œ๋ฅผ ๊ธฐ์ž…ํ•œ๋‹ค.

์„ค์น˜ ์‹œ ์›ํ•˜๋Š” ๋ฒ„์ „, ๋ธŒ๋žœ์น˜ ๋ฐ ์ปค๋ฐ‹์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ํ›„ ์›ํ•˜๋Š” packge product๋ฅผ ๊ณจ๋ผ์„œ Finish ๊นŒ์ง€ ํ•˜๋ฉด...

SwiftyDropbox๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์„ค์น˜๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


[SwiftyDropbox - ํ”„๋กœ์ ํŠธ ์„ค์ •ํ•˜๊ธฐ]

์•„๋ž˜ ํ”„๋กœ์ ํŠธ ์„ค์ •ํ•˜๋Š” ํŠœํ† ๋ฆฌ์–ผ์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ง„ํ–‰ํ•˜์˜€๋‹ค. https://github.com/dropbox/SwiftyDropbox#configure-your-project

๋จผ์ € Info.plist ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ ์ „์— dropbox์— app์„ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค. ๋กœ๊ทธ์ธ ํ›„ apps์— ๋“ค์–ด๊ฐ€๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฒ„ํŠผ์ด ์žˆ๋‹ค.

์ดํ›„ ํ•„์ˆ˜ ๋ฌธํ•ญ์„ ์„ ํƒ, ์ž…๋ ฅ ํ›„ create app ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋งŒ๋“ค์–ด์ฃผ๋ฉด ๋œ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด App key๊ฐ€ ๋ฐœ๊ธ‰๋˜๋Š”๋ฐ, ์ด๊ฑธ ์ด์ œ Info.plist๋ฅผ ์ˆ˜์ •ํ•˜๋Š”๋ฐ ํ™œ์šฉํ•  ๊ฒƒ์ด๋‹ค.

ํŠœํ† ๋ฆฌ์–ผ์—์„œ ํ•˜๋ผ๋Š”๋ฐ๋กœ Info.plist๋ฅผ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ค€๋‹ค.

<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>dbapi-8-emm</string>
        <string>dbapi-2</string>
    </array>

์•„๊นŒ ๋งŒ๋“ค๊ณ  ์–ป์€ App key๋ฅผ db- ๋’ค๋ถ€ํ„ฐ ๊ธฐ์ž…ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

<key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>db-<APP_KEY></string>
            </array>
            <key>CFBundleURLName</key>
            <string></string>
        </dict>
    </array>

์ดํ›„ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€์„œ AppDelegate์— DropboxClient ์ธ์Šคํ„ด์Šค๋ฅผ ์ดˆ๊ธฐํ™” ํ•ด์ค€๋‹ค.

import SwiftyDropbox

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    DropboxClientsManager.setupWithAppKey("<APP_KEY>")
    return true
}

๊ทธ๋ฆฌ๊ณ  SceneDelegate์— ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ธ์ฆ์ด ๋ชจ๋‘ ์™„๋ฃŒ๋œ ํ›„ redirection์„ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

import SwiftyDropbox

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
     let oauthCompletion: DropboxOAuthCompletion = {
      if let authResult = $0 {
          switch authResult {
          case .success:
              print("Success! User is logged into DropboxClientsManager.")
          case .cancel:
              print("Authorization flow was manually canceled by user!")
          case .error(_, let description):
              print("Error: \(String(describing: description))")
          }
      }
    }

    for context in URLContexts {
        // stop iterating after the first handle-able url
        if DropboxClientsManager.handleRedirectURL(context.url, completion: oauthCompletion) { break }
    }
}
    }

์ดํ›„ ๋งจ์ฒ˜์Œ์— ์‹œ์ž‘ํ•˜๋Š” ๋ทฐ์— ๋กœ๊ทธ์ธ์„ ํ•ด์„œ ์ธ์ฆ ํ† ํฐ์„ ๋ฐ›์•„์˜ค๋Š” ์ž‘์—…์„ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ ๊ฐ™์€ ๊ฒฝ์šฐ UISplitViewController๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, rootView์ธ SplitViewController์—์„œ๋Š” ํ•ด๋‹น ์ž‘์—…์ด ์ •์ƒ์ ์œผ๋กœ ๋œจ์ง€์•Š์•˜๋‹ค. (์ด์œ ๋Š” ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค.) ๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ UIViewController์—์„œ ์ง„ํ–‰ํ•ด์•ผํ•˜๋‚˜.. ์‹ถ์–ด์„œ UITableViewController์˜ viewDidLoad()์—์„œ ํ•ด๋‹น ์ž‘์—…์„ ์‹คํ–‰ํ•ด์ฃผ๋‹ˆ ๋กœ๊ทธ์ธ์ฐฝ์ด ์ •์ƒ์ ์œผ๋กœ ๋–ด๋‹ค.

import SwiftyDropbox

func myButtonInControllerPressed() {
    // OAuth 2 code flow with PKCE that grants a short-lived token with scopes, and performs refreshes of the token automatically.
    let scopeRequest = ScopeRequest(scopeType: .user, scopes: ["account_info.read"], includeGrantedScopes: false)
    DropboxClientsManager.authorizeFromControllerV2(
        UIApplication.shared,
        controller: self,
        loadingStatusDelegate: nil,
        openURL: { (url: URL) -> Void in UIApplication.shared.open(url, options: [:], completionHandler: nil) },
        scopeRequest: scopeRequest
    )

    // Note: this is the DEPRECATED authorization flow that grants a long-lived token.
    // If you are still using this, please update your app to use the `authorizeFromControllerV2` call instead.
    // See https://dropbox.tech/developers/migrating-app-permissions-and-access-tokens
    DropboxClientsManager.authorizeFromController(UIApplication.shared,
                                                  controller: self,
                                                  openURL: { (url: URL) -> Void in
                                                    UIApplication.shared.open(url, options: [:], completionHandler: nil)
                                                  })
}

์—ฌ๊ธฐ์„œ scopes๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์€ ์•ฑ์ด Dropbox ๊ณ„์ • ์ •๋ณด๋ฅผ ๋ณด๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ถŒํ•œ์˜ ๋ฒ”์œ„๋ฅผ ๋œปํ•œ๋‹ค. ์•„๊นŒ App key๋ฅผ ์–ป์—ˆ๋˜ ๊ณณ์—์„œ Permissions ํƒญ์„ ํด๋ฆญํ•˜๋ฉด Account์˜ ์ •๋ณด๊ฐ€ ๋‚˜์˜จ๋‹ค. ๋”ฐ๋ผ์„œ ํ•„์š”ํ•œ Account๋ฅผ scopes์— ๋„ฃ์–ด์ฃผ๋ฉด ๋˜๊ฒ ๋‹ค.

์ด ๋‹ค์Œ์— API์— ํ˜ธ์ถœํ•  DropboxClient ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

import SwiftyDropbox

// Reference after programmatic auth flow
let client = DropboxClientsManager.authorizedClient

client๋ฅผ ํ†ตํ•ด ์—…๋กœ๋“œ์™€ ๋‹ค์šด๋กœ๋“œ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

let fileData = "testing data example".data(using: String.Encoding.utf8, allowLossyConversion: false)!

let request = client.files.upload(path: "/test/path/in/Dropbox/account", input: fileData)
    .response { response, error in
        if let response = response {
            print(response)
        } else if let error = error {
            print(error)
        }
    }
    .progress { progressData in
        print(progressData)
    }

// in case you want to cancel the request
if someConditionIsSatisfied {
    request.cancel()
}
// Download to URL
let fileManager = FileManager.default
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destURL = directoryURL.appendingPathComponent("myTestFile")
let destination: (URL, HTTPURLResponse) -> URL = { temporaryURL, response in
    return destURL
}
client.files.download(path: "/test/path/in/Dropbox/account", overwrite: true, destination: destination)
    .response { response, error in
        if let response = response {
            print(response)
        } else if let error = error {
            print(error)
        }
    }
    .progress { progressData in
        print(progressData)
    }


// Download to Data
client.files.download(path: "/test/path/in/Dropbox/account")
    .response { response, error in
        if let response = response {
            let responseMetadata = response.0
            print(responseMetadata)
            let fileContents = response.1
            print(fileContents)
        } else if let error = error {
            print(error)
        }
    }
    .progress { progressData in
        print(progressData)
    }

๋‘๊ฐ€์ง€์˜ ๊ณตํ†ต์ ์€ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ์—…๋กœ๋“œํ•  ๋•Œ ๊ฒฝ๋กœ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์ ์ด๋‹ค. ๋ฉ”๋ชจ์žฅ ํ”„๋กœ์ ํŠธ์˜ ๊ฒฝ์šฐ CoreData๋ฅผ ํ†ตํ•ด์„œ ๋ฉ”๋ชจ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฑ์—…์˜ ํ˜•ํƒœ๋กœ CoreData์˜ ๊ฒฝ๋กœ๋ฅผ ์–ป์–ด๋‚ด์„œ .sqlite, .sqlite-shm, .sqlite-wal ์ด 3๊ฐœ์˜ ํŒŒ์ผ์„ ์—…๋กœ๋“œ ๋ฐ ๋‹ค์šด๋กœ๋“œ ํ•ด์ฃผ๋„๋ก ๊ตฌํ˜„ํ•ด์ฃผ์—ˆ๋‹ค.

์—…๋กœ๋“œ, ๋‹ค์šด๋กœ๋“œ ๋ชจ๋‘ ํŒŒ์ผ์„ ๋ฎ์–ด์“ธ๊ฑด์ง€์— ๋Œ€ํ•œ ์˜ต์…˜์ด ์žˆ์œผ๋‹ˆ ์ž์„ธํ•œ๊ฑด ์•„๋ž˜ ๋„ํ๋จผํŠธ์—์„œ ๊ฒ€์ƒ‰ํ•ด๋ณด๋ฉด ๋˜๊ฒ ๋‹ค.

https://dropbox.github.io/SwiftyDropbox/api-docs/latest/index.html


[SwiftyDropbox - download๊ฐ€ ๋๋‚˜๋Š” ์‹œ์ ์— ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ]

๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋๋‚œ ํ›„ CoreData๋ฅผ fetch๋ฅผ ํ•˜๊ณ  TableView๋ฅผ reload๋ฅผ ํ•ด์ฃผ๊ณ  ์‹ถ์—ˆ์œผ๋‚˜ ์‹คํŒจํ–ˆ์—ˆ๋‹ค.

  • ์ด์œ  ํŒŒ์ผ์ด ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ์กด์žฌํ•˜์—ฌ, ์—ฌ๋Ÿฌ๊ฐœ์˜ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œ ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ˜๋ณต๋ฌธ์„ ๋Œ๋ฆฌ๊ณ  ์žˆ์—ˆ์œผ๋‚˜, fetch์™€ reload๋ฅผ for-in๋ฌธ ๋‚ด๋ถ€์—์„œ ํ•ด์ฃผ๊ณ  ์žˆ์–ด์„œ, ๋ทฐ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๊ฐ€ ์žˆ๊ณ , ์•ˆ๋˜๊ธฐ๋„ ํ•˜๋Š” ํ˜„์ƒ์ด ๋‚˜ํƒ€๋‚ฌ๋‹ค.
  • ํ•ด๊ฒฐ ๊ทธ๋ž˜์„œ for-in๋ฌธ์ด ์ข…๋ฃŒ๋œ ์‹œ์ ์— fetch๋ฅผ ํ•˜๊ณ  view๋ฅผ reload๋ฅผ ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด, ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋ชจ๋‘ ์™„๋ฃŒ๋˜๋Š” ์‹œ์ ์„ DispatchGroup๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ถ”์ ํ•˜๊ณ , ๋ฐ˜๋ณต๋ฌธ์—์„œ ์‹œ์ž‘๋˜์—ˆ๋˜ ๋‹ค์šด๋กœ๋“œ ์ž‘์—…์ด ๋ชจ๋‘ ๋๋‚˜๊ฒŒ ๋˜๋ฉด ์•„๋ž˜ ๋ทฐ๋ฅผ ๋‹ค์‹œ ์„ค์ •ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์˜€๋‹ค.
func download(_ tableViewController: NotesViewController?) {
    let group = DispatchGroup() // ๊ทธ๋ฃน ์ƒ์„ฑ
    for fileName in fileNames {
        let destURL = applicationSupportDirectoryURL.appendingPathComponent(fileName)
        let destination: (URL, HTTPURLResponse) -> URL = { _, _ in
            return destURL
        }
        group.enter() // ์ž‘์—… ์‹œ์ž‘
        client?.files.download(path: fileName, overwrite: true, destination: destination)
            .response { _, error in
                if let error = error {
                    print(error)
                }
                group.leave() // ์ž‘์—… ๋
            }
    }
    group.notify(queue: .main) { // ๋ชจ๋“  ์ž‘์—…์ด ๋๋‚œ๋‹ค๋ฉด ...
        PersistentManager.shared.setUpNotes()
        tableViewController?.tableView.reloadData()
        tableViewController?.stopActivityIndicator()
    }
}