Skip to content
Merged
52 changes: 52 additions & 0 deletions MovieBooking/Data/DataSources/BookingDataSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// BookingDataSource.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation

protocol BookingDataSourceProtocol {
func saveBooking(_ bookingInfo: BookingInfo) async throws -> BookingInfo
func fetchAllBookings() async throws -> [BookingInfo]
func deleteBooking(id: String) async throws
}

final class LocalBookingDataSource: BookingDataSourceProtocol {
private let userDefaults: UserDefaults
private let bookingsKey = "com.moviebooking.bookings"

init(userDefaults: UserDefaults = .standard) {
self.userDefaults = userDefaults
}

func saveBooking(_ bookingInfo: BookingInfo) async throws -> BookingInfo {
var bookings = try await fetchAllBookings()
bookings.append(bookingInfo)

let encoder = JSONEncoder()
let data = try encoder.encode(bookings)
userDefaults.set(data, forKey: bookingsKey)

return bookingInfo
}

func fetchAllBookings() async throws -> [BookingInfo] {
guard let data = userDefaults.data(forKey: bookingsKey) else {
return []
}

let decoder = JSONDecoder()
return try decoder.decode([BookingInfo].self, from: data)
}

func deleteBooking(id: String) async throws {
var bookings = try await fetchAllBookings()
bookings.removeAll { $0.id == id }

let encoder = JSONEncoder()
let data = try encoder.encode(bookings)
userDefaults.set(data, forKey: bookingsKey)
}
}
28 changes: 28 additions & 0 deletions MovieBooking/Data/Repository/BookingRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// BookingRepository.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation

struct BookingRepository: BookingRepositoryProtocol {
private let dataSource: BookingDataSourceProtocol

init(dataSource: BookingDataSourceProtocol = LocalBookingDataSource()) {
self.dataSource = dataSource
}

func createBooking(_ bookingInfo: BookingInfo) async throws -> BookingInfo {
try await dataSource.saveBooking(bookingInfo)
}

func fetchBookings() async throws -> [BookingInfo] {
try await dataSource.fetchAllBookings()
}

func deleteBooking(id: String) async throws {
try await dataSource.deleteBooking(id: id)
}
}
61 changes: 61 additions & 0 deletions MovieBooking/Domain/Entity/BookingInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// BookingInfo.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation

struct BookingInfo: Identifiable, Codable, Equatable {
let id: String
let movieId: String
let movieTitle: String
let posterPath: String
let theaterId: Int
let theaterName: String
let showDate: Date // 상영 날짜
let showTime: String // 상영 시간 (예: "14:30")
let numberOfPeople: Int
let totalPrice: Int
let bookedAt: Date // 예매한 날짜

init(
id: String = UUID().uuidString,
movieId: String,
movieTitle: String,
posterPath: String,
theaterId: Int,
theaterName: String,
showDate: Date,
showTime: String,
numberOfPeople: Int,
totalPrice: Int,
bookedAt: Date = Date()
) {
self.id = id
self.movieId = movieId
self.movieTitle = movieTitle
self.posterPath = posterPath
self.theaterId = theaterId
self.theaterName = theaterName
self.showDate = showDate
self.showTime = showTime
self.numberOfPeople = numberOfPeople
self.totalPrice = totalPrice
self.bookedAt = bookedAt
}

/// 표시용 상영 날짜 (예: "2025년 10월 20일")
var displayShowDate: String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "yyyy년 M월 d일 (E)"
return formatter.string(from: showDate)
}

/// 전체 상영 정보 (예: "2025년 10월 20일 14:30")
var fullShowDateTime: String {
"\(displayShowDate) \(showTime)"
}
}
13 changes: 13 additions & 0 deletions MovieBooking/Domain/Entity/MovieTheater.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// MovieTheater.swift
// MovieBooking
//
// Created by 홍석현 on 10/18/25.
//

import Foundation

struct MovieTheater: Identifiable, Equatable {
let id: Int
let name: String
}
54 changes: 54 additions & 0 deletions MovieBooking/Domain/Entity/ShowTime.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// ShowTime.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation

struct ShowTime: Identifiable, Codable, Equatable {
let id: String
let date: Date
let time: String

init(id: String = UUID().uuidString, date: Date, time: String) {
self.id = id
self.date = date
self.time = time
}

/// 표시용 날짜 문자열 (예: "2025년 10월 20일")
var displayDate: String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "yyyy년 M월 d일"
return formatter.string(from: date)
}

/// 표시용 요일 (예: "월요일")
var displayWeekday: String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "EEEE"
return formatter.string(from: date)
}

/// 표시용 짧은 날짜 (예: "10/20 (월)")
var displayShortDate: String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "M/d"
let dateStr = formatter.string(from: date)

formatter.dateFormat = "E"
let weekdayStr = formatter.string(from: date)

return "\(dateStr) (\(weekdayStr))"
}

/// 전체 표시 (예: "2025년 10월 20일 14:30")
var fullDisplay: String {
"\(displayDate) \(time)"
}
}
28 changes: 28 additions & 0 deletions MovieBooking/Domain/Repository/BookingRepositoryProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// BookingRepositoryProtocol.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation
import Dependencies

protocol BookingRepositoryProtocol {
func createBooking(_ bookingInfo: BookingInfo) async throws -> BookingInfo
func fetchBookings() async throws -> [BookingInfo]
func deleteBooking(id: String) async throws
}

private enum BookingRepositoryKey: DependencyKey {
static let liveValue: any BookingRepositoryProtocol = BookingRepository()
static let previewValue: any BookingRepositoryProtocol = BookingRepository()
static let testValue: any BookingRepositoryProtocol = BookingRepository()
}

extension DependencyValues {
var bookingRepository: BookingRepositoryProtocol {
get { self[BookingRepositoryKey.self] }
set { self[BookingRepositoryKey.self] = newValue }
}
}
38 changes: 38 additions & 0 deletions MovieBooking/Domain/UseCase/CreateBookingUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CreateBookingUseCase.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation
import Dependencies

protocol CreateBookingUseCaseProtocol {
func execute(_ bookingInfo: BookingInfo) async throws -> BookingInfo
}

struct CreateBookingUseCase: CreateBookingUseCaseProtocol {
@Dependency(\.bookingRepository) var repository

func execute(_ bookingInfo: BookingInfo) async throws -> BookingInfo {
// 간단한 딜레이로 네트워크 호출 시뮬레이션
try await Task.sleep(nanoseconds: 500_000_000) // 0.5초

// Repository를 통해 저장
return try await repository.createBooking(bookingInfo)
}
}

private enum CreateBookingUseCaseKey: DependencyKey {
static let liveValue: CreateBookingUseCaseProtocol = CreateBookingUseCase()
static let previewValue: CreateBookingUseCaseProtocol = CreateBookingUseCase()
static let testValue: CreateBookingUseCaseProtocol = CreateBookingUseCase()
}

extension DependencyValues {
var createBookingUseCase: CreateBookingUseCaseProtocol {
get { self[CreateBookingUseCaseKey.self] }
set { self[CreateBookingUseCaseKey.self] = newValue }
}
}
38 changes: 38 additions & 0 deletions MovieBooking/Domain/UseCase/FetchBookingsUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// FetchBookingsUseCase.swift
// MovieBooking
//
// Created by 홍석현 on 10/19/25.
//

import Foundation
import Dependencies

protocol FetchBookingsUseCaseProtocol {
func execute() async throws -> [BookingInfo]
}

struct FetchBookingsUseCase: FetchBookingsUseCaseProtocol {
@Dependency(\.bookingRepository) var repository

func execute() async throws -> [BookingInfo] {
try await Task.sleep(nanoseconds: 300_000_000) // 0.3초
let bookings = try await repository.fetchBookings()

// 예매 날짜 최신순으로 정렬
return bookings.sorted { $0.bookedAt > $1.bookedAt }
}
}

private enum FetchBookingsUseCaseKey: DependencyKey {
static let liveValue: FetchBookingsUseCaseProtocol = FetchBookingsUseCase()
static let previewValue: FetchBookingsUseCaseProtocol = FetchBookingsUseCase()
static let testValue: FetchBookingsUseCaseProtocol = FetchBookingsUseCase()
}

extension DependencyValues {
var fetchBookingsUseCase: FetchBookingsUseCaseProtocol {
get { self[FetchBookingsUseCaseKey.self] }
set { self[FetchBookingsUseCaseKey.self] = newValue }
}
}
Loading