Today, I released an update to GPSTrack, my privacy-focused GPS tracking app. It is available on the App Store.
- Improvements
- Minor UI improvements.
- Bug Fixes
- Fix iOS 26 animation issues.
- Fix detection of changes to map region on iOS 26.
Today, I released an update to GPSTrack, my privacy-focused GPS tracking app. It is available on the App Store.
Today, I released an update to GPSTrack, my privacy-focused GPS tracking app. It is available on the App Store.
Today, I released an update to GPSTrack, my privacy-focused GPS tracking app. It is available on the App Store.
Today, I released GPSTrack 3.0, my privacy-focused GPS tracking app for iOS. It is available on the App Store. This is the first public release since 2014.
GPSTrack 3 is a complete rewrite from the last released version, now in Swift and (mostly) SwiftUI. The changelog is so long it’s almost better to think of this as a completely new app.
This version switches from being paid-up-front to a subscription model. While paid-up-front worked in the early era of the app store, it’s no longer a sustainable approach for most developers, and as much as I dislike subscriptions myself, switching to subscriptions is the only way I can justify further development.
Users who purchased GPSTrack while it was previously on sale have full access to all functionality that was available in the last public release (2.2.1). Some (but not all) new features require a subscription. If you previously purchased GPSTrack and are not sure if the app is recognizing your prior purchase, please feel free to reach out.
The new Observation framework in iOS 17 (and aligned macOS, tvOS, and watchOS releases), with its @Observable macro, provides a fantastic and higher-performance way for SwiftUI applications to trigger view updates, compared to the older ObservableObject approach.
To do this, the framework retains strong references to reference objects that are being tracked. However, this combines with a bug in SwiftUI that causes objects in views to not always be released when the view is dismissed.
[Update: The bug in SwiftUI was fixed sometime after iOS 17.1, so this tool is no longer needed. It is available for historical insight.]
This manifests when a SwiftUI view presents a sheet or fullScreenCover and the view captures an object. The object is retained by the system, but is not released when the presented view goes away.
For small objects, this may not be a problem, unless they’re also tracking notifications or other external events.
Larger objects are the main problem; I discovered this issue when I noticed one of my apps using several gigabytes of memory when it had no open windows.
This bug is present in at least iOS 17.0 .. 17.1b3.
To solve this problem, I created the SwiftUIMemoryLeakWorkaround package. This package provides a way to resolve the problem in a way that should be relatively backwards-compatible when the OS bug is eventually fixed.
The solution is to instead have a UIViewController handle the presentation of sheets. This is accomplished by injecting a coordinator object into the SwiftUI environment that has a UIViewController that is the parent of the SwiftUI view. Then, the SwiftUI view is modified to call the coordinator, rather than sheet or fullScreenCover directly. The coordinator uses its stored view controller to present a new view, and creates a new coordinator to inject into the sheet view with the child view controller.
An extension on View provides accessors (leak_workaround_sheet and leak_workaround_fullScreenCover) that create a view modifier that uses the coordinator to trigger presentation. In the event the coordinator is not set or is set to nil, it falls back to the system behavior.
The included Example.xcodeproj demonstrates both the problem and the solution.
This is not a perfect solution. Sometimes, the coordinator itself is leaked. The coordinator object contains two weak references and an optional UUID, and so is relatively tiny compared to the view models that would likely be leaked instead.