지난번에 문제였던 날짜문제부터 뜯어고치도록 해보자!
class Date {
var year : Int
var month : Int
var day : Int
init(year: Int, month: Int, day: Int) {
self.year = year
self.month = month
self.day = day
start()
}
func start () {
switch month {
case 1,3,5,7,8,10,12 :
if day > 31 {
day -= 31
month += 1
}
case 2 :
if day > 28 {
day -= 28
month += 1
}
case 4,6,9,11 :
if day > 30 {
day -= 30
month += 1
}
default:
break
}
if month > 12 {
year += 1
month -= 12
}
}
func showDate () -> String{
return "\(year)-\(month)-\(day)"
}
}
먼저 Date라는 클래스를 선언하고 연, 월, 일의 프로퍼티를 가지게 한다. 그리고 start()라는 메서드를 만들어 입력받은 월이 12를 넘어가면 다음년도로 넘어가고, 일도 월별로 switch문을 통해 해당 월의 최대 일수를 넘어가게 되면 다음달로 넘어가게 한다. 그리고 이 start() 메서드를 클래스의 생성자에서 실행되도록 코드를 작성해주었다. (이렇게 되면 윤년은 오류가 생길 것이다. 하지만 전체적인 구현이 목표이기에 이런부분은 기억해두고 이후 완성단계에서 수정하도록 하자.)
이제 room클래스의 bookingdays 프로퍼티가 String이 아닌 Date 클래스를 받는 배열이 되도록 바꿔준다.
class room {
let roomnuber : Int
let price : Int
var bookingdays : [Date] = []
init(roomnumber : Int, price : Int) {
self.roomnuber = roomnumber
self.price = price
}
}
이후 bookingroom() 메서드의 내용도 이에 맞게 수정해준다.
func stringToDate (string : String) -> Date{
var arr = string.components(separatedBy: "-") // 2023"-"07"-"21 => ['2023', '07', '21']
var year = Int(arr[0])!
var month = Int(arr[1])!
var day = Int(arr[2])!
var date = Date(year: year, month: month, day: day)
return date
}
func bookingroom(index : Int) {
if index < roomList.count && index != -1 {
print("희망하시는 체크인날짜를 입력하세요 ex)2023-07-21")
var str = getString()
var date = stringToDate(string: str)
var temp1 : [Date] = [date]
print("일수를 입력하세요 ex) 2023-07-21 체크인, 2023-07-23 체크아웃을 희망한다면 3(일)입니다.")
var term = getInt()
if money >= term * roomList[index].price{
for i in 1..<term {
temp1.append(Date(year: date.year, month: date.month, day: date.day+i))
// 2023-07-21, 2023-07-22, 2023-07-23 Date temp 3개의 날짜
}
for i in 0..<temp1.count {
if roomList[index].bookingdays.contains(temp1[i]){ // Date타입을 비교하기 위해선 Equatable프로토콜이 적용되어야한다.
print("죄송합니다 \(temp1[i].showDate())는 예약이 되어있습니다.")
temp1 = [] // 같은 원소가 발견된다면 빈배열로 수정
break
}
}
roomList[index].bookingdays.append(contentsOf: temp1)
}
else{
print("현재 소지금액이 부족합니다.")
}
}
}
먼저 "2023-07-23"와 같이 String형식으로 받은 데이터를 stringToDate()라는 메서드를 통해 date라는 이름의 Date 클래스 타입의 객체로 생성한다. 그리고 임시 배열 temp1을 생성하여 배열의 원소로 date를 넣어준다. 이후 term이라는 변수에 머무를 일수를 저장하고, term과 방의 가격을 이용하여 소지금액과 비교한다. 소지금이 조건을 만족하면 for문을 이용하여 체크인 날짜부터 term의 일수만큼 date타입의 객체를 만들어 temp1 배열에 추가한다. 이후 temp1의 모든 원소를 기존의 bookingdays의 원소와 contains를 이용하여 비교하고 같다면 예약되어있다고 판단하는 과정을 거친다.
여기서 문제가 발생하였는데, contains를 이용하여 비교할 경우 원시타입의 Int, String등의 데이터 타입만 비교가 가능하였다. 따라서 extension을 이용하여 Date클래스가 contains로 비교가 가능하도록 설정해주어야 했다.
extension Date : Equatable {
static func == (lhs : Date, rhs : Date) -> Bool {
return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day
}
}
Equatable은 Swift에서 제공하는 프로토콜이다. Date클래스에 Equatable프로토콜을 적용하고, 비교 메서드에 내가 원하는 조건을 설정해주면 된다. year, month, day가 모두 일치하여야 같은 객체라는 결과가 나오도록 설정하였다.
만약 lhs.day == rhs.day 라는 조건을 뺀다면 year, month만 일치해도 같은 객체로 인식할 것이다. 필요한 조건에 따라서 활용하면 좋을 것이다.
이렇게 비교를 하여 같은 원소가 발견된다면 temp1을 빈배열로 바꾸고 예약되어있는 날짜를 알려주는 출력문이 나오고 for문이 중지된다.
같은 원소가 발견되지 않는다면 temp1에 저장되어있는 배열을 기존의 bookingdays에 추가한다.
프로토콜을 적용하며 extension을 활용하자 떠오른 생각인데 기본적으로 지원되는 프레임워크의 Date에 extension을 활용했다면 좀 더 간단하게 구현을 했을 수 있겠다는 아쉬움이 들었다.
이렇게 우리는 예약페이지 까지 수정을 완료하였다!
이제 예약내역을 확인하는 페이지를 수정해보도록 하겠다.
기존에 사용하던 showbookingList()는 bookingdays에 있는 모든 내역들을 출력하기에 체크인 날짜와 체크아웃 날짜만 골라서 보여주기는 힘들다. 나는 bookingdays의 내용을 수정하는 과정에서 체크인날짜와 체크아웃날짜를 프로퍼티로 받는 Check라는 클래스를 만들어서 관리하기로 하였다.
func showDate () -> String{
return "\(year)-\(month)-\(day)"
}
class Check {
var checkin : String
var checkout : String
init(date1 : Date, date2 : Date) {
self.checkin = date1.showDate()
self.checkout = date2.showDate()
}
}
class room {
let roomnuber : Int
let price : Int
var bookingdays : [Date] = []
var bookingcheck : [Check] = [] // 체크인 날짜와 체크아웃 날짜를 저장하기 위한 프로퍼티
init(roomnumber : Int, price : Int) {
self.roomnuber = roomnumber
self.price = price
}
}
func bookingroom(index : Int) {
if index < roomList.count && index != -1 {
print("희망하시는 체크인날짜를 입력하세요 ex)2023-07-21")
var str = getString()
var date = stringToDate(string: str)
var temp1 : [Date] = [date]
var temp2 : [Check] = [] // temp1과 마찬가지로 임시 배열을 생성하여 Check타입의 데이터를 받는다.
print("일수를 입력하세요 ex) 2023-07-21 체크인, 2023-07-23 체크아웃을 희망한다면 3(일)입니다.")
var term = getInt()
if money >= term * roomList[index].price{
for i in 1..<term {
temp1.append(Date(year: date.year, month: date.month, day: date.day+i))
}
var check = Check(date1: (temp1.first)!, date2: (temp1.last)!) // 예약 날짜의 첫날과 마지막날
temp2.append(check)
for i in 0..<temp1.count {
if roomList[index].bookingdays.contains(temp1[i]){
print("죄송합니다 \(temp1[i].showDate())는 예약이 되어있습니다.")
temp1 = []
temp2 = [] //temp1과 마찬가지로 같은 원소가 발견된다면 빈 배열로 수정
break
}
}
roomList[index].bookingdays.append(contentsOf: temp1)
roomList[index].bookingcheck.append(contentsOf: temp2)
}
else{
print("현재 소지금액이 부족합니다.")
}
}
}
이부분은 추가된 내용이 어렵지않다. bookingdays와 마찬가지로 임시 배열 temp2를 생성한 뒤, temp1의 첫번째 원소(체크인날짜), 마지막 원소(체크아웃날짜)를 Check클래스의 객체를 생성한 뒤, temp1과 같은 조건문을 거쳐 거쳐 빈 배열이 되거나 그대로 저장되는 과정이다.
마지막으로 예약내역을 보여주는 함수를 수정한다.
func showbookingList () {
for i in 0..<roomList.count{
for k in 0..<roomList[i].bookingcheck.count{
print("호실 : \(roomList[i].roomnuber)호 체크인 : \(roomList[i].bookingcheck[k].checkin) 체크아웃 : \(roomList[i].bookingcheck[k].checkout)")
}
}
}
위의 내용을 잘 이해했다면 이부분은 큰 설명이 필요없을 것이다!
마지막으로 5번 페이지를 만들어 체크인 날짜 순으로 예약내역을 출력하는 과정은 스스로 구성해보자.
새로운 메서드를 생성하여 처리해도 좋고, main의 switch case에 직접 한줄한줄 작성해도 좋을 것이다.
5번 페이지의 코드도 올리고 설명하고 싶지만, 당분간은 팀프로젝트에 집중하고 이후 시간이 된다면 이 프로젝트의 뒷부분은 포스팅하도록 하겠다. 모두 화이팅~!
실행화면