Skip to content

lxodud/ProjectManager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

39 Commits
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ“‘ Project Manager

  • ํ•˜๋ชจ์™€ Wonbi๊ฐ€ ๋งŒ๋“  ํ”„๋กœ์ ํŠธ ๋งค๋‹ˆ์ € App์ž…๋‹ˆ๋‹ค.

๐Ÿ“– ๋ชฉ์ฐจ

  1. ํŒ€ ์†Œ๊ฐœ
  2. ํŒ€ ์œ„ํ‚ค
  3. ์‹คํ–‰ ํ™”๋ฉด
  4. Diagram
  5. ํด๋” ๊ตฌ์กฐ
  6. ํƒ€์ž„๋ผ์ธ
  7. ๊ธฐ์ˆ ์  ๋„์ „
  8. ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ๋ฐ ๊ณ ๋ฏผ
  9. ์ฐธ๊ณ  ๋งํฌ

๐ŸŒฑ ํŒ€ ์†Œ๊ฐœ

Wonbi Hamo

๐Ÿงญ ํŒ€ ์œ„ํ‚ค

๐Ÿ›  ์‹คํ–‰ ํ™”๋ฉด

ํ”„๋กœ์ ํŠธ ์ถ”๊ฐ€
ํ”„๋กœ์ ํŠธ ํŽธ์ง‘
ํ”„๋กœ์ ํŠธ ์‚ญ์ œ
ํ”„๋กœ์ ํŠธ ์ƒํƒœ ๋ณ€๊ฒฝ

๐Ÿ‘€ Diagram

๐Ÿ™ ๊ธฐ์ˆ ์Šคํƒ ๋งˆ์ธ๋“œ๋งต

๐Ÿ“ ์ฑ…์ž„ ์ฃผ๋„ ์„ค๊ณ„

๐Ÿ— ์•„ํ‚คํ…์ณ

๐Ÿงฌ Class Diagram

๐Ÿ—‚ ํด๋” ๊ตฌ์กฐ

ProjectManager
โ”œโ”€โ”€ Info.plist
โ”œโ”€โ”€ Resources
โ”‚ย ย  โ”œโ”€โ”€ AppDelegate.swift
โ”‚ย ย  โ””โ”€โ”€ SceneDelegate.swift
โ””โ”€โ”€ Sources
    โ”œโ”€โ”€ Extension
    โ”‚ย ย  โ””โ”€โ”€ Date+.swift
    โ”œโ”€โ”€ Model
    โ”‚ย ย  โ”œโ”€โ”€ Project.swift
    โ”‚ย ย  โ””โ”€โ”€ State.swift
    โ”œโ”€โ”€ View
    โ”‚ย ย  โ”œโ”€โ”€ Components
    โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ CountLabel.swift
    โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ HeaderView.swift
    โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ ProjectCell.swift
    โ”‚ย ย  โ””โ”€โ”€ Controllers
    โ”‚ย ย      โ”œโ”€โ”€ AddViewController.swift
    โ”‚ย ย      โ”œโ”€โ”€ EditViewController.swift
    โ”‚ย ย      โ”œโ”€โ”€ MainViewController.swift
    โ”‚ย ย      โ”œโ”€โ”€ ProjectListViewController.swift
    โ”‚ย ย      โ””โ”€โ”€ ProjectViewController.swift
    โ””โ”€โ”€ ViewModel
        โ”œโ”€โ”€ AddViewModel.swift
        โ”œโ”€โ”€ EditViewModel.swift
        โ”œโ”€โ”€ HeaderViewModel.swift
        โ”œโ”€โ”€ MainViewModel.swift
        โ”œโ”€โ”€ ProjectCellViewModel.swift
        โ””โ”€โ”€ ViewModelDelegate.swift

โฐ ํƒ€์ž„๋ผ์ธ

๐Ÿ‘Ÿ Step 1

  • ๊ธฐ์ˆ ์Šคํƒ ๊ฒฐ์ •
    • โœ… ์š”๊ตฌ์‚ฌํ•ญ๊ณผ ํ˜„์žฌ ์ƒํ™ฉ์— ๊ฐ€์žฅ ์ ํ•ฉํ•˜๋‹ค ํŒ๋‹จ๋˜๋Š” ๊ธฐ์ˆ ์Šคํƒ ๊ฒฐ์ •
  • ์„ค์น˜
    • โœ… SPM์„ ์ด์šฉํ•œ FireBase ์„ค์น˜
    • โœ… brew๋ฅผ ์ด์šฉํ•œ SwiftLint ์„ค์น˜

๐Ÿ‘Ÿ Step 2

  • ํ™”๋ฉด ๊ตฌํ˜„
    • โœ… ํ”„๋กœ์ ํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” 3๊ฐœ์˜ ํ…Œ์ด๋ธ” ๋ทฐ์™€ ํ—ค๋” ๊ตฌํ˜„
    • โœ… ์ถ”๊ฐ€์™€ ์ˆ˜์ •์„ ํ•  ์ˆ˜ ์žˆ๋Š” Add/Edit๋ทฐ ๊ตฌํ˜„
  • ๊ธฐ๋Šฅ ๊ตฌํ˜„
    • โœ… ์ถ”๊ฐ€ ๋ฒ„ํŠผ์„ ํ„ฐ์น˜ํ•˜๊ฑฐ๋‚˜ ๊ฐ ํ”„๋กœ์ ํŠธ์˜ ์…€์„ ํ„ฐ์น˜ํ–ˆ์„ ๋•Œ ๊ฐ๊ฐ Add/Edit๋ทฐ๋ฅผ ํ™”๋ฉด์— ๋„์šฐ๋„๋ก ๊ตฌํ˜„
    • โœ… ์‚ฌ์šฉ์ž๊ฐ€ ์…€์„ ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๋ฉด popover๊ฐ€ ๋‚˜ํƒ€๋‚˜ ์…€์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋„๋ก ๊ตฌํ˜„
    • โœ… ์‚ฌ์šฉ์ž๊ฐ€ ์…€์„ ์Šค์™€์ดํ”„ํ•˜๋ฉด ํ”„๋กœ์ ํŠธ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„
ํŽผ์ณ๋ณด๊ธฐ

๐ŸŸก View

โšช๏ธ Components

1๏ธโƒฃ HeaderView

  • titleLabel
    • ์‚ฌ์šฉ์ž์˜ ํ”„๋กœ์ ํŠธ์˜ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ ˆ์ด๋ธ”์ž…๋‹ˆ๋‹ค.
  • countLabel
    • ์‚ฌ์šฉ์ž์˜ ํ”„๋กœ์ ํŠธ์˜ ์ด ๊ฐฏ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ ˆ์ด๋ธ”์ž…๋‹ˆ๋‹ค.

2๏ธโƒฃ CountLabel

  • ์ปค์Šคํ…€๋ ˆ์ด๋ธ”
    • ๋ฐฐ๊ฒฝ์„ ๋™๊ทธ๋ž€ ๋ชจ์–‘์œผ๋กœ ๊นŽ์•„ ๋™๊ทธ๋ž€ ๋ชจ์–‘์˜ ์ปค์Šคํ…€ ๋ ˆ์ด๋ธ”์ž…๋‹ˆ๋‹ค.
    • ์ˆซ์ž๊ฐ€ ์ปค์ ธ์„œ ๊ฐ€๋กœ๋กœ ๊ธธ์–ด์ง€๋ฉด ๊ธธ์ด์— ๋งž๊ฒŒ ์บก์Š๋ชจ์–‘์œผ๋กœ ๋ณ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.

3๏ธโƒฃ ProjectCell

  • ํ…Œ์ด๋ธ”๋ทฐ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ปค์Šคํ…€ ์…€์ž…๋‹ˆ๋‹ค.

๐Ÿ”ด Controllers

1๏ธโƒฃ AddViewController

  • ์ถ”๊ฐ€ํ•˜๊ธฐ ๋ฒ„ํŠผ(+ ๋ฒ„ํŠผ)์„ ๋ˆŒ๋ €์„ ๋•Œ ๋‚˜ํƒ€๋‚˜๋Š” ๋ทฐ ์ž…๋‹ˆ๋‹ค.
  • pageSheet๋ฐฉ์‹์˜ ๋ชจ๋‹ฌํ”„๋ฆฌ์  ํ…Œ์ด์…˜์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

2๏ธโƒฃ EditViewController

  • ํ”„๋กœ์ ํŠธ ์…€์„ ๋ˆŒ๋ €์„ ๋•Œ ๋‚˜ํƒ€๋‚˜๋Š” ๋ทฐ ์ž…๋‹ˆ๋‹ค.
  • AddViewController์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ํ™”๋ฉด์— ๋„์šฐ์ง€๋งŒ, Edit๋ฒ„ํŠผ์ด ์žˆ์–ด ๋‚ด์šฉ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ ProjectViewController

  • AddViewController์™€ EditViewController๊ฐ€ ์ƒ์†ํ•˜๋Š” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์ž…๋‹ˆ๋‹ค.
  • ๋‘ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํ™”๋ฉด ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ ๋ฒ„ํŠผ์„ ์ œ์™ธ ํ•˜๋ฉด ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์†์„ ํ†ตํ•ด ๊ตฌํ˜„ํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

4๏ธโƒฃ MainViewController

  • ์ฒ˜์Œ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋Š” ๋ฉ”์ธ ๋ทฐ์ž…๋‹ˆ๋‹ค.
  • 3๊ฐœ์˜ ProjectListViewController๋ฅผ ์ฐจ์ผ๋“œ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

5๏ธโƒฃ ProjectListViewController

  • ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ๊ฐ€์ง€๋Š” MainViewController์˜ ์ฐจ์ผ๋“œ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ์ž…๋‹ˆ๋‹ค.
  • ํ…Œ์ด๋ธ” ๋ทฐ ์™€ ํ—ค๋” ๋ทฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ๊ฐ๊ฐ ๋‹ด๋‹นํ•˜๋Š” ํ”„๋กœ์ ํŠธ ์ƒํƒœ์— ๋งž๊ฒŒ ํ™”๋ฉด์— ์ถœ๋ ฅํ•ด์ค๋‹ˆ๋‹ค.

๐ŸŸ  ViewModel

1๏ธโƒฃ AddViewModel

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅ ํ›„ ์ €์žฅํ•˜๋ฉด, ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€ ViewModelDelegate๋ฅผ ํ†ตํ•ด MainViewModel๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.

2๏ธโƒฃ EditViewModel

  • ์‚ฌ์šฉ์ž๊ฐ€ ์…€์„ ํ„ฐ์น˜ํ•˜๋ฉด ๊ทธ ์…€์— ์žˆ๋Š” ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ project ํ”„๋กœํผํ‹ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ Edit ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด isEditing ํ”„๋กœํผํ‹ฐ ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ • ํ›„ ์ €์žฅํ•˜๋ฉด, ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€ ViewModelDelegate๋ฅผ ํ†ตํ•ด MainViewModel๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.

3๏ธโƒฃ HeaderViewModel

  • ProjectListViewController๊ฐ€ ์ดˆ๊ธฐํ™” ๋  ๋•Œ title๊ณผ cellCount๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
  • ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚ผ cellCount์˜ ๋‚ด์šฉ์„ String์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

4๏ธโƒฃ MainViewModel

  • ๊ฐ๊ฐ ์ƒํƒœ๋ณ„ Project ๋ฐฐ์—ด์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ํ™”๋ฉด ํ…Œ์ด๋ธ” ๋ทฐ ์…€์— ๋‚˜ํƒ€๋‚ผ ๋‚ด์šฉ์„ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ๋กœ ์ถ”๊ฐ€ํ•œ ํ”„๋กœ์ ํŠธ๋‚˜ ์ˆ˜์ •ํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐ ํ…Œ์ด๋ธ” ๋ทฐ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋กฑ ํ”„๋ ˆ์Šค ์ œ์Šค์ณ๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์ด๋ฅผ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

5๏ธโƒฃ ProjectCellViewModel

  • ๊ฐ ์…€์— ๋“ค์–ด๊ฐˆ ๋‚ด์šฉ 3๊ฐ€์ง€๋ฅผ ๊ฐ๊ฐ ์…€์— ๋“ค์–ด๊ฐˆ ๋‚ด์šฉ์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋งˆ๊ฐ์ผ์ด ์ง€๋‚ฌ๋Š”์ง€ ํŒ๋‹จํ•˜๊ณ  ์ง€๋‚ฌ๋‹ค๋ฉด ๋นจ๊ฐ„์ƒ‰์„ ๊ฐ™์ด ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.

๐ŸŸข Model

1๏ธโƒฃ Project

  • ์ œ๋ชฉ, ๋งˆ๊ฐ์ผ, ์ƒ์„ธ๋‚ด์šฉ, ์ƒํƒœ๋ฅผ ๊ฐ๊ฐ String๊ณผ Date, State ํƒ€์ž…์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ชจ๋ธ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
  • ๊ฐ ๊ฐœ์ฒด๋Š” UUID๋ฅผ idํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ๋‚ด์šฉ์ด ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋”๋ผ๋„ ์ด ๊ฐ์ฒด๋“ค์„ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ State

  • ํ”„๋กœ์ ํŠธ์˜ ์ƒํƒœ๋ฅผ Todo, Doing, Done 3๊ฐ€์ง€๋กœ ์ œํ•œํ•˜๋Š” ์—ด๊ฑฐํ˜• ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
  • ๊ฐ ์ƒํƒœ์˜ ์ด๋ฆ„์„ ์•Œ๋ ค์ฃผ๋Š” name ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๐ŸŸฃ Delegate

1๏ธโƒฃ ViewModelDelegate

  • AddViewModel๊ณผ EditViewModel์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ์ž‘์—…์„ ๋Œ€์‹  ์ˆ˜ํ–‰ํ•˜๋Š” ๋Œ€๋ฆฌ์ž ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

2๏ธโƒฃ ProjectListActionDelegate

  • ๋ฆฌ์ŠคํŠธ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋Œ€์‹  ๋„˜๊ฒจ์ค„ ๋Œ€๋ฆฌ์ž ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

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

โš™๏ธ ์ฑ…์ž„ ์ฃผ๋„ ์„ค๊ณ„

ํŽผ์ณ๋ณด๊ธฐ
  • ์‹œ์Šคํ…œ์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ธ ์‹œ์Šคํ…œ ์ฑ…์ž„์„ ํŒŒ์•…ํ•˜๊ณ  ์ด๋ฅผ ๋” ์ž‘์€ ์ฑ…์ž„์œผ๋กœ ๋ถ„ํ• ํ•˜์—ฌ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ฐพ์•„ ์ฑ…์ž„์„ ํ• ๋‹นํ•˜๋Š” ์„ค๊ณ„๋ฒ•์ž…๋‹ˆ๋‹ค.
  • ๊ฐ์ฒด๊ฐ€ ์™ธ๋ถ€์— ๋ณด์—ฌ์ค˜์•ผ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์ข‹์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์บก์Šํ™”์— ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ๊ฐ์ฒด์—๊ฒŒ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ๊ณ  ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ ธ์•ผํ•  ์ฑ…์ž„์ด ๋šœ๋ ทํ•ด์ ธ์„œ ์‘์ง‘๋„๊ฐ€ ๋†’์€ ์„ค๊ณ„๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๐Ÿ’ก ์ฑ…์ž„ ์ฃผ๋„ ์„ค๊ณ„๋ฅผ ํ†ตํ•ด ์ข€ ๋” ๊ฐ์ฒด์ง€ํ–ฅ์ ์ด๊ณ , ์—ญํ• ์ด ๋šœ๋ ทํ•˜๊ณ  ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ ์ž ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

โš™๏ธ SPM

ํŽผ์ณ๋ณด๊ธฐ
  • Swift Package Manager๋Š” Xcode 11 ๋ถ€ํ„ฐ ์• ํ”Œ์—์„œ ๊ณต์‹์œผ๋กœ ์ง€์›ํ•˜๊ณ  Xcode์— ๋‚ด์žฅ๋œ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋„๊ตฌ๋กœ, Cocoa Pod์— ๋น„ํ•ด package๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์‰ฝ๊ณ  podfile์„ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋˜, ์จ๋“œํŒŒํ‹ฐ์ธ CocoaPods ๊ณผ๋Š” ๋‹ฌ๋ฆฌ ํผ์ŠคํŠธํŒŒํ‹ฐ ํˆด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์ธ ์„ค์น˜๊ฐ€ ํ•„์š”์—†๋Š” ์žฅ์ ๋„ ์žˆ๊ณ , Xcode์— ๋‚ด์žฅ๋œ ๋„๊ตฌ์ด๊ธฐ ๋•Œ๋ฌธ์— Xcode ์‚ฌ์ด๋“œ๋ฐ”์— ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œ์‹œํ•˜๊ณ  ํŒจํ‚ค์ง€์˜ ํ˜„์žฌ ๋ฒ„์ „๋„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

  • ๐Ÿ’ก ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Firebase ๋“ฑ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๋Š”๋ฐ, ์ข€ ๋” ํŽธ๋ฆฌํ•˜๊ณ  ์ง๊ด€์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” SPM์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ์„ ๋†’์—ฌ๋ณด๊ณ ์ž ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

โš™๏ธ MVVM ์•„ํ‚คํ…์ณ

ํŽผ์ณ๋ณด๊ธฐ
  • MVVM์˜ ๊ฐ„๋‹จํ•œ ์žฅ๋‹จ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ 
    • View์™€ Model์ด ์„œ๋กœ ์ „ํ˜€ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ์— ๋…๋ฆฝ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๊ณ , ์œ ๋‹›ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • ๊ฐ ์—ญํ• ๊ณผ ์šฉ๋„์— ๋”ฐ๋ผ ๊ตฌ๋ถ„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๋‹จ์ 
    • ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์ด ํ•„์ˆ˜์ ์œผ๋กœ ์š”๊ตฌ๋˜์–ด ์„ค๊ณ„์˜ ์–ด๋ ค์›€์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๊ฐ€ ๋งŽ์ด ์—†๊ณ , UI๊ฐ€ ๊ฐ„๋‹จํ•œ ์•ฑ์—์„œ๋Š” ์˜คํžˆ๋ ค ์„ค๊ณ„๊ฐ€ ์–ด๋ ค์›Œ ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๐Ÿ’ก MVC์—์„œ ViewController๊ฐ€ ๋งŽ์€ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ญํ• ์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ MVVM์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ” ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ๋ฐ ๊ณ ๋ฏผ

๐Ÿš€ ๋™๊ทธ๋ผ๋ฏธ๋กœ ๊ฐ์‹ผ ์ˆซ์ž ๊ทธ๋ฆฌ๊ธฐ

ํŽผ์ณ๋ณด๊ธฐ

๋ฌธ์ œ ๐Ÿ‘ป

  • ํ”„๋กœ์ ํŠธ ์š”๊ตฌ์‚ฌํ•ญ ์ค‘ ๊ฐ ์ƒํƒœ์˜ ๊ฐฏ์ˆ˜๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ์ˆซ์ž๋ฅผ ๋™๊ทธ๋ผ๋ฏธ ๋ชจ์–‘์œผ๋กœ ๊ฐ์‹ธ์„œ ๋ณด์—ฌ์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋™๊ทธ๋ผ๋ฏธ ๋ชจ์–‘์˜ ๋ทฐ๋ฅผ ๊ทธ๋ฆฌ๊ณ  ๊ทธ์œ„์— ์ˆซ์ž ๋ ˆ์ด๋ธ”์„ ์˜ฌ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ–ˆ์ง€๋งŒ ๋„ˆ๋ฌด ๋ณต์žกํ•œ ๋ฐฉ๋ฒ•์ด๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ˆซ์ž์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์งˆ๊ฒฝ์šฐ, ๋ ˆ์ด๋ธ” ํฌ๊ธฐ์— ๋งž๊ฒŒ ๋ทฐ๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ด ๋„ˆ๋ฌด ๋ณต์žกํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๐Ÿ”ซ

final class CountLabel: UILabel {
    private let padding = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        layer.masksToBounds = true
        layer.cornerRadius = rect.height / 2
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: padding))
    }

    override var intrinsicContentSize: CGSize {
        var contentSize = super.intrinsicContentSize
        contentSize.height += padding.top + padding.bottom
        contentSize.width += padding.left + padding.right

        return contentSize
    }
}
  • ๋ ˆ์ด๋ธ”์˜ layer์— ์ ‘๊ทผํ•˜์—ฌ cornerRadius๊ฐ’์„ ์ฃผ๊ณ  backgroundColor๋ฅผ ๊ฒ€์ •์ƒ‰์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ๋ ˆ์ด๋ธ” ๊ธธ์ด์— ๋”ฐ๋ผ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ•˜๋„๋ก ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ, UILabel์€ padding๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—, intrinsicContentSize๋ฅผ ์ด์šฉํ•˜์—ฌ padding๊ฐ’์„ ์ฃผ๋„๋ก ์ปค์Šคํ…€ ๋ ˆ์ด๋ธ”์„ ๋งŒ๋“ค์–ด ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์–‘์˜ ๋™๊ทธ๋ผ๋ฏธ์™€ ์บก์Š๋ชจ์–‘์„ ๊ฐ€์ง€๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๐Ÿ’ญ MVVM์‚ฌ์šฉ์— ๊ด€ํ•œ ๊ณ ์ฐฐ

ํŽผ์ณ๋ณด๊ธฐ
  • MVVM ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ ์šฉํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์žˆ์–ด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์žฅ๋‹จ์ ์„ ์ง์ ‘ ๋Š๊ปด๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋จผ์ €, ํ™•์‹คํžˆ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์˜ ํฌ๊ธฐ๊ฐ€ ๋งŽ์ด ์ค„์–ด๋“ค๊ณ  ๊ฐ์ž์˜ ์—ญํ• ์— ๋งž๊ฒŒ ๊ฐ์ฒด๋ฅผ ๋‚˜๋ˆ„์–ด ์ข€ ๋” ๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ ์„ค๊ณ„๋ฅผ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋”๋ถˆ์–ด, ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์˜ ์—ญํ• ์„ ๋ทฐ๋ฅผ ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž์˜ ์ธํ„ฐ๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ์—ญํ• ๋กœ ์“ฐ๋‹ค๋ณด๋‹ˆ, ๊ฐ์ฒด๊ฐ„ ์˜์กด๋„๋ฅผ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€์ง€๊ฒŒ ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ์ด๋ฅผ ์ด์šฉํ•ด ๊ฐ์ฒด๊ฐ„ ๋Š์Šจํ•œ ๊ฒฐํ•ฉ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ณผ์ •์—์„œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•˜๋‹ค๋ณด๋‹ˆ ํ๋ฆ„์„ ์ฝ๊ธฐ ์–ด๋ ค์›Œ ์—ฌ๋Ÿฌ๋ฒˆ ๋‹ค์‹œ ์ฝ์œผ๋ฉด์„œ ์ดํ•ดํ•˜๋Š” ์‹œ๊ฐ„๋“ค์ด ์ถ”๊ฐ€์ ์œผ๋กœ ํ•„์š”ํ–ˆ๊ณ , ์ฝ”๋“œ์˜ ๋ณต์žก๋„๊ฐ€ ๋งŽ์ด ์˜ฌ๋ผ๊ฐ”๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ, ๊ฐ์ฒด๊ฐ„ ์—ญํ• ์— ์ง‘์ค‘ํ•ด์„œ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋‹ˆ MVC๋ณด๋‹ค ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ํ˜„์ €ํžˆ ๋Š๋ ค์ง€๋Š” ๊ฒƒ์„ ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ Functional Reactive Programming์„ ์ข€ ๋” ๊ณต๋ถ€ํ•ด๋ด์•ผ๊ฒ ๋‹ค ์ƒ๊ฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๐Ÿ’ญ ๋ชจ๋ธ์˜ state๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„์ง€์„๊นŒ

ํŽผ์ณ๋ณด๊ธฐ
  • enum ์‚ฌ์šฉ
    • ocp๋ฅผ ์œ„๋ฐ˜ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์กด์žฌํ•œ๋‹ค.
    • case๊ฐ€ ์ถ”๊ฐ€๋  ๊ฒฝ์šฐ ๊ธฐ์กด์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.
  • ๋ชจ๋ธ ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•˜์—ฌ ๊ฐ state๋ณ„ ๊ตฌ์ฒด ํƒ€์ž… ์ƒ์„ฑ
    • state๊ฐ€ ์ถ”๊ฐ€๋˜๋”๋ผ๊ณ  ๊ธฐ์กด์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Œ
    • ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•œ๋‹ค.

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ ํ•ต์‹ฌ ๋กœ์ง ์ค‘ ํ•˜๋‚˜๋Š” todo, doing, done ์„ธ๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ๋กœ์ง์ด๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ๋ธ ์•ˆ์— enum์„ ์‚ฌ์šฉํ•ด์„œ ๊ฐ ์ผ€์ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ ์ผ€์ด์Šค๋ณ„ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š”๊ฒƒ์ด ๋งž๋Š”๊ฒƒ์ธ๊ฐ€? ์•„๋‹ˆ๋ฉด ์„ธ๊ฐ€์ง€์˜ ๊ฐ๊ฐ์˜ ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ๋งŒ๋“ค๊ณ  ๊ทธ ๋ชจ๋ธ์— ํ•ด๋‹นํ•˜๋Š” ViewModel๊ณผ View๋ฅผ ๊ฐ๊ฐ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋งž๋Š”๊ฒƒ์ธ๊ฐ€? ๋ผ๋Š” ๊ณ ๋ฏผ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ ์•„์˜ˆ ๋ฐฉํ–ฅ์„ฑ์ด ์ž˜๋ชป๋œ ๊ฒƒ์€ ์•„๋‹Œ๊ฐ€? ์„ธ๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ๊ผญ ๋ถ„๋ฅ˜ํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š”๊ฐ€? ์ƒํƒœ๋ผ๋Š” ํ•˜๋‚˜์˜ ๊ฐ’ ๋•Œ๋ฌธ์— ์„ธ๊ฐ€์ง€์˜ ๋ชจ๋ธ์ด ์ƒ๊ธฐ๋Š” ๊ฒƒ์ด ์˜ณ์€ ๋ฐฉํ–ฅ์„ฑ์ธ๊ฐ€? ์ƒํƒœ๋Š” ๋ชจ๋ธ์˜ ํ•œ๊ฐ€์ง€์˜ ์†์„ฑ์— ๋ถˆ๊ณผํ•œ๋ฐ, ์ด ํ”„๋กœํผํ‹ฐ ํ•˜๋‚˜ ๋•Œ๋ฌธ์— ๋ชจ๋ธ์ด ๋ถ„๊ธฐ ๋˜๋Š” ๊ฒƒ์ด ๋งž๋Š” ๊ฒƒ์ธ๊ฐ€? ๋ผ๋Š” ๊ณ ๋ฏผ์— ๋„๋‹ฌํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

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

๐Ÿ’ญ Date์˜ ์ฒ˜๋ฆฌ

ํŽผ์ณ๋ณด๊ธฐ

Dateํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋ธ์ด ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ์€ Dateํƒ€์ž…์œผ๋กœ Deadline์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ๋ทฐ๋Š” ์ด๋ฅผ String์œผ๋กœ ๋ฐ›์•„์„œ ํ‘œํ˜„ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์„ ํŠธ๋ Œ์Šคํผ ํ•˜๋Š” ๊ฐ์ฒด๋Š” ๋ˆ„๊ฐ€ ๋˜์•ผ ํ•˜๋Š”์ง€ ๊ณ ๋ฏผํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ Cell์˜ ViewModel์„ ๋งŒ๋“ค์–ด Dateํƒ€์ž…์˜ ๊ฐ’์„ String์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์„ ๊ฐ€์ง€๊ฒŒ ํ•˜๊ณ  Date Extension์„ ํ†ตํ•ด์„œ DateFormatter์™€ formatting๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๐Ÿ’ญ MainViewModel์—์„œ ๊ฐ ํ…Œ์ด๋ธ”๋ทฐ์— ๋ฟŒ๋ฆด ๋ชจ๋ธ์„ ์™œ ์ชผ๊ฐฐ๋Š”๊ฐ€

ํŽผ์ณ๋ณด๊ธฐ

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

๐Ÿ”— ์ฐธ๊ณ  ๋งํฌ

[๊ณต์‹๋ฌธ์„œ]

Swift Package Manager
intrinsicContentSize
Firebase
SwiftLint Rules


โฌ†๏ธ ๋งจ ์œ„๋กœ ์ด๋™ํ•˜๊ธฐ

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages