Skip to content

Conversation

@KuKaH
Copy link
Collaborator

@KuKaH KuKaH commented Dec 17, 2025

🍎 iOS Pull request

  • Combine 스터디 과제

🏓 구현 내용

  • 영화진흥위원회 open api를 mvvm + combine으로 연결

datePicker

private let datePicker = UIDatePicker().then {
        $0.datePickerMode = .date
        $0.date = Date()
        $0.preferredDatePickerStyle = .wheels
        $0.locale = Locale(identifier: "ko_KR")
    }

일별 박스오피스 API를 활용했기 때문에 DatePicker를 활용하여 날짜를 선택하고, 해당 날짜에 맞는 박스오피스 데이터를 조회할 수 있게 했습니다.

Input/Output 패턴

struct Input {
    let inquiryButtonTapped: AnyPublisher<Date, Never>
}

struct Output {
    let boxOfficeList: AnyPublisher<[DailyBoxOffice], Never>
    let isLoading: AnyPublisher<Bool, Never>
}

Input

  • inquiryButtonTapped: 조회 버튼 탭 이벤트와 선택된 Date 전달

Output

  • boxOfficeList : API 응답으로 받은 박스오피스 영화 목록
  • isLoading : API 호출 중 로딩 상태

Input/Output 패턴을 사용하여 View에서 발생한 이벤트와 ViewModel에서 관리하는 상태를 명확히 분리했습니다.

transform 함수

func transform(input: Input) -> Output {
        let isLoadingSubject = CurrentValueSubject<Bool, Never>(false)
        let boxOfficeListSubject = CurrentValueSubject<[DailyBoxOffice], Never>([])
        
        input.inquiryButtonTapped
            .map { date -> String in
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyyMMdd"
                formatter.locale = Locale(identifier: "ko_KR")
                return formatter.string(from: date)
            }
            .handleEvents(receiveOutput: { _ in
                isLoadingSubject.send(true)
            })
            .flatMap { [boxOfficeService] dateString in
                boxOfficeService.fetchDailyBoxOffice(date: dateString)
                    .catch { _ in Just([])}
            }
            .sink { movies in
                isLoadingSubject.send(false)
                boxOfficeListSubject.send(movies)
            }
            .store(in: &cancellables)
        
        return Output(boxOfficeList: boxOfficeListSubject.eraseToAnyPublisher(), isLoading: isLoadingSubject.eraseToAnyPublisher())
    }
  1. map : API 요청 형식에 맞게 문자열을 변환
  2. flatMap : 비동기 API 호출처리
  3. sink : 결과 받아서 로딩 종료 및 데이터 전달

처음에는 sink 안에서 또 sink를 사용하니 중첩 구조가 복잡했는데, flatMap을 활용하여 비동기 데이터 흐름을 깔끔하게 처리했습니다.

📸 스크린샷

기능 스크린샷

☠️ 어려웠던 점

벌써 스터디 막바지네요 ㄷㄷ
모두 고생 많으셨습니다~~

@KuKaH KuKaH self-assigned this Dec 17, 2025
Copy link
Collaborator

@y-eonee y-eonee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋습니다~~

Comment on lines +20 to +22
struct DailyBoxOffice: Decodable {
let rnum: String // 순번
let rank: String // 순위
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꿀팁: cmd + a -> cmd + i 하면 줄바꿈됩니다

Comment on lines +42 to +56
output.boxOfficeList
.sink { [weak self] list in
self?.boxOfficeList = list
self?.contentView.movieTableView.reloadData()
}
.store(in: &cancellables)

output.isLoading
.sink { [weak self] isLoading in
self?.contentView.setLoading(isLoading)
}
.store(in: &cancellables)

contentView.onTapInquiry = { [weak self] date in
self?.inquiryButtonTappedSubject.send(date)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굿

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants