Skip to content

just1103/AppStore_Clone

Repository files navigation

iOS 13.0 Swift 5.6 Xcode 13.4 Run Test

AppStore Clone ํ”„๋กœ์ ํŠธ

iTunes Search API๋ฅผ ํ†ตํ•ด ์•ฑ ID ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ธ ํ™”๋ฉด์— ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

Foldering

AppStoreClone
โ”œโ”€โ”€ App
โ”œโ”€โ”€ Presentation
โ”‚   โ”œโ”€โ”€ LookupScene
โ”‚   โ”‚   โ”œโ”€โ”€ ViewModel
โ”‚   โ”‚   โ””โ”€โ”€ View      
โ”‚   โ”œโ”€โ”€ DetailScene
โ”‚   โ”‚   โ”œโ”€โ”€ ViewModel
โ”‚   โ”‚   โ””โ”€โ”€ View   
โ”‚   โ””โ”€โ”€ ScreenshotScene
โ”‚       โ”œโ”€โ”€ ViewModel
โ”‚       โ””โ”€โ”€ View  
โ”œโ”€โ”€ Model
โ”œโ”€โ”€ Network
โ”‚   โ””โ”€โ”€ Response
โ”œโ”€โ”€ Protocols
โ”œโ”€โ”€ Extensions
โ”œโ”€โ”€ Utilities
โ””โ”€โ”€ Resources
AppStoreCloneTests
โ””โ”€โ”€ Mock

์ถ”๊ฐ€ ๊ตฌํ˜„ํ•œ ๊ธฐ๋Šฅ โœ…

  • ์ฝ”๋“œ๋กœ UI๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • MockURLSession์„ ํ™œ์šฉํ•œ ๋„คํŠธ์›Œํฌ ํ…Œ์ŠคํŠธ ๋“ฑ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒ์„ธํ™”๋ฉด์˜ ์ƒ๋‹จ์— ๊ณต์œ  ๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒ์„ธํ™”๋ฉด์— SummaryScrollView๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ํ‰์ , ์—ฐ๋ น, ์นดํ…Œ๊ณ ๋ฆฌ ๋“ฑ ์ฃผ์š” ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋ƒˆ์Šต๋‹ˆ๋‹ค.
  • ์•ฑ ํ‰์ ์„ ๋ณ„ ์ด๋ฏธ์ง€๋กœ ๋‚˜ํƒ€๋ƒˆ๊ณ , ํ‰๊ฐ€ ๊ฐœ์ˆ˜๋ฅผ ์ฒœ/๋งŒ ๋‹จ์œ„๋กœ ํ™˜์‚ฐํ•˜์—ฌ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
  • ์ƒ์„ธํ™”๋ฉด ์Šคํฌ๋ฆฐ์ƒท ํ•ญ๋ชฉ์—์„œ ํƒญํ•œ indexPath์˜ ํ•ญ๋ชฉ์„ ์Šคํฌ๋ฆฐ์ƒท ํ™•๋Œ€ ํ™”๋ฉด์—์„œ ๊ฐ€์žฅ ๋จผ์ € ๋ณด์ด๋„๋ก ๋‚˜ํƒ€๋‚˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒ์„ธํ™”๋ฉด ์ •๋ณด ํ•ญ๋ชฉ์„ InfoTableView๋กœ ๊ตฌํ˜„ํ•˜์—ฌ ์•ฑ์˜ ์„ธ๋ถ€์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋ƒˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฏธ์ง€ Cache๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ€๋กœ/์„ธ๋กœ ๋ชจ๋“œ ์ „ํ™˜์— ๋Œ€์‘ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ ์ „ํ™˜์— ๋Œ€์‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ชฉ์ฐจ

Feature-1. ๋„คํŠธ์›Œํฌ ๋ฐ ๊ฒ€์ƒ‰ํ™”๋ฉด ๊ตฌํ˜„

1-1 ์ฃผ์š” ๊ธฐ๋Šฅ

  • ์‚ฌ์šฉ์ž๊ฐ€ TextField์— ์ž…๋ ฅํ•œ ์•ฑ ID๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์—์„œ ์•ฑ ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.
  • ์•ฑ ID๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฉด Label์— ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • URLSession์„ ํ†ตํ•ด ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. (MockURLSession์„ ํ†ตํ•œ ํ…Œ์ŠคํŠธ ์‹คํ–‰)

1-2 ๊ตฌํ˜„ ๋‚ด์šฉ

1. MVVM-C ์ ์šฉ

Coordinator๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ด€๋ฆฌํ•˜๊ณ , ํ™”๋ฉด์ „ํ™˜ ์—ญํ• ์„ ์ „๋‹ดํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด navigationController๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…์œผ๋กœ ํ•˜์œ„ ChildCoordinator์— ์ „๋‹ฌํ•˜๊ณ , ํ™”๋ฉด์ „ํ™˜ ์‹œ ํ•ด๋‹น navigationController๊ฐ€ ๋‹ค์Œ ํ™”๋ฉด์„ push ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฒ€์ƒ‰ํ™”๋ฉด -> ์ƒ์„ธํ™”๋ฉด -> ์Šคํฌ๋ฆฐ์ƒท ํ™•๋Œ€ ํ™”๋ฉด ์ˆœ์œผ๋กœ ์—ฐ๊ฒฐ๋˜๋ฏ€๋กœ ๊ฐ ์ƒ์œ„ Coordinator์˜ childCoordinators์— ํ•˜์œ„ Coordinator๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ํ•˜์œ„ ํ™”๋ฉด์ด pop๋  ๋•Œ, ViewModel์˜ deinit์—์„œ ํ•ด๋‹น Coordinator๊ฐ€ finish๋˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ Delegate ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ์ƒ์œ„ Coordinator๊ฐ€ removeFromChildCoordinators(coordinator:)๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•˜์œ„ Coordinator๊ฐ€ childCoordinator์—์„œ ์ œ๊ฑฐํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

Debug Memory Graph๋ฅผ ํ†ตํ•œ ๋””๋ฒ„๊น…์œผ๋กœ Coordinator/ViewModel ๋“ฑ์ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ •์ƒ์ ์œผ๋กœ ํ•ด์ œ๋˜๋Š”์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

2. ๋„คํŠธ์›Œํฌ ๊ตฌํ˜„ ๋ฐ API ์ถ”์ƒํ™”

๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Combine ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋Š” Publisher ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ , ViewModel ๋ฐ ViewController๋ฅผ Bindingํ•˜์—ฌ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚ด๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ViewController์—์„œ PassthroughSubject ํƒ€์ž…์„ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ API๋ฅผ ์—ด๊ฑฐํ˜•์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ, API๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด case๊ฐ€ ํ•„์š”ํ•˜์—ฌ ์—ด๊ฑฐํ˜•์ด ๋น„๋Œ€ํ•ด์ง€๊ณ , ์—ด๊ฑฐํ˜• ๊ด€๋ จ switch๋ฌธ์„ ๋งค๋ฒˆ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ API๋ฅผ ๊ตฌ์กฐ์ฒด ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ , URL ํ”„๋กœํผํ‹ฐ ์™ธ์—๋„ HttpMethod ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•œ APIProtocol ํƒ€์ž…์„ ์ฑ„ํƒํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ตฌ์กฐ๋Š” ์ฝ”๋“œ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๋ฉฐ, ํ˜‘์—… ์‹œ ํŒ€์›์ด ๊ฐ์ž ๋‹ด๋‹นํ•œ API ๊ตฌ์กฐ์ฒด ํƒ€์ž…์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜์—ฌ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

3. MockURLSession์„ ํ†ตํ•œ ๋„คํŠธ์›Œํฌ ํ…Œ์ŠคํŠธ

์•„๋ž˜์˜ ๋ชฉ์ ์„ ์œ„ํ•ด MockURLSession์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ์‹ค์ œ ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ์˜ ์†๋„๊ฐ€ ๋Š๋ ค์ง
  • ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ƒํƒœ์— ๋”ฐ๋ผ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์‹ ๋ขฐ๋„๊ฐ€ ๋–จ์–ด์ง
  • ์‹ค์ œ ์„œ๋ฒ„์™€ ํ†ต์‹ ์„ ํ•˜๋ฉฐ ์„œ๋ฒ„์— ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์—…๋กœ๋“œ๋˜๋Š” Side-Effect๊ฐ€ ๋ฐœ์ƒํ•จ

๋˜ํ•œ ํ–ฅํ›„ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ํŒŒ์ผ์ด ๋Š˜์–ด๋‚  ๊ฒƒ์— ๋Œ€๋น„ํ•˜์—ฌ Mock ๋ฐ์ดํ„ฐ๋กœ JSON ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ณ , Bundle(for: type(of: self))๋กœ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ–ˆ์Šต๋‹ˆ๋‹ค.

Feature-2. ์ƒ์„ธํ™”๋ฉด ๊ตฌํ˜„

2-1 ์ฃผ์š” ๊ธฐ๋Šฅ

  • AppStore์˜ ์•ฑ ์†Œ๊ฐœ ํŽ˜์ด์ง€์™€ ์œ ์‚ฌํ•œ ๋””์ž์ธ์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ™”๋ฉด ์š”์†Œ๋ฅผ Main, Summary ScrollView, Screenshot CollectionView, Description Label, Info TableView๋กœ ๋‚˜๋ˆ„์–ด ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์•ฑ์˜ ์Šคํฌ๋ฆฐ์ƒท ์ด๋ฏธ์ง€๋ฅผ CollectionView๋กœ ๋ณด์—ฌ์ฃผ๊ณ , Cell์„ ํƒญํ•˜๋ฉด ์ด๋ฏธ์ง€๋ฅผ ํฌ๊ฒŒ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • ์•ฑ ์„ค๋ช… ์˜์—ญ์˜ ํŽผ์น˜๊ธฐ ๋ฒ„ํŠผ์„ ํƒญํ•˜๋ฉด ์„ค๋ช…์ด ํŽผ์ณ์ง‘๋‹ˆ๋‹ค. ํŽผ์ณ์ง„ ์ƒํƒœ์—์„œ ์ ‘๊ธฐ ๋ฒ„ํŠผ์„ ํƒญํ•˜๋ฉด ๋‹ค์‹œ ์„ค๋ช…์ด ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

2-2 ๊ตฌํ˜„ ๋‚ด์šฉ

1. Summary ScrollView ๋ฐ Vertical Separator ๊ตฌํ˜„

ScrollView ๋‚ด๋ถ€์— ContainerStackView๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ๊ทธ ๋‚ด๋ถ€์— ๋ณ„์ /์—ฐ๋ น/์นดํ…Œ๊ณ ๋ฆฌ/๊ฐœ๋ฐœ์ž/์–ธ์–ด ๋“ฑ ๊ฐ ํ•ญ๋ชฉ์˜ StackView๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ๊ฐ ํ•ญ๋ชฉ์„ EqualSpacing์œผ๋กœ ๋ฐฐ์น˜ํ•ด์•ผ ํ–ˆ์œผ๋ฏ€๋กœ Separator๋ฅผ ContainerStackView์— ๋„ฃ์œผ๋ฉด Layout์ด ๊นจ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ StackView Extension์„ ํ†ตํ•ด ArrangedSubview๊ฐ€ ์•„๋‹Œ Subview์— Separator๋ฅผ ๋„ฃ๊ณ , ๊ธฐ์กด ์ปจํ…์ธ ์˜ trailingAnchor ์œ„์น˜์— Separator๋ฅผ ๋ฐฐ์น˜ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

func addVerticalSeparators(color: UIColor = .systemGray3, heightRatio: CGFloat = 0.5, spacing: CGFloat = 0) {
    let separatorCount = arrangedSubviews.count - 1

    (0..<separatorCount).forEach { index in
        guard let subview = subviews[safe: index] else { return }

        let separatorView = createVerticalSeparatorView(color: color)
        addSubview(separatorView)

        NSLayoutConstraint.activate([
            separatorView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: heightRatio),
            separatorView.trailingAnchor.constraint(equalTo: subview.trailingAnchor, constant: spacing),
            separatorView.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }
}

2. ํ‰์ ์„ ์ด๋ฏธ์ง€๋ทฐ๋กœ ํ‘œ์‹œํ•˜๋Š” StarRatingStackView ๊ตฌํ˜„

๋ณ„์ ์„ ๋‚˜ํƒ€๋‚ผ StarImageView ์ข…๋ฅ˜๋ฅผ filled/halfFilled/empty๋กœ ๊ตฌ๋ถ„ํ–ˆ๊ณ , ํ‰์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ํ•„์š”ํ•œ ๊ฐœ์ˆ˜๋งŒํผ ์ข…๋ฅ˜๋ณ„ ImageView๋ฅผ ์ƒ์„ฑํ•˜์—ฌ StackView์— ์ถ”๊ฐ€ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๋กœ์ง๊ณผ ๊ฐ™์ด ์ •์ˆ˜๋ฅผ ๋บ€ ์†Œ์ˆ˜์  ์˜์—ญ์˜ ๊ฐ’์ด 0.5 ์ด์ƒ์ด๋ฉด halfFilledStar ์ด๋ฏธ์ง€๋ทฐ๊ฐ€ 1๊ฐœ ์ƒ์„ฑ๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

private func configureStarImageView(with rating: Double) -> [StarImageView] {
    let starCountByKind = calculateStarCountByKind(with: rating)

    var starImageViews = [StarImageView]()
    starImageViews += (0..<starCountByKind.filled).map { _ in StarImageView(kind: .filled) }
    starImageViews += (0..<starCountByKind.halfFilled).map { _ in StarImageView(kind: .halfFilled) }
    starImageViews += (0..<starCountByKind.empty).map { _ in StarImageView(kind: .empty) }

    return starImageViews
}

private func calculateStarCountByKind(with rating: Double) -> (filled: Int, halfFilled: Int, empty: Int) {
    let filledStarCount = Int(rating)

    let remainder = rating.truncatingRemainder(dividingBy: 1)
    let halfFilledStarCount = remainder >= 0.5 ? 1 : 0

    let emptyStarCount = maxStarCount - filledStarCount - halfFilledStarCount

    return (filledStarCount, halfFilledStarCount, emptyStarCount)
}

3. ScreenshotCollectionView ๊ตฌํ˜„

์ƒ์„ธํ™”๋ฉด ๋ฐ ์Šคํฌ๋ฆฐ์ƒท ํ™•๋Œ€ ํ™”๋ฉด์—์„œ CollectionView์˜ Compositional Layout์„ ํ™œ์šฉํ•˜์—ฌ Cell์„ ๋ฐฐ์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‘ ํ™”๋ฉด์˜ Cell ๊ตฌ์„ฑ์ด ๊ฐ™์œผ๋ฏ€๋กœ ๋ชจ๋‘ ScreenshotCell์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

Section์˜ orthogonalScrollingBehavior๋Š” ๊ฐ๊ฐ groupPaging, groupPagingCentered์œผ๋กœ ์„ค์ •ํ•˜์—ฌ AppStore์™€ ๋™์ผํ•˜๊ฒŒ Pagination์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ํ™”๋ฉด

๊ฒ€์ƒ‰ ํ™”๋ฉด ์ƒ์„ธ ํ™”๋ฉด ๊ฐ€๋กœ/์„ธ๋กœ ๋ชจ๋“œ ๋Œ€์‘ ๋‹คํฌ/๋ผ์ด๋“œ ๋ชจ๋“œ ๋Œ€์‘

๋ณด์™„ํ•  ์ 

  • ์ถ”๊ฐ€ ๊ตฌํ˜„ํ•  ๋‚ด์šฉ์„ ์ฃผ์„์œผ๋กœ ๋‚จ๊ฒจ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ณต์œ  ๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
    • ์ƒ์„ธํ™”๋ฉด์˜ ์Šคํฌ๋ฆฐ์ƒท์—์„œ ํƒญํ•œ ํ•ญ๋ชฉ์„ ์Šคํฌ๋ฆฐ์ƒท ํ™•๋Œ€ ํ™”๋ฉด์—์„œ ๋‚˜ํƒ€๋‚˜๋„๋ก ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
    • ์Šคํฌ๋ฆฐ์ƒท ํ™•๋Œ€ ํ™”๋ฉด์„ modal๋กœ ๋„์šฐ๋„๋ก ์ˆ˜์ •ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
    • SummaryScrollView์—์„œ ์นดํ…Œ๊ณ ๋ฆฌ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ImageView๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
    • Localization์„ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. (SummaryScrollView์˜ ์–ธ์–ด, ์นดํ…Œ๊ณ ๋ฆฌ๋ช…, InfoTableView์˜ ์–ธ์–ด ํ•ญ๋ชฉ ๋“ฑ)
    • Quick/Numble์„ ํ™œ์šฉํ•˜์—ฌ ViewModel ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • ๋ฆฌ๋ทฐ ๋…ธํŠธ : Lookup API ํŠน์„ฑ์ƒ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ ์ค‘ ํ‰๊ฐ€ ๊ฐœ์ˆ˜ (userRatingCount), ์•ฑ ํฌ๊ธฐ (fileSize) ๋“ฑ์ด ์‹ค์ œ AppStore์—์„œ ํ™•์ธ๋˜๋Š” ๊ฐ’๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

About

Combine+UIKit, MVVM-C

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages