๋ฐ๊นฅ์์ ์๋ผ๋ ์ก์ด๋ค์ ์ด๋์๋ ์ ์๋ผ๋๋ฐ, ์ง์ ์๋ ์๋ฌผ๋ค์ ์์ด๋ ๊ฒ ์ฝํ๊ณ ์์ด ๋ง์ด ๊ฐ๊น.
์ผ๋ง ์ , ์ญ ์์์ ์ฌ์จ ํ ์ด๋ธ์ผ์์ ์คํํฐํ๋ฆ์ด ์๋ค์๋คํด์ง๊ธฐ ์์ํ๋ค. ์ถํด๊ทผ์ ํ๋ค๋ณด๋ฉด ์๋ฌด๋๋ ๋ฌผ ์ฃผ๋ ์ฃผ๊ธฐ๋ฅผ ์์ฃผ ์๊ฒ ๋๋ค๋ณด๋ ์ด๋ค ๋๋ ํ์ด ๋ง๋ฅด๋๋ก ๋ฌผ์ ์ฃผ์ง ์๊ณ , ์ด๋ค ๋๋ ์ฃผ๊ณ ๋ ๊น๋จน๊ณ ๋ ์ฃผ๊ธฐ๋ ํ๋ ๋ถ์์ฌ๊ฐ ์ผ์ด๋๊ธฐ๋ ํ๋ค. (์๋ฌผ ์ ์ฅ์์๋ ๋ถ๋ง์ด ์ด๋ง์ ๋ง์ด ์๋ ๊ฑฐ๋ค..)
์ด๋ฌ์ ๋ฌํ ์ด์ ๋ก ๋จํธ์๊ฒ ์๋ฌผ ๋ฌผ์ฃผ๊ธฐ ์ ๋ฌด๋ฅผ ์ธ์์ธ๊ณ ํ๊ณ ์ ํ๋๋ฐ, ๋จํธ์ ๋ณธ์ธ์ด ์ํ๋ ๊ฐ๋จํ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์ด๋ฌ๋ผ๊ณ ์๊ตฌํ๋ค.
์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ๋ค.
1. ์๋ฌผ ์ ๋ณด CRUD
2. ์๋ฌผ ์ฃผ์ ์ ๋ณด๋ฅผ ๋ฆฌ์คํธ๋ก ํ ๋์ ์ฝ๊ฒ ๋ณผ ์ ์์ด์ผ ํ๋ค.
- ์ด๋ฆ / ์ฌ์ง / ๋ฌผ์ฃผ๋ ๋ D-Day
3. โ๋ฌผ์ฃผ๊ธฐโ๋ฅผ ๋ฆฌ์คํธ์์ ๋ฐ๋ก ํ ์ ์์ด์ผ ํ๋ค.
4. ๋ฌผ์ฃผ๋ ๋ ์ ์์ง ์๊ฒ ์๋ฆผ์ด ์์ด์ผ ํ๋ค.
- ํ๋ฃจ ์ AM 10:00
- ๋น์ผ PM 12:00
4. ๋ฌผ ์ค ๊ธฐ๋ก๋ ์ง๋ 10์ผ ์ ๋ ์์ผ๋ฉด ์ข๊ฒ ๋ค.
๊ธฐ๋ฅ์ ๊ฐ๋จํด์ ๊ฐ๋ฐ์ ์ฃผ๋ง๋์ ์๋ฃ๋ฅผ ํ๊ณ , ์ฑ์คํ ์ด ์ฌ๋ ค์ผํ ์ค์ (์ฑ ๋ฑ๋ก, ์คํฌ๋ฆฐ์ท ์ ์ ๋ฑ)์ ์ฝ ํ๋ฃจ ์ ๋ ๊ฑธ๋ฆฐ ๋ฏ ํ๋ค. ์ฑ์คํ ์ด ์ฌ์ฌ๊น์ง ํด์ ์ด 3-4์ผ ์ ๋ ์์๋์๋ค. (ํ๋ฒ ์นด๋ฉ๋ผ ๋ฉ์ธ์ง ๋์ถฉ ์จ์ ๋ฆฌ์ ๋จ -ใ -..)
๊ฐ์ฅ ์ฃผ์ ๊ธฐ๋ฅ์ธ ์๋ฆผ ์๊ฐ์ ๋ํด์๋ Calendar
๋ฅผ ์ด์ฉํด์ ์ฝ๊ฒ ํด๊ฒฐํ ์ ์์๋ค. (์ฒ์์๋ ๋ค์ ์๊ฐ๊น์ง์ TimeInterval์ ๊ณ์ฐํ๋ ค๊ณ ํ๊ณ ์์์...)
/// ์์ฃผ ์ฐ๋ ๋ ์ง ๊ณ์ฐ์ ๋ํด์๋ extension์ผ๋ก ๋ง๋ค์ด ๋๋๊ฒ ํธํ๋ค.
extension TimeInterval {
func plusDay(dayAmount:Int) -> TimeInterval {
let plusSecondsAmount = (TimeInterval)((60 * 60 * 24) * dayAmount)
return self + plusSecondsAmount
}
func minusDay(dayAmount:Int) -> TimeInterval {
let minusSecondsAmount = (TimeInterval)((60 * 60 * 24) * dayAmount)
return self - minusSecondsAmount
}
}
protocol PlantPresentable {
var image: UIImage? { get set }
var name: String { get set }
var startAt: Date { get set }
var lastWateringAt: Date? { get set }
var period: Int { get set }
var wateringDateRecord: [Date]? { get }
}
extension PlantPresentable {
var needWateringDay: Date {
// ๋ง์ง๋ง์ผ๋ก ๋ฌผ ์ค ๋ ์ง + ๋ฌผ ์ค์ผ ํ๋ ์ฃผ๊ธฐ(period) Day๋ฅผ ๋ํ๊ณ
let needTimeInterval = (lastWateringAt ?? startAt).timeIntervalSince1970.plusDay(dayAmount: period)
return Calendar.current
.date(
bySettingHour: 10, minute: 0, second: 0,// ํด๋น ๋ ์ง์์์ ์ํ๋ ์๊ฐ์ ์ค์ ํด์ Date๋ฅผ ๊ตฌํ๋ค.
of: Date(timeIntervalSince1970: needTimeInterval)
) ?? Date()
}
// ์์์ ๊ตฌํ ๋ ์ง์ ํ๋ฃจ ์ ๋ ์
var needWateringAgoDate: Date {
return Calendar.current
.date(
bySettingHour: 12, minute: 0, second: 0,
of: Date(timeIntervalSince1970: needWateringDay.timeIntervalSince1970.minusDay(dayAmount: 1))
) ?? Date()
}
}
ํฐ ๋ฌธ์ ๊ฐ ๋ฌ๋๊ฑด ์์๊ณ , ๊ธฐ์ต์ ๋จ๋ ์๋ฌ๋ ๋ก์ปฌ ๋
ธํฐํผ์ผ์ด์
์ ๋ฑ๋กํ๋๋ฐ ์ ๊ทผ ๊ถํ ๋ฑ๋ก/๊ฑฐ์ ํ์ ์ด๋ฒคํธ์ ๋ํด
Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
์ด๋ฐ ์๋ฌ๊ฐ ๋ฐ์ํ๋ ๊ฒ.
์ฑ์ด ํ์ฐธ lock ์ํ์ ๋น ์ก๋ค๊ฐ ์ด๋ฒคํธ๊ฐ ์คํ๋์๋ค.
์ด ๋ถ๋ถ์ main thread๋ก ์ด๋ฒคํธ๋ฅผ ๋๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐ ํ๋ค.
class LocalNotificationManager {
func requestPermission(onAccess: @escaping () -> Void) {
UNUserNotificationCenter
.current()
.requestAuthorization(options: [.alert]) { granted, error in
if false == granted {
DispatchQueue.main.async {
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
}
return
}
guard granted == true && error == nil else { return }
onAccess()
}
}
(...์๋ต...)
}
ํด๊ฐ ๊ธฐ๊ฐ์ด๋ผ ๋๋ฉด์(?) ๋น ๋ฅด๊ฒ ๋ง๋ค๋ค ๋ณด๋ ๊ณ ์ณ์ผํ ๋ถ๋ถ์ด ๊ฝค ์๋ค. ์ถํ์ ๋ค์ด๊ฐ ์๋ฌผ ์ผ์ง๊ฐ์ ๋ค์ด์ด๋ฆฌ ๊ธฐ๋ฅ๋ ์ถ๊ฐ๋ ๋ ๋ฆฌํํ ๋ง๋ ํจ๊ป ํด์ผ๊ฒ ๋ค.
์๋ค์๋คํ๋ ์๋ฌผ๋ค์ ์๋ฆฌ๋ฅผ ์ฎ๊ฒจ์ค ์ดํ๋ก ์ํ๊ฐ ์กฐ๊ธ์ฉ ์ข์์ง๊ณ ์๋ค. ์ง์ด ์ตํด์ ์ธ์ง ๋ ์ด ์ ๋ง๋ฅด์ง ์์์ ๊ฑฑ์ ํ๋๋ฐ ์กฐ์ฌ๋ฅผ ํด๋ณด๋ ๋ฐ๋์ ์ฌ์ด์ค์ผ ํ๋์ ์ ํ๊ธฐ๋ฅผ ํ๋ ๋์คฌ๋ค.
๋ด ์ ํ๊ธฐ ....
๋ณ๊ฑด ์๋์ง๋ง ํด๊ฐ ๋์์ ์ค๋๋ง์ ์ฑ ๋ง๋ค์ด์ ๋ธ๋ก๊ทธ๋ฅผ ์ ์ด๋ดค๋ค.
๋ค์์๋ ๋ง๋ ์ง ์ข ๋ ๋ ํธ๋ก ๋
ธํธ ๊ธฐ๋ฅ ์
๋ฐ์ดํธ ํ๋ฉด์ ํฌ์คํ
๋ ํด๋ด์ผ๊ฒ ๋ค.
๐ฑ Jobcho AppStore Link