├── .gitignore
├── Changelog.md
├── Images
└── Screenshots
│ ├── iPhone 11 Pro Max - Groups.jpeg
│ ├── iPhone 11 Pro Max - Tickmate.jpeg
│ ├── iPhone 11 Pro Max - Track.jpeg
│ ├── iPhone 11 Pro Max - Tracks.jpeg
│ ├── iPhone 11 Pro Max - Widget.jpg
│ └── iPhone 11 Pro Max - iCloud.jpeg
├── LICENSE
├── Privacy Policy.txt
├── README.md
└── Tickmate
├── Tickmate.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
└── xcshareddata
│ └── xcschemes
│ ├── Tickmate.xcscheme
│ ├── TicksWidgetExtension.xcscheme
│ └── TicksWidgetIntentsExtension.xcscheme
├── Tickmate
├── Controllers
│ ├── GroupController.swift
│ ├── Persistence.swift
│ ├── StoreController.swift
│ ├── TickController.swift
│ ├── TrackController.swift
│ └── ViewControllerContainer.swift
├── Info.plist
├── Model
│ ├── Binding+onChange.swift
│ ├── Defaults.swift
│ ├── Tickmate+Convenience.swift
│ ├── Tickmate+Wrapping.swift
│ ├── Tickmate.xcdatamodeld
│ │ ├── .xccurrentversion
│ │ └── Tickmate.xcdatamodel
│ │ │ └── contents
│ ├── TrackGroups.swift
│ └── TrackRepresentation.swift
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── Resources
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── icon-40.png
│ │ │ ├── icon-40@2x.png
│ │ │ ├── icon-40@3x.png
│ │ │ ├── icon-60@2x.png
│ │ │ ├── icon-60@3x.png
│ │ │ ├── icon-72.png
│ │ │ ├── icon-72@2x.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-76@2x.png
│ │ │ ├── icon-83.5@2x.png
│ │ │ ├── icon-small-50.png
│ │ │ ├── icon-small-50@2x.png
│ │ │ ├── icon-small.png
│ │ │ ├── icon-small@2x.png
│ │ │ ├── icon-small@3x.png
│ │ │ ├── icon.png
│ │ │ ├── icon@2x.png
│ │ │ ├── ios-marketing.png
│ │ │ ├── notification-icon@2x.png
│ │ │ ├── notification-icon@3x.png
│ │ │ ├── notification-icon~ipad.png
│ │ │ └── notification-icon~ipad@2x.png
│ │ ├── AppIcon.solidimagestack
│ │ │ ├── Back.solidimagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Background.png
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Front.solidimagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Ticks.png
│ │ │ │ └── Contents.json
│ │ │ └── Middle.solidimagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── Dividers.png
│ │ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Configuration.storekit
│ ├── Launch Screen.storyboard
│ ├── PresetTracks.swift
│ └── SymbolsList.swift
├── Supplementary Views
│ ├── AcknowledgementLink.swift
│ ├── AlertItem.swift
│ ├── Animation+Push.swift
│ ├── Color+RGB.swift
│ ├── PageView.swift
│ ├── StateEditButton.swift
│ ├── TextWithCaption.swift
│ ├── View+Centered.swift
│ ├── View+Conditional.swift
│ └── View+DismissKeyboard.swift
├── Tickmate.entitlements
├── TickmateApp.swift
└── Views
│ ├── AcknowledgementsView.swift
│ ├── ArchivedTracksView.swift
│ ├── ContentView.swift
│ ├── ExportTracksSelectionView.swift
│ ├── GroupView.swift
│ ├── GroupsView.swift
│ ├── OnboardingView.swift
│ ├── PresetTracksView.swift
│ ├── SettingsView.swift
│ ├── ShareSheet.swift
│ ├── SymbolPicker.swift
│ ├── TickView.swift
│ ├── TicksView.swift
│ ├── TrackView.swift
│ └── TracksView.swift
├── TicksWidget
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ └── WidgetBackground.colorset
│ │ └── Contents.json
├── Info.plist
├── TicksWidget.intentdefinition
└── TicksWidget.swift
├── TicksWidgetExtension.entitlements
└── TicksWidgetIntentsExtension
├── Info.plist
├── IntentHandler.swift
└── TicksWidgetIntentsExtension.entitlements
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
92 | # macOS
93 | .DS_Store
94 |
--------------------------------------------------------------------------------
/Changelog.md:
--------------------------------------------------------------------------------
1 | # Tickmate Changelog
2 |
3 | ## v1.4
4 |
5 | Released on the App Store 2024/03/17
6 |
7 | + Tracks can now be archived
8 | ([#93](https://github.com/skjiisa/Tickmate-iOS/pull/93))
9 |
10 | ## v1.3 and v1.3.1
11 |
12 | v1.3 released on the iOS App Store 2024/01/29
13 | v1.3.1 released on the visionOS App Store 2024/02/02
14 |
15 | ### Major changes
16 |
17 | + Native Apple Vision Pro support
18 |
19 | ### Minor improvements
20 |
21 | + Improve future-proofing by only showing known in-app purchases
22 | ([#88](https://github.com/skjiisa/Tickmate-iOS/pull/88))
23 | + Fix iOS 16 and 17 issues related to Introspect for SwiftUI
24 | ([#89](https://github.com/skjiisa/Tickmate-iOS/pull/89))
25 | + Fix tracks rearranging themselves on new installs
26 | ([#91](https://github.com/skjiisa/Tickmate-iOS/pull/91))
27 | + Make swiping between pages less janky (again)
28 | ([a40b9a9](https://github.com/skjiisa/Tickmate-iOS/commit/a40b9a9dc6aab239704391e5666402d8f7735a95))
29 |
30 | ## v1.2.3
31 |
32 | Released on the App Store 2021/11/04
33 |
34 | + Fix laggy swiping between track groups
35 | ([f7711aa](https://github.com/Isvvc/Tickmate-iOS/commit/f7711aa2aa063f74c441e3e0f0b2abf5dc9fed00))
36 |
37 | ## v1.2.2
38 |
39 | Released on the App Store 2021/10/04
40 |
41 | ### Minor improvements
42 |
43 | + Sort tracks in the widget by the in-app sort order as opposed to the order they are selected in
44 | ([3fda8c7](https://github.com/Isvvc/Tickmate-iOS/pull/74/commits/3fda8c79826095acf8986522dc9612e6eb054362))
45 |
46 | #### iOS 15 fixes
47 |
48 | + Fix not being able to open sheets
49 | ([319eb0d](https://github.com/Isvvc/Tickmate-iOS/pull/74/commits/319eb0d880b28e81bba60f5c00ea3ac090ea637a))
50 | + Fix text fields being empty
51 | ([9bac4d9](https://github.com/Isvvc/Tickmate-iOS/pull/74/commits/9bac4d9c6bf52af7bcdbca208722c0c37ee1ffdc))
52 |
53 | ## v1.2.1
54 |
55 | Released on the App Store 2021/07/17
56 |
57 | + Fix issue with new tracks not showing in the tracks list
58 | ([e381324](https://github.com/Isvvc/Tickmate-iOS/commit/e3813249cd457132fe258c3df918759bcb10bae0))
59 |
60 | ## v1.2
61 |
62 | ### Major features
63 |
64 | + Widgets!
65 | ([#51](https://github.com/Isvvc/Tickmate-iOS/pull/51))
66 | + There are now 3 sizes of customizable widgets for your Home Screen or Today View.
67 | + Fix laggy ticking
68 | ([#50](https://github.com/Isvvc/Tickmate-iOS/issues/50))
69 | + Ticking days should now happen immediately when tapped
70 |
71 | ### Minor improvements
72 |
73 | + Fix text wrap issue on "Yesterday" text on larger Dynamic Type sizes
74 | ([#48](https://github.com/Isvvc/Tickmate-iOS/issues/48))
75 | + Add a basic launch screen
76 | ([#49](https://github.com/Isvvc/Tickmate-iOS/issues/49))
77 | + Add version and build numbers in settings
78 | ([#52](https://github.com/Isvvc/Tickmate-iOS/issues/52))
79 | + Fix layout of tracks row when large number of tracks exist
80 | ([#54](https://github.com/Isvvc/Tickmate-iOS/issues/54))
81 |
82 | ## v1.1
83 |
84 | Released
85 |
86 | + 2021/06/23 on GitHub
87 | + 2021/07/09 on the App Store
88 |
89 | ### Major features
90 |
91 | + Track groups
92 | ([#29](https://github.com/Isvvc/Tickmate-iOS/pull/29))
93 | + In-app purchase that allows you to group tracks together and swipe between groups on the main screen
94 |
95 | ### Minor improvements
96 |
97 | + Fix lag when editing Tracks
98 | ([949a361](https://github.com/Isvvc/Tickmate-iOS/commit/949a3619418b0896a61bbb3dc54569f3b834e41d))
99 | + Fix issue where date would not update when opening the app on a new day
100 | ([934c875](https://github.com/Isvvc/Tickmate-iOS/commit/934c8755327b20a1636dac4a28b3c0af1dd3eb58))
101 | + Add new section of preset tracks
102 | ([df74640](https://github.com/Isvvc/Tickmate-iOS/commit/df74640dff3619046ddd1bafa685cd4b597b7b4d), [#46](https://github.com/Isvvc/Tickmate-iOS/pull/46))
103 | + Fix week separators sometimes not showing correctly or at all
104 | ([#47](https://github.com/Isvvc/Tickmate-iOS/pull/47))
105 | + Minor back-end bug fixes
106 | ([15bcc73](https://github.com/Isvvc/Tickmate-iOS/commit/15bcc734103871d9e455a107b4edb0592cc9f99e))
107 |
--------------------------------------------------------------------------------
/Images/Screenshots/iPhone 11 Pro Max - Groups.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skjiisa/Tickmate-iOS/4ba89869b55205e4f51fa032610793a5c7d5c206/Images/Screenshots/iPhone 11 Pro Max - Groups.jpeg
--------------------------------------------------------------------------------
/Images/Screenshots/iPhone 11 Pro Max - Tickmate.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skjiisa/Tickmate-iOS/4ba89869b55205e4f51fa032610793a5c7d5c206/Images/Screenshots/iPhone 11 Pro Max - Tickmate.jpeg
--------------------------------------------------------------------------------
/Images/Screenshots/iPhone 11 Pro Max - Track.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skjiisa/Tickmate-iOS/4ba89869b55205e4f51fa032610793a5c7d5c206/Images/Screenshots/iPhone 11 Pro Max - Track.jpeg
--------------------------------------------------------------------------------
/Images/Screenshots/iPhone 11 Pro Max - Tracks.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skjiisa/Tickmate-iOS/4ba89869b55205e4f51fa032610793a5c7d5c206/Images/Screenshots/iPhone 11 Pro Max - Tracks.jpeg
--------------------------------------------------------------------------------
/Images/Screenshots/iPhone 11 Pro Max - Widget.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skjiisa/Tickmate-iOS/4ba89869b55205e4f51fa032610793a5c7d5c206/Images/Screenshots/iPhone 11 Pro Max - Widget.jpg
--------------------------------------------------------------------------------
/Images/Screenshots/iPhone 11 Pro Max - iCloud.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skjiisa/Tickmate-iOS/4ba89869b55205e4f51fa032610793a5c7d5c206/Images/Screenshots/iPhone 11 Pro Max - iCloud.jpeg
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2025, Elaine Lyons
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/Privacy Policy.txt:
--------------------------------------------------------------------------------
1 | This app does not collect any personal information.
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tickmate
2 |
3 | 1-bit Journal
4 |
5 |
6 |
7 | ### Screenshots
8 |
9 |
10 |
11 | ## Description
12 |
13 | Tickmate allows you to track any daily occurrences.
14 | It is a is a 1-bit journal, meaning each day is either ticked or not.
15 | You can track anything from habits you wish to build or break to how often you wash your hair.
16 |
17 | + Use a "reversed" track for occurrences you don't want to happen, so they're automatically ticked unless you untick them.
18 | + You can use the "allow multiple" feature to be able to tick a single day multiple times.
19 | + With iCloud sync, all your data will be securely synced across all devices.
20 | + Add customizable widgets for your Home Screen or Today View.
21 |
22 | [Changelog](Changelog.md)
23 |
24 | ### Inspiration
25 |
26 | Tickmate for iOS was designed after, but not affiliated with, [lordi/tickmate](https://github.com/lordi/tickmate) for Android.
27 |
28 | ## Build
29 |
30 | Just set your developer team in Xcode and build!
31 |
32 | Dependencies:
33 |
34 | * [malcommac/SwiftDate](https://github.com/malcommac/SwiftDate)
35 | * [siteline/SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect)
36 |
37 | These dependencies will be automatically fetched by Swift Package Manager in Xcode.
38 |
39 | ## License
40 |
41 | This project is open-source and licensed under the [The 2-Clause BSD License](LICENSE).
42 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "3879669beac337797e14d697a5b49a02d41791009205fbb4a311d44492aad7b3",
3 | "pins" : [
4 | {
5 | "identity" : "swiftdate",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/malcommac/SwiftDate.git",
8 | "state" : {
9 | "revision" : "6190d0cefff3013e77ed567e6b074f324e5c5bf5",
10 | "version" : "6.3.1"
11 | }
12 | },
13 | {
14 | "identity" : "swiftui-introspect",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/siteline/SwiftUI-Introspect.git",
17 | "state" : {
18 | "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336",
19 | "version" : "1.3.0"
20 | }
21 | }
22 | ],
23 | "version" : 3
24 | }
25 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate.xcodeproj/xcshareddata/xcschemes/Tickmate.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
54 |
55 |
56 |
62 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate.xcodeproj/xcshareddata/xcschemes/TicksWidgetExtension.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
16 |
22 |
23 |
24 |
30 |
36 |
37 |
38 |
39 |
40 |
46 |
47 |
59 |
61 |
67 |
68 |
69 |
70 |
74 |
75 |
79 |
80 |
84 |
85 |
86 |
87 |
95 |
97 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate.xcodeproj/xcshareddata/xcschemes/TicksWidgetIntentsExtension.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
16 |
22 |
23 |
24 |
30 |
36 |
37 |
38 |
39 |
40 |
46 |
47 |
59 |
61 |
67 |
68 |
69 |
70 |
78 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate/Controllers/GroupController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GroupController.swift
3 | // Tickmate
4 | //
5 | // Created by Elaine Lyons on 5/26/21.
6 | //
7 |
8 | import CoreData
9 | import SwiftUI
10 |
11 | class GroupController: NSObject, ObservableObject {
12 |
13 | static let sortDescriptors = [
14 | NSSortDescriptor(keyPath: \TrackGroup.index, ascending: true),
15 | // For consistency when there are index collisions with iCloud sync
16 | NSSortDescriptor(keyPath: \TrackGroup.name, ascending: true),
17 | ]
18 |
19 | var fetchedResultsController: NSFetchedResultsController
20 | var trackController: TrackController?
21 | var animateNextChange = false
22 |
23 | init(preview: Bool = false) {
24 | let context = (preview ? PersistenceController.preview : PersistenceController.shared).container.viewContext
25 |
26 | let fetchRequest: NSFetchRequest = TrackGroup.fetchRequest()
27 | fetchRequest.sortDescriptors = Self.sortDescriptors
28 |
29 | fetchedResultsController = NSFetchedResultsController(
30 | fetchRequest: fetchRequest,
31 | managedObjectContext: context,
32 | sectionNameKeyPath: nil,
33 | cacheName: nil)
34 |
35 | super.init()
36 |
37 | fetchedResultsController.delegate = self
38 |
39 | do {
40 | try fetchedResultsController.performFetch()
41 | } catch {
42 | NSLog("Error performing TrackGroups fetch: \(error)")
43 | }
44 | }
45 | }
46 |
47 | extension GroupController: NSFetchedResultsControllerDelegate {
48 | func controllerWillChangeContent(_ controller: NSFetchedResultsController) {
49 | // TODO: This is weird
50 | // I feel like I remember having some bugginess when having this in a
51 | // withAnimation all the time, so I did this to be safe. To be fair,
52 | // that would've also been when I was using the awful index collision
53 | // resolution below, so those could've been related.
54 | if animateNextChange {
55 | withAnimation {
56 | objectWillChange.send()
57 | }
58 | animateNextChange = false
59 | } else {
60 | objectWillChange.send()
61 | }
62 | }
63 | /*
64 | func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
65 | guard [.delete, .insert].contains(type) else { return }
66 |
67 | var changed = false
68 |
69 | // Update indices
70 | fetchedResultsController.fetchedObjects?.enumerated()
71 | .map({ (Int16($0), $1) })
72 | .filter({ $0 != $1.index })
73 | .forEach { index, item in
74 | item.index = index
75 | changed = true
76 | }
77 |
78 | if changed {
79 | trackController?.scheduleSave()
80 | }
81 | trackController?.saveIfScheduled()
82 | }
83 | */
84 | }
85 |
--------------------------------------------------------------------------------
/Tickmate/Tickmate/Controllers/Persistence.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Persistence.swift
3 | // Tickmate
4 | //
5 | // Created by Elaine Lyons on 2/19/21.
6 | //
7 |
8 | import SwiftUI
9 | import CoreData
10 | import SwiftDate
11 |
12 | class PersistenceController {
13 |
14 | //MARK: Static instances
15 |
16 | static let shared = PersistenceController()
17 |
18 | static var preview: PersistenceController = {
19 | let result = PersistenceController(inMemory: true)
20 | let viewContext = result.container.viewContext
21 |
22 | let group = TrackGroup(name: "Test group", index: 0, context: viewContext)
23 | result.previewGroup = group
24 |
25 | let dateString = TrackController.iso8601.string(from: Date() - 5.days)
26 | for i: Int16 in 0..<6 {
27 | let track = Track(
28 | name: String(UUID().uuidString.dropLast(28)),
29 | multiple: 1...2 ~= i,
30 | reversed: i == 2,
31 | startDate: dateString,
32 | index: i,
33 | isArchived: i == 5,
34 | context: viewContext
35 | )
36 |
37 | if i == 1 {
38 | for day in 0..<5 {
39 | let count = Int16.random(in: 0..<4)
40 | if count > 0 {
41 | let tick = Tick(track: track, dayOffset: Int16(day), context: viewContext)
42 | tick.count = count
43 | }
44 | }
45 | } else {
46 | for day in 0..<5 where Bool.random() {
47 | Tick(track: track, dayOffset: Int16(day))
48 | }
49 | track.groups = [group]
50 | }
51 | }
52 | result.save()
53 | return result
54 | }()
55 |
56 | //MARK: Demo
57 |
58 | #if DEBUG
59 | func loadDemo() -> PersistenceController {
60 | let viewContext = container.viewContext
61 |
62 | // Only create demo data if there is not already data
63 | let fetchRequest: NSFetchRequest