Prevented a couple potential crashes

Not force unwrapping things is good!
This commit is contained in:
2026-03-16 12:47:12 -04:00
parent 420f49cafc
commit a88521c35f
2 changed files with 39 additions and 19 deletions

View File

@@ -292,7 +292,7 @@
CODE_SIGN_ENTITLEMENTS = TigerDineWidgets/TigerDineWidgets.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 34;
CURRENT_PROJECT_VERSION = 35;
DEVELOPMENT_TEAM = 5GF7GKNTK4;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = TigerDineWidgets/Info.plist;
@@ -304,7 +304,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = "dev.ninjacheetah.RIT-Dining.Widgets";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -325,7 +325,7 @@
CODE_SIGN_ENTITLEMENTS = TigerDineWidgets/TigerDineWidgets.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 34;
CURRENT_PROJECT_VERSION = 35;
DEVELOPMENT_TEAM = 5GF7GKNTK4;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = TigerDineWidgets/Info.plist;
@@ -337,7 +337,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = "dev.ninjacheetah.RIT-Dining.Widgets";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -481,7 +481,7 @@
CODE_SIGN_ENTITLEMENTS = TigerDine/TigerDine.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 34;
CURRENT_PROJECT_VERSION = 35;
DEVELOPMENT_TEAM = 5GF7GKNTK4;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -500,7 +500,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = "dev.ninjacheetah.RIT-Dining";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -518,7 +518,7 @@
CODE_SIGN_ENTITLEMENTS = TigerDine/TigerDine.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 34;
CURRENT_PROJECT_VERSION = 35;
DEVELOPMENT_TEAM = 5GF7GKNTK4;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -537,7 +537,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = "dev.ninjacheetah.RIT-Dining";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -94,20 +94,33 @@ class DiningModel {
// If we can't access the lastRefreshed key, then there is likely no cache.
if let lastRefreshed = lastRefreshed {
if Calendar.current.startOfDay(for: now) == Calendar.current.startOfDay(for: lastRefreshed) {
print("cache hit, loading from cache")
// Last refresh happened today, so the cache is fresh and we should load that.
print("cache hit, trying load from cache")
await getDaysRepresented()
let decoder = JSONDecoder()
let cachedLocationsByDay = try decoder.decode([[DiningLocation]].self, from: (UserDefaults(suiteName: "group.dev.ninjacheetah.RIT-Dining")!.data(forKey: "cachedLocationsByDay")!))
// Load cache, update open status, do a notification cleanup, and return. We only need to clean up because loading
// cache means that there can't be any new notifications to schedule since the last real data refresh.
locationsByDay = cachedLocationsByDay
updateOpenStatuses()
await cleanupPushes()
// These checks ensure that the key can actually be loaded from UserDefaults and that the cached JSON data can
// actually be loaded from the cache before trying to use it, to prevent potential crashes from force unwrapping
// it. Currently unclear on what could make these fail if the lastRefreshed date loaded as today, but this should
// mitigate it by falling back on a network load if they do.
if let cacheUserDefaults = UserDefaults(suiteName: "group.dev.ninjacheetah.RIT-Dining") {
if let cacheData = cacheUserDefaults.data(forKey: "cachedLocationsByDay") {
let cachedLocationsByDay = try decoder.decode([[DiningLocation]].self, from: cacheData)
isLoaded = true
return
// Load cache, update open status, do a notification cleanup, and return. We only need to clean up because
// loading cache means that there can't be any new notifications to schedule since the last real data refresh.
locationsByDay = cachedLocationsByDay
updateOpenStatuses()
await cleanupPushes()
isLoaded = true
return
} else {
print("cache exists, but failed to load JSON data")
}
} else {
print("cache appears to exist, but failed to load from UserDefaults")
}
}
print("cache miss")
// Otherwise, the cache is stale and we can fall out to the call to update it.
@@ -153,7 +166,14 @@ class DiningModel {
let now = Date()
for push in visitingChefPushes.pushes {
if now > push.endTime {
visitingChefPushes.pushes.remove(at: visitingChefPushes.pushes.firstIndex(of: push)!)
// Guard this with an if let to avoid force unwrapping the index. That's something that theoretically
// should always be safe given that this is iterating over elements so obviously that element should exist,
// however there was an issue where this would sometimes unwrap a nil. My theory is that there was a small
// chance of this task getting run twice concurrently under certain conditions, and so one would remove the
// notification right before the other tried, and then it would be gone and the index would be nil.
if let pushIndex = visitingChefPushes.pushes.firstIndex(of: push) {
visitingChefPushes.pushes.remove(at: pushIndex)
}
}
}
}