Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
440 changes: 440 additions & 0 deletions MEGABOX/MEGABOX.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions MEGABOX/MEGABOX/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>$(API_KEY)</string>
<key>BASE_URL</key>
<string>$(BASE_URL)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>카카오네이티브앱키설정</string>
</array>
</dict>
</array>
<key>UIAppFonts</key>
<array>
<string>Pretendard-Black.otf</string>
<string>Pretendard-Bold.otf</string>
<string>Pretendard-ExtraBold.otf</string>
<string>Pretendard-Light.otf</string>
<string>Pretendard-Medium.otf</string>
<string>Pretendard-Regular.otf</string>
<string>Pretendard-SemiBold.otf</string>
<string>Pretendard-Thin.otf</string>
</array>
</dict>
</plist>
22 changes: 22 additions & 0 deletions MEGABOX/MEGABOX/MEGABOXApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// MEGABOXApp.swift
// MEGABOX
//
// Created by 박정환 on 12/17/25.
//

import SwiftUI

@main
struct MEGABOXApp: App {
@StateObject private var movieViewModel = MovieViewModel()

var body: some Scene {
WindowGroup {
NavigationStack {
MovieView()
}
.environmentObject(movieViewModel) // ⭐ 여기!
}
}
}
14 changes: 14 additions & 0 deletions MEGABOX/MEGABOX/MovieModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// MovieModel.swift
// MEGABOX
//
// Created by 박정환 on 12/17/25.
//

import Foundation

struct MovieModel: Identifiable, Hashable {
let id = UUID()
let title: String
let poster: String
}
89 changes: 89 additions & 0 deletions MEGABOX/MEGABOX/MovieView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// HomeView.swift
// MEGABOX
//
// Created by 박정환 on 12/17/25.
//

import SwiftUI
import Kingfisher

struct MovieView: View {
@EnvironmentObject private var viewModel: MovieViewModel

var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 24) {
Image(.meboxTitle)
.padding(.leading, 12)
.padding(.top, 16)

MovieChart
}
}
.task {
await viewModel.fetchNowPlaying()
}
}

private var MovieChart: some View {
VStack(alignment: .leading, spacing: 12) {
ScrollView(.horizontal, showsIndicators: false) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기부분 뎁스가 좀 깊은것같은데 리팩해봐도좋을듯여

HStack(spacing: 24) {
ForEach(viewModel.movies) { movie in
VStack(alignment: .leading) {
if movie.poster == "poster3" {
Button {
} label: {
ZStack {
KFImage(URL(string: movie.poster))
.resizable()
.frame(width: 148, height: 212)
.scaledToFit()
ProgressView()
.frame(width: 148, height: 212)
}
}
} else {
ZStack {
KFImage(URL(string: movie.poster))
.resizable()
.frame(width: 148, height: 212)
.scaledToFit()
ProgressView()
.frame(width: 148, height: 212)
}
}

Button(action: {}) {
Text("바로 예매")
.font(.medium16)
.foregroundColor(Color.purple03)
.frame(width: 148, height: 36)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.purple03, lineWidth: 1)
)
}
Text(movie.title)
.font(.bold22)
.frame(width: 148, alignment: .leading)
.lineLimit(1)
.truncationMode(.tail)
.padding(.bottom, 3)
}
}
}
}
}
.padding(.leading, 16)
}
}


#Preview {
NavigationStack {
MovieView()
}
.environmentObject(MovieViewModel())
}
45 changes: 45 additions & 0 deletions MEGABOX/MEGABOX/MovieViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// MovieViewModel.swift
// MEGABOX
//
// Created by 박정환 on 12/17/25.
//

import Foundation
import Moya
import Combine
import CombineMoya

final class MovieViewModel: ObservableObject {
@Published var movies: [MovieModel] = []
private var cancellables = Set<AnyCancellable>()

let service = MovieService()

func fetchNowPlaying() {
service.provider.requestPublisher(.nowPlaying(page: 1))
.map(\.data)
.decode(type: NowPlayingResponseDTO.self, decoder: JSONDecoder())
.map { dto in
dto.results.map {
MovieModel(
title: $0.title,
poster: "https://image.tmdb.org/t/p/w500" + ($0.posterPath ?? "")
)
}
}
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { completion in
if case .failure(let error) = completion {
print("현재 상영 중인 영화 불러오기 실패: \(error)")
self.movies = []
}
},
receiveValue: { [weak self] movies in
self?.movies = movies
}
)
.store(in: &cancellables)
}
}
53 changes: 53 additions & 0 deletions MEGABOX/MEGABOX/Network/MovieAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// MovieAPI.swift
// MEGABOX
//
// Created by 박정환 on 12/17/25.
//

import Foundation
import Moya
import Alamofire
Comment on lines +9 to +10
Copy link
Collaborator

Choose a reason for hiding this comment

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

오....모야랑 알라모를 둘다쓴이유가 무엇이죠


enum MovieAPI {
case nowPlaying(page: Int)
}

extension MovieAPI: TargetType {
var baseURL: URL {
return URL(string: "https://api.themoviedb.org/3")!
}

var path: String {
switch self {
case .nowPlaying:
return "/movie/now_playing"
}
}

var method: Moya.Method {
return .get
}

var task: Task {
switch self {
case .nowPlaying(let page):
// API Key를 쿼리 파라미터에 추가
let parameters: [String: Any] = [
"api_key": Bundle.main.object(forInfoDictionaryKey: "API_KEY") as? String ?? "",
"language": "ko-KR",
"page": page,
"region": "KR"
]
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
}
}

var headers: [String: String]? {
return ["accept": "application/json"]
}

var sampleData: Data {
return Data()
}
}
26 changes: 26 additions & 0 deletions MEGABOX/MEGABOX/Network/MovieService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// MovieService.swift
// MEGABOX
//
// Created by 박정환 on 12/17/25.
//

import Foundation
import Moya

final class MovieService {
let provider = MoyaProvider<MovieAPI>()

func request(_ target: MovieAPI) async throws -> Response {
try await withCheckedThrowingContinuation { continuation in
provider.request(target) { result in
switch result {
case .success(let response):
continuation.resume(returning: response)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}
Loading