From 2d902360b82a8eaef8fa4b582619edb42ff83737 Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Sat, 31 Oct 2020 14:32:21 -0600 Subject: [PATCH] Replace isCurrent calculation with "next expected entry date" --- Shared/APODClient.swift | 14 ++++++++++++-- Shared/Foundation+SpacePOD.swift | 11 +++++++++++ Shared/YearMonthDay.swift | 19 ++++++++----------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Shared/APODClient.swift b/Shared/APODClient.swift index 1a77816..81c1697 100644 --- a/Shared/APODClient.swift +++ b/Shared/APODClient.swift @@ -113,6 +113,11 @@ public class APODEntry: Codable { public static let placeholder: APODEntry = APODEntry(rawEntry: RawAPODEntry(date: YearMonthDay.today, hdurl: nil, url: nil, title: "Example", copyright: "Example copyright", explanation: nil, mediaType: "blah"), asset: .unknown(URL(fileURLWithPath: "/dev/null")), localDataURL: URL(fileURLWithPath: "/dev/null"), localImageURL: URL(fileURLWithPath: "/dev/null")) + + /// The earliest expected date that the next entry will be available from the server. + static func nextExpectedEntryDate(after entry: APODEntry) -> Date? { + return entry.date.nextDate(in: .losAngeles) + } } struct RawAPODEntry: Codable { @@ -214,10 +219,15 @@ public class APODClient { public func loadLatestImage() -> AnyPublisher { // Return the latest cached entry if it's not stale. if let lastCached = _cache.last?.value { - // TODO: maybe improve this by predicting the next expected entry date (midnight Pacific)? + // Would we expect to find a new entry if we queried the server now? + let newEntryExpected = APODEntry.nextExpectedEntryDate(after: lastCached) + .map { $0.timeIntervalSinceNow > 0 } ?? true + + // Has it been an hour since we last queried the server? let lastCacheDate = UserDefaults.spaceAppGroup.lastAPODCacheDate let cacheIsStale = lastCacheDate.map { -$0.timeIntervalSinceNow > 60*60 } ?? true - if !lastCached.date.isCurrent && cacheIsStale { + + if newEntryExpected && cacheIsStale { DBG("Cache is stale: \(lastCacheDate?.description ?? "never cached")") } else { DBG("Loaded \(lastCached.date) from cache") diff --git a/Shared/Foundation+SpacePOD.swift b/Shared/Foundation+SpacePOD.swift index b7693ad..e7bfe25 100644 --- a/Shared/Foundation+SpacePOD.swift +++ b/Shared/Foundation+SpacePOD.swift @@ -1,6 +1,17 @@ import Foundation import Combine +extension Calendar { + public static let losAngeles = configure(Calendar(identifier: .gregorian)) { + $0.locale = Locale(identifier: "en_US") + $0.timeZone = .losAngeles + } +} + +extension TimeZone { + public static let losAngeles = TimeZone(identifier: "America/Los_Angeles")! +} + public extension DateComponents { init(YMDString string: String) throws { let components = string.split(separator: "-") diff --git a/Shared/YearMonthDay.swift b/Shared/YearMonthDay.swift index 9a29e68..ce236c8 100644 --- a/Shared/YearMonthDay.swift +++ b/Shared/YearMonthDay.swift @@ -1,7 +1,5 @@ import Foundation -let TIME_ZONE_LA = TimeZone(identifier: "America/Los_Angeles")! - public struct YearMonthDay { public let year: Int public let month: Int @@ -31,21 +29,20 @@ public struct YearMonthDay { } public func asDate() -> Date? { - guard let date = Calendar.current.date(from: DateComponents(timeZone: TIME_ZONE_LA, year: year, month: month, day: day)) + guard let date = Calendar.current.date(from: DateComponents(timeZone: .losAngeles, year: year, month: month, day: day)) else { return nil } return date } - var isCurrent: Bool { - guard let date = asDate() else { - return false - } - var calendar = Calendar.current - calendar.locale = Locale(identifier: "en_US") - calendar.timeZone = TIME_ZONE_LA - return calendar.isDateInToday(date) || calendar.isDateInTomorrow(date) + public func nextDate(in calendar: Calendar) -> Date? { + guard + let thisDate = calendar.date(from: DateComponents(year: year, month: month, day: day)), + let nextDate = calendar.date(byAdding: .day, value: 1, to: thisDate) + else { return nil } + + return calendar.startOfDay(for: nextDate) } }