-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix version 3.0 beta build #691
Conversation
@gavirawson-apple This is ready for review, let me know if you want me to make any adjustments. |
@gavirawson-apple It looks like there are a couple files missing that SPM can see, but prevents testing. These files are currently not part of the Xcode build which is why it succeeds:
|
I've also tested these changes locally in Xcode 14.3 and fix all errors/warnings. |
CareKit/CareKit/Shared/Synchronization/CareStoreFetchRequestController.swift
Show resolved
Hide resolved
I think the CI for this PR needs approval to run. You can track the working build in my local PR: cbaker6#8 |
.github/workflows/swift.yml
Outdated
exclude: | ||
- destination: 'platform=watchOS\ Simulator,OS=9.1,name=Apple\ Watch\ Series\ 5\ \(40mm\)' | ||
scheme: 'CareKitUI' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add this back
platform: ios | ||
- platform: ios | ||
scheme: "CareKit" | ||
- platform: watchos | ||
scheme: "CareKit" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add macOS
@@ -88,7 +88,7 @@ public struct OCKScheduleElement: Codable, Equatable { | |||
} | |||
} | |||
|
|||
private let calendar: Calendar | |||
private static var calendar = Calendar.current |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to static computed property
@@ -0,0 +1,4 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove file
@@ -57,8 +56,7 @@ class TestColorExtension: XCTestCase { | |||
|
|||
func testClamping() { | |||
let start = UIColor(red: 0, green: 0, blue: 0, alpha: 1) | |||
let lightened = start.lightened(10) | |||
let lightened = start.lightened(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has a pre-condition where the argument passed to lightened
has to be <=1
. For some reason it hasn't been failing on the iOS tests, but the watchOS tests caught the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found the issue. The reason was TestColorExtension
wasn't included in the XCTest scheme so it never ran when the CI tests the Xcode project. I noticed the issue in SPM after #691 (comment) since SPM auto detects test files instead of depending on the project scheme.
The update to include the test is here: 225341b
@@ -4,7 +4,7 @@ import PackageDescription | |||
let package = Package( | |||
name: "CareKit", | |||
defaultLocalization: "en", | |||
platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6)], | |||
platforms: [.iOS(.v14), .macOS(.v11), .watchOS(.v7)], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bumps to minimum iOS 14 along with counterparts so SPM matches the Xcode Project.
name: "CareKitUITests", | ||
dependencies: ["CareKitUI"], | ||
path: "CareKitUI/CareKitUITests", | ||
exclude: ["Info.plist", "CareKitUI.xctestplan"]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test target was missing from SPM so CareKitUITests weren't showing up when the project was opened with Package.swift
@gavirawson-apple this is ready for review. You can see the passing tests in the CI here: cbaker6#8 The CI should begin running against this repo once it's approved |
// 8. Lock in the changes. If this fails, all | ||
// merged changes will be rolled back and | ||
// we'll need to try again later. | ||
try self.context.save() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bug wasn't easy to find, but was introduced with the changes in #553. The issue is with the last increment to the knowledge vector is never saved in context, causing data to be lost when saving OCK objects using a CareKit card or other view because it doesn't have the most up-to-date clock value.
The fix is just to save after the increment and probably have a heuristic that mentions a save should always occur after an increment at some point before exiting the respective block of code.
The current test cases don't catch this issue because they are testing the stores directly as oppose to using the CareKit cards to save the outcomes.
The example case to replicate the issue is below:
- Setup
Device1
to connect to aOCKRemoteServer
- Add tasks
- Save an outcome using a CareKit card and sync
Device1
to theOCKRemoteServer
- Bring a new
Device2
online and sync it to theOCKRemoteServer
.Device2
will now reflect everything onDevice1
- Delete the saved outcome using a CareKit card on
Device2
. This will trigger a pullRevision, but notice during pushRevisions no new revision records will be found because the CareKit didn't have the correct Knowledge Vector clock value because it wasn't saved in context. CausingDevice1
to never receive the deleted outcome, forcing the devices out-of-sync
I've tested the updates using my sample and using OCKWatchConnectivityPeer
. Before the fix I was able to get the iPhone and watch out-of-sync. After the updates, the devices remained synchronized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can also be done after push revisions is complete, but I'm not sure if it's actually better and requires extra code:
// 4. Detect and resolve any conflicts that exist. The store
// is a CRDT, so there aren't any conflicts at the data
// layer, but we want to resolve concurrent changes.
self.resolveConflicts { error in
self.context.perform {
do {
// 5. Lock in the changes. If this fails, all
// merged changes will be rolled back and
// we'll need to try again later.
try self.context.save()
// 6. Local revisions will now include any patches
// that were applied during conflict resolution.
let localKnowledge = self.context.knowledgeVector
let localRevisions = try self.computeRevisions(
since: remoteKnowledge
)
// 7. Bump knowledge vector to indicate that any
// objects created beyond this point in time
// are considered unknown to the peer.
self.context.knowledgeVector.increment(
clockFor: self.context.clockID
)
// 8. Push conflict resolutions + local changes to remote
remote.pushRevisions(
deviceRevisions: localRevisions,
deviceKnowledge: localKnowledge) { error in
if let error = error {
os_log("Failed to push revision. %{private}@",
log: .store, type: .error, error as NSError)
}
self.context.perform {
do {
// 9. Lock in the changes. If this fails, all
// merged changes will be rolled back and
// we'll need to try again later.
try self.context.save()
} catch {
self.context.rollback()
completion(error)
return
}
// 10. The sync is still considered successful
// even if the remote doesn't accept the
// push. The next time we sync with it, it
// will still have the same knowledge
// vector, which means the proper diff will
// be generated.
completion(nil)
}
}
} catch {
self.context.rollback()
completion(error)
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks amazing Corey, I just have one minor suggestion to consider before we merge away.
Co-authored-by: Gavi Rawson <51756298+gavirawson-apple@users.noreply.github.com>
@gavirawson-apple let me know if you need anything else from me on this PR |
Thanks Corey, everything is set here. We're just going through some process on our end. |
store
inOCKDailyPageViewController.swift
public readable, commit: 38be8ceOCKCDPostalAddress
init, commit: 863a5e8careKitStore->careStore
commit: 4bf2d81iOS
andwatchOS
to prevent breaking PR's onwatchOS
IPHONEOS_DEPLOYMENT_TARGET
to14
WATCHOS_DEPLOYMENT_TARGET
to7.0
CareKitFHIR
project to build/test onwatchOS
AsyncAlgorithms
to0.1.0
Note1: The watchOS platform should appear after this PR is merged to the main as the added
.spi.yml
tells Swift Package Index what scheme to use for building watch.