From 89c67ae807fc5dbe6a89cbd86fe0ec2832291d6f Mon Sep 17 00:00:00 2001 From: akoi Date: Sun, 25 Oct 2026 09:06:05 +0100 Subject: [PATCH 1/6] fix: generate universal iOS app icons --- .../Resources/Assets.xcassets/AppIcon.appiconset/Contents.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/LoopFollow/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/LoopFollow/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index c928251a4..dc4a1901f 100644 --- a/LoopFollow/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/LoopFollow/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -4,7 +4,6 @@ "filename" : "1024.png", "idiom" : "universal", "platform" : "ios", - "scale" : "1x", "size" : "1024x1024" }, { @@ -17,7 +16,6 @@ "filename" : "1024-dark.png", "idiom" : "universal", "platform" : "ios", - "scale" : "1x", "size" : "1024x1024" }, { @@ -30,7 +28,6 @@ "filename" : "1024-tinted.png", "idiom" : "universal", "platform" : "ios", - "scale" : "1x", "size" : "1024x1024" } ], From df2b19a83136c99830c811baa2247bc712e2f9c8 Mon Sep 17 00:00:00 2001 From: akoi Date: Sun, 25 Oct 2026 11:14:36 +0100 Subject: [PATCH 2/6] fix: align app and extension build numbers --- LoopFollow/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopFollow/Info.plist b/LoopFollow/Info.plist index 898d09616..92f5cf212 100644 --- a/LoopFollow/Info.plist +++ b/LoopFollow/Info.plist @@ -39,7 +39,7 @@ CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) ITSAppUsesNonExemptEncryption LSApplicationCategoryType From 9e340fa0bbf7375a5ea49fd405c6443578450c77 Mon Sep 17 00:00:00 2001 From: akoi Date: Sun, 25 Oct 2026 11:55:14 +0100 Subject: [PATCH 3/6] fix: retain default app build number --- LoopFollow.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LoopFollow.xcodeproj/project.pbxproj b/LoopFollow.xcodeproj/project.pbxproj index 560cd2f8e..a68a86d50 100644 --- a/LoopFollow.xcodeproj/project.pbxproj +++ b/LoopFollow.xcodeproj/project.pbxproj @@ -771,6 +771,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = "LoopFollow/Loop Follow.entitlements"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = "$(LF_DEVELOPMENT_TEAM)"; INFOPLIST_FILE = LoopFollow/Info.plist; @@ -796,6 +797,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = "LoopFollow/Loop Follow.entitlements"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = "$(LF_DEVELOPMENT_TEAM)"; INFOPLIST_FILE = LoopFollow/Info.plist; From 2a8badac9fb0a6e5fe3e7acfdd8fbe361597e07e Mon Sep 17 00:00:00 2001 From: akoi Date: Sun, 25 Oct 2026 12:10:49 +0100 Subject: [PATCH 4/6] revert: retain upstream build-number settings Prepare local upload versions with upstream's agvtool step instead of changing application version settings. Keep only the icon compatibility fix in the final diff. --- LoopFollow.xcodeproj/project.pbxproj | 2 -- LoopFollow/Info.plist | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/LoopFollow.xcodeproj/project.pbxproj b/LoopFollow.xcodeproj/project.pbxproj index a68a86d50..560cd2f8e 100644 --- a/LoopFollow.xcodeproj/project.pbxproj +++ b/LoopFollow.xcodeproj/project.pbxproj @@ -771,7 +771,6 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = "LoopFollow/Loop Follow.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = "$(LF_DEVELOPMENT_TEAM)"; INFOPLIST_FILE = LoopFollow/Info.plist; @@ -797,7 +796,6 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = "LoopFollow/Loop Follow.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = "$(LF_DEVELOPMENT_TEAM)"; INFOPLIST_FILE = LoopFollow/Info.plist; diff --git a/LoopFollow/Info.plist b/LoopFollow/Info.plist index 92f5cf212..898d09616 100644 --- a/LoopFollow/Info.plist +++ b/LoopFollow/Info.plist @@ -39,7 +39,7 @@ CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 1 ITSAppUsesNonExemptEncryption LSApplicationCategoryType From a4b66389c2403a3412095e2ba35e27b3b790799e Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 30 Oct 2026 14:31:53 +0100 Subject: [PATCH 5/6] fix: backport DST-safe Statistics day stepping with regression tests Adapt upstream LoopFollow PR #795 to a shared calendar-day helper. Preserves local Xcode 27.1 app icon fix and adds Europe/Stockholm DST tests. --- LoopFollow/Stats/SimpleStatsViewModel.swift | 6 +-- LoopFollow/Stats/StatsDataFetcher.swift | 6 +-- LoopFollow/Stats/StatsDateRange.swift | 13 +++++ Tests/StatsCalendarDayTests.swift | 59 +++++++++++++++++++++ 4 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 Tests/StatsCalendarDayTests.swift diff --git a/LoopFollow/Stats/SimpleStatsViewModel.swift b/LoopFollow/Stats/SimpleStatsViewModel.swift index 841f4ca2a..8b027b4f4 100644 --- a/LoopFollow/Stats/SimpleStatsViewModel.swift +++ b/LoopFollow/Stats/SimpleStatsViewModel.swift @@ -226,9 +226,9 @@ class SimpleStatsViewModel: ObservableObject { var currentTime = startTime while currentTime < endTime { - let currentDate = Date(timeIntervalSince1970: currentTime) - let dayStart = calendar.startOfDay(for: currentDate).timeIntervalSince1970 - let nextDayStart = dayStart + 24 * 60 * 60 + guard let day = StatsCalendarDay.bounds(containing: currentTime, calendar: calendar) else { break } + let dayStart = day.start + let nextDayStart = day.next // Process each profile segment for this day for i in 0 ..< sortedProfile.count { diff --git a/LoopFollow/Stats/StatsDataFetcher.swift b/LoopFollow/Stats/StatsDataFetcher.swift index 18353dce0..a40dbcc32 100644 --- a/LoopFollow/Stats/StatsDataFetcher.swift +++ b/LoopFollow/Stats/StatsDataFetcher.swift @@ -539,9 +539,9 @@ class StatsDataFetcher { var currentTime = startTime while currentTime < endTime { - let currentDate = Date(timeIntervalSince1970: currentTime) - let dayStart = calendar.startOfDay(for: currentDate).timeIntervalSince1970 - let nextDayStart = dayStart + 24 * 60 * 60 + guard let day = StatsCalendarDay.bounds(containing: currentTime, calendar: calendar) else { break } + let dayStart = day.start + let nextDayStart = day.next for i in 0 ..< sortedProfile.count { let segmentRate = sortedProfile[i].value diff --git a/LoopFollow/Stats/StatsDateRange.swift b/LoopFollow/Stats/StatsDateRange.swift index e2e00458b..4f0fd5845 100644 --- a/LoopFollow/Stats/StatsDateRange.swift +++ b/LoopFollow/Stats/StatsDateRange.swift @@ -15,3 +15,16 @@ enum StatsDateRange { return (start: calendar.startOfDay(for: start), end: end) } } + + +/// Calendar-day progression for historical stats; a local day can be 23 or 25 hours. +/// Shared by basal gap reconstruction and temp-basal adjustment calculations. +enum StatsCalendarDay { + static func bounds(containing timestamp: TimeInterval, calendar: Calendar) -> (start: TimeInterval, next: TimeInterval)? { + let dayStart = calendar.startOfDay(for: Date(timeIntervalSince1970: timestamp)) + guard let nextDayStart = calendar.date(byAdding: .day, value: 1, to: dayStart) else { + return nil + } + return (start: dayStart.timeIntervalSince1970, next: nextDayStart.timeIntervalSince1970) + } +} diff --git a/Tests/StatsCalendarDayTests.swift b/Tests/StatsCalendarDayTests.swift new file mode 100644 index 000000000..de5f94825 --- /dev/null +++ b/Tests/StatsCalendarDayTests.swift @@ -0,0 +1,59 @@ +// LoopFollow +// StatsCalendarDayTests.swift + +import Foundation +@testable import LoopFollow +import Testing + +struct StatsCalendarDayTests { + private func stockholmCalendar() throws -> Calendar { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = try #require(TimeZone(identifier: "Europe/Stockholm")) + return calendar + } + + @Test("Autumn clock change has a 25-hour day and always advances") + func autumnClockChange() throws { + let calendar = try stockholmCalendar() + let midnight = try #require(calendar.date(from: DateComponents(year: 2026, month: 10, day: 25))) + let day = try #require(StatsCalendarDay.bounds(containing: midnight.timeIntervalSince1970, calendar: calendar)) + #expect(day.next - day.start == 25 * 60 * 60) + + // During the repeated hour, adding 24h to midnight would remain on 25 Oct. + let finalHour = day.next - 30 * 60 + let nearEnd = try #require(StatsCalendarDay.bounds(containing: finalHour, calendar: calendar)) + #expect(nearEnd.start == day.start) + #expect(nearEnd.next > finalHour) + + let next = try #require(StatsCalendarDay.bounds(containing: nearEnd.next, calendar: calendar)) + #expect(next.start == day.next) + #expect(next.next > next.start) + } + + @Test("Spring clock change has a 23-hour day") + func springClockChange() throws { + let calendar = try stockholmCalendar() + let midnight = try #require(calendar.date(from: DateComponents(year: 2026, month: 3, day: 29))) + let day = try #require(StatsCalendarDay.bounds(containing: midnight.timeIntervalSince1970, calendar: calendar)) + #expect(day.next - day.start == 23 * 60 * 60) + } + + @Test("Seven statistics calendar days progress across the autumn transition") + func sevenDayHistory() throws { + let calendar = try stockholmCalendar() + let start = try #require(calendar.date(from: DateComponents(year: 2026, month: 10, day: 22))) + var cursor = start.timeIntervalSince1970 + var seenDays = Set() + + for _ in 0 ..< 7 { + let day = try #require(StatsCalendarDay.bounds(containing: cursor, calendar: calendar)) + #expect(day.start == cursor) + #expect(day.next > cursor) + #expect(seenDays.insert(day.start).inserted) + cursor = day.next + } + #expect(seenDays.count == 7) + let expected = try #require(calendar.date(from: DateComponents(year: 2026, month: 10, day: 29))) + #expect(cursor == expected.timeIntervalSince1970) + } +} From 22040408ea85d67a2a2d8dff5b6f12e9f29a32c8 Mon Sep 17 00:00:00 2001 From: Anton Date: Fri, 30 Oct 2026 14:32:44 +0100 Subject: [PATCH 6/6] style: keep calendar-day helper spacing consistent --- LoopFollow/Stats/StatsDateRange.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/LoopFollow/Stats/StatsDateRange.swift b/LoopFollow/Stats/StatsDateRange.swift index 4f0fd5845..0cbac59f2 100644 --- a/LoopFollow/Stats/StatsDateRange.swift +++ b/LoopFollow/Stats/StatsDateRange.swift @@ -16,7 +16,6 @@ enum StatsDateRange { } } - /// Calendar-day progression for historical stats; a local day can be 23 or 25 hours. /// Shared by basal gap reconstruction and temp-basal adjustment calculations. enum StatsCalendarDay {