TheSongOfSongs / DevEvent

๐ŸŽ‰๐ŸŽˆ ๊ฐœ๋ฐœ์ž {์›จ๋น„๋‚˜, ์ปจํผ๋Ÿฐ์Šค, ํ•ด์ปคํ†ค} ํ–‰์‚ฌ๋ฅผ iOS ์•ฑ์œผ๋กœ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

๋ฐ๋ธŒ์บ˜๋ฆฐ๋”


๐ŸŽ‰๐ŸŽˆ ๊ฐœ๋ฐœ์ž {์›จ๋น„๋‚˜, ์ปจํผ๋Ÿฐ์Šค, ํ•ด์ปคํ†ค} ํ–‰์‚ฌ๋ฅผ iOS ์•ฑ์œผ๋กœ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.



โš™๏ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ

  • Language: Swift 5.5.2
  • iOS Deployment Target: iOS 13.0 +
  • Xcode: 13.0 compatible

๐Ÿ‘ฉโ€๐Ÿ’ป ๊ธฐ์ˆ  ์Šคํƒ

  • CocoaPods
  • CoreData
  • MVVM
  • RxSwift
  • Unit Test

๐Ÿ“š ์‚ฌ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

CocoaPods


๐Ÿ›  ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

1. Coordinator ํŒจํ„ด

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-01-02 แ„‹แ…ฉแ„’แ…ฎ 7 42 15

  • ๋„ค๋ชจ ์ƒ์ž๋กœ ํ‘œํ˜„๋œ coordinator์€ protocol Coordinator์„ ์ค€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค

  • View Controller๋Š” ์ผ๋Œ€์ผ๋กœ ๋งค์นญ๋˜๋Š” coordinator๊ฐ€ ์žˆ์œผ๋ฉฐ ๋ทฐ ์ „ํ™˜์˜ ์ผ์„ coordinator์—๊ฒŒ ์ „๋‹ดํ•ด ๋กœ์ง์„ ๋ถ„๋ฆฌ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค

    โœ… Coordinator ํŒจํ„ด ๋„์ž…์˜ ํšจ๊ณผ

    ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ทฐ ์ „ํ™˜ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค. ๋ทฐ ์ „ํ™˜์ด ๋ณต์žกํ•˜๊ณ  ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด View Controller์—์„œ ๋ทฐ ์ „ํ™˜ ๋กœ์ง์„ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋„์ž… ๊ฐ€์น˜๊ฐ€ ๋†’์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ•™์Šต์— ์˜์˜๋ฅผ ๋‘๊ณ  ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.


2. MVVM ํŒจํ„ด๊ณผ Input/Output ๋ฐ”์ธ๋”ฉ

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-01-02 แ„‹แ…ฉแ„’แ…ฎ 9 22 48

  • View Controller๋Š” View๋ฅผ ์†Œ์œ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๋‘˜์€ ๊ธด๋ฐ€ํ•˜๊ฒŒ ์—ฎ์—ฌ์žˆ์Šต๋‹ˆ๋‹ค

  • View Controller๋Š” ViewModel์„ ์†Œ์œ ํ•˜๋ฉฐ ๋‘˜์€ Input๊ณผ Output์„ ํ†ตํ•ด ์†Œํ†ตํ•ฉ๋‹ˆ๋‹ค

  • View Controller๋Š” ViewModel์˜ input์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์•ก์…˜์„ ์š”๊ตฌํ•˜๊ณ  ViewModel์€ Output์„ ํ†ตํ•ด Model์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•˜์—ฌ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค

  • ViewModel์˜ Input๊ณผ Output์€ struct ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค. ViewModel์˜ ๋ฐ์ดํ„ฐ ๊ด€๋ จ ํ”„๋กœํผํ‹ฐ๋“ค์€ ๋ชจ๋‘ private์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. View Controller์—์„œ๋Š” ํ•จ์ˆ˜ transform(input:) ์„ ํ†ตํ•ด output์„ ์–ป์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    โœ… Input, Output ๋„์ž… ์ด์œ 

    Intput, Output์„ ๋„์ž…ํ•˜๊ธฐ ์ด์ „์—๋Š” ViewModel์˜ ํ”„๋กœํผํ‹ฐ๋“ค์ด public์œผ๋กœ ๊ณต๊ฐœ๋˜์–ด ์žˆ์–ด View Controller๊ฐ€ ์ž„์˜๋กœ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด ๋ฐ์ดํ„ฐ ๋ณ€์งˆ ์šฐ๋ ค๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์„ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ Input/Output ๊ตฌ์กฐ๋ฅผ ๋งŽ์ด ์ด์šฉํ•˜๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๊ณ  ์ ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ, ViewModel์˜ ํ”„๋กœํผํ‹ฐ๋“ค์€ privateํ•˜๊ฒŒ ์„ค์ •๋˜์–ด ๋ฐ์ดํ„ฐ์˜ ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ๋”๋ถˆ์–ด ํ•จ์ˆ˜ transform(input:) ๋ฅผ ํ†ตํ•ด ๋กœ์ง์˜ ๋ถ„๋ฆฌ์‹œ์ผœ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ธฐ ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค. (ViewModel์—์„œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ RxSwift๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ”์ธ๋”ฉ๋˜๋Š” ์ฝ”๋“œ์—์„œ๋Š” Input/Output์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค)

//  HomeViewController.swift

final class HomeViewModel: ViewModelType {
    struct Input {
        var requestFetchingEvents: Observable<Void>
    }
    
    struct Output {
        var dataSources: Observable<[SectionOfEvents]>
        var isNetworkConnect: Observable<Bool>
    }

    private let eventsFromServer: BehaviorRelay<[SectionOfEvents]> = BehaviorRelay(value: [])
    private let favoriteEvents: BehaviorRelay<[EventCoreData]> = BehaviorRelay(value: [])
    
    func transform(input: Input) -> Output {
        ...
        return Output(dataSources: ..., isNetworkConnect: ...)
    }
}
//  HomeViewModel.swift

final class HomeViewController: UIViewController {
    let viewModel = HomeViewModel()

    func bindViewModel() {
        let input = HomeViewModel.Input(requestFetchingEvents: requestFetchingEvents.asObservable())
        let output = viewModel.transform(input: input)
        ...
    }
}

3. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง

image

  • NetworkService๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์—์„œ html ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค

  • DevEventsFetcher์—์„œ ๊ฐ€์ ธ์˜จ String ๊ฐ’์ธ html์„ ํŒŒ์‹ฑํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•ฉ๋‹ˆ๋‹ค

  • DevEventsFetcher๋Š” ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์ด๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งˆ๋‹ค ์ด๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  ViewModel๋“ค์—๊ฒŒ ์•Œ๋ ค์ค๋‹ˆ๋‹ค

  • CoreData์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์ €์žฅํ•˜๋Š” PersistanceManager๋ฅผ ์‹ฑ๊ธ€ํ„ด ๊ฐ์ฒด๋กœ ๋‘์–ด ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค

    โœ… NetworkService ๊ฐ์ฒด

    ๊ฐœ๋ฐœ ์ดˆ๊ธฐ์—๋Š” ์‹ฑ๊ธ€ํ„ด์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค๊ฐ€ ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ๋„์ž…ํ•˜๋ฉฐ DevEventsFetcher์—์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์—์„œ ๋„คํŠธ์›Œํฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ์€ NetworkService ํด๋ž˜์Šค ํ•˜๋‚˜๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ์‹ฑ๊ธ€ํ„ด์œผ๋กœ ์„ค๊ณ„ํ•˜์˜€์œผ๋‚˜, ํ˜„์žฌ DevEventsFetcher์—์„œ๋งŒ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด ์ผ์–ด๋‚˜๋ฉฐ ์œ ๋‹›ํ…Œ์ŠคํŠธ ๋•Œ mockURLSession์„ ๋งŒ๋“œ๋Š”๋ฐ ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์œ ๋‹›ํ…Œ์ŠคํŠธ๋ฅผ ๋„์ž…ํ•˜๋ฉด์„œ ์‹ฑ๊ธ€ํ„ด์ด ์•„๋‹Œ ๋‹จ์ˆœ ํด๋ž˜์Šค๋กœ ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค.


4. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ

์‹ฑ๊ธ€ํ„ด ํด๋ž˜์Šค์ธ NetworkConnectionManager๋ฅผ ๋‘์–ด ์‚ฌ์šฉ์ž์˜ ๊ธฐ๊ธฐ๊ฐ€ ๋„คํŠธ์›Œํฌ์— ์—ฐ๊ฒฐ๋˜์–ด์žˆ๋Š”์ง€ ๊ฐ์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์ด ์‹คํ–‰๋  ๋•Œ ์ž๋™์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ์‹คํ–‰๋˜๋ฉฐ, ViewModel๋“ค์ด ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์—ฌ๋ถ€๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ์–ด ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.


โ™ป๏ธ ์—…๋ฐ์ดํŠธ

  • v1.0.4 iOS16์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„๊ทธ ๊ฐœ์„ 
  • v1.0.3 ์ฆ๊ฒจ์ฐพ๊ธฐํ•œ ์ด๋ฒคํŠธ ๋ชฉ๋ก์—์„œ ์ข…๋ฃŒ๋œ ์ด๋ฒคํŠธ section ์ถ”๊ฐ€
  • v1.0.2 ๋ณ„ ๋ชจ์–‘ ์ด๋ฏธ์ง€ ์ค‘์•™์ •๋ ฌ ๋ฐ ๋ฆฌํŒฉํ„ฐ๋ง
  • v1.0.1 ๋‹คํฌ๋ชจ๋“œ ๋””์ž์ธ ๊ฐœ์„  ๋ฐ ๋ฆฌํŒฉํ„ฐ๋ง
  • v1.0 ์•ฑ ์Šคํ† ์–ด ์ถœ์‹œ (22.04.11)

โœ‰๏ธ ๋ฌธ์˜์‚ฌํ•ญ

jinhyang.programmer@gmail.com

About

๐ŸŽ‰๐ŸŽˆ ๊ฐœ๋ฐœ์ž {์›จ๋น„๋‚˜, ์ปจํผ๋Ÿฐ์Šค, ํ•ด์ปคํ†ค} ํ–‰์‚ฌ๋ฅผ iOS ์•ฑ์œผ๋กœ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.


Languages

Language:HTML 83.5%Language:Swift 16.4%Language:Ruby 0.1%