Skip to content

Add records/resolve and records/accept share operations (#41, #42) - #428

Merged
leogdion merged 5 commits into
v1.0.0-beta.4from
41-42-records-resolve-accept
Aug 27, 2026
Merged

Add records/resolve and records/accept share operations (#41, #42)#428
leogdion merged 5 commits into
v1.0.0-beta.4from
41-42-records-resolve-accept

Conversation

@leogdion

Copy link
Copy Markdown
Member

Summary

Implements CloudKit Web Services' two record-sharing endpoints:

Public API:

letinfos=tryawait service.resolveShares([ShortGUID(value:"", shouldFetchRootRecord:true, rootRecordDesiredKeys:["title"])])letaccepted=tryawait service.acceptShares([ShortGUID(value:"")])

Auth: no database: parameter

Apple's reference fixes both paths' database scope to public, and both operations act on behalf of the current user. So — following the existing fetchCaller() precedent — they hard-code .public(.requires(.webAuth)) and expose nodatabase: parameter. This is not a silent policy default (cf. feedback_no_silent_policy_defaults): there is no valid alternative for the caller to choose, so the choice is removed from the API rather than defaulted.

Both endpoints validate the request as a whole — a bad short GUID fails the entire call rather than producing a per-item error — so there is deliberately noRecordResult-style per-item failure variant (same reasoning as assets/rereference).

How the wire format was verified

Both endpoints are absent from .claude/docs/webservices.md (and from Apple's current online docs), so per project memory reference_cloudkit_archived_endpoints.md the shapes were confirmed against Apple's archived CloudKit Web Services Reference:

SourceConfirms
FetchingRecordInformation.htmlPOST …/public/records/resolve, request { shortGUIDs: [ShortGUID] }, response { results: [ShortGUIDResult] }
AcceptingShareRecords.htmlPOST …/public/records/accept, same request/response shapes
Types.htmlShortGUID Dictionary, ShortGUID Result Dictionary, Share Participant Dictionary, and the share-related Record Dictionary keys — every key name, type, required flag, and enum value modeled here
.claude/docs/webservices.md:1610-1672Share creation keys (createShortGUID, forRecord, publicPermission, participants) and the share response keys
.claude/docs/cloudkitjs.md (CloudKit.RecordInfo, CloudKit.Share, CloudKit.ShareParticipant)Cross-check of the share/participant field sets

Changes

openapi.yaml (regenerated via ./Scripts/generate-openapi.shSources/MistKitOpenAPI/ was never hand-edited):

  • New paths records/resolve (operationId: resolveShortGUIDs) and records/accept (operationId: acceptShares).
  • New schemas ShortGUID, ShortGUIDResult, ShortGUIDResultResponse, ShareParticipant, ShareReference, ShareTargetReference.
  • Per the Accepting Share Records (records/accept) #42 gap analysis: share request keys on RecordRequest (createShortGUID, forRecord, publicPermission, participants) and share response keys on RecordResponse (shortGUID, share, publicPermission, participants, owner, currentUserParticipant).

Domain models (Sources/MistKit/Models/Sharing/): ShortGUID, ShareRecordInfo, ShareInfo, ShareParticipant, SharePermission, ShareParticipantType, ShareAcceptanceStatus, ShareDatabaseScope, SharePotentialMatch.

ShareInfo lifts the share-specific keys off a cloudKit.share record, because RecordInfo models a plain record and intentionally carries no sharing metadata. ShareDatabaseScope is deliberately separate from Database — it is a plain descriptor CloudKit returns, carrying no PublicAuthPreference. Environment gains Codable (it is already a String raw-value enum) so ShareRecordInfo can synthesize its conformance.

Operations: CloudKitService+ShareOperations.swift, CloudKitResponseProcessor+Sharing.swift, plus the two Operations.*.Output error-mapping extensions and OperationInputPath conformances.

Docs: CLAUDE.md/AGENTS.md operations table + a "Share Operations" section; README roadmap moves both issues into a new v1.0.0-beta.4 section.

Verification

CheckResult
swift build✅ Build complete
swift test571 tests in 178 suites passed (baseline 568; 11 sharing operation tests + model tests added)
mise exec -- swift-format✅ Applied, clean
./Scripts/lint.sh (swiftlint + swift-format lint + header.sh + periphery)0 violations, 0 serious in 408 files; "No unused code detected"
./Scripts/generate-openapi.sh re-run✅ Idempotent — committed output matches openapi.yaml

Tests assert the serialized request body (that shortGUIDs is sent in order, that shouldFetchRootRecord/rootRecordDesiredKeys are carried, and that omitted optionals are not emitted as nulls), full response-field mapping, the potentialMatchList ambiguous-caller path, ShareInfo extraction, and top-level BAD_REQUEST handling for both operations.

Not verified

  • No live-service call. Everything is verified against archived documentation and mock transports; neither endpoint was exercised against a real CloudKit container. Apple's archived pages carry no verbatim JSON examples for either endpoint, so field names come from the Types.html dictionaries rather than from a sample payload.
  • potentialMatchList is the least-documented part of the response — Types.html describes participantId + contactInformation{emailAddress, phoneNumber} but shows no example. Modeled as described; the fields are all optional so an unexpected shape degrades rather than throws.
  • Share creation is not wired into a MistKit convenience. The createShortGUID / forRecord / publicPermission / participants request keys are modeled in the schema (as Accepting Share Records (records/accept) #42's gap analysis asks) and are reachable via MistKitOpenAPI, but no curated "create a share" API is exposed on CloudKitService — that is a larger surface deserving its own issue.
  • No MistDemo integration phase was added for these endpoints, since they need a real share URL from a second iCloud account to exercise.

Closes#41
Closes#42

🤖 Generated with Claude Code

@coderabbitai

coderabbitaiBot commented Aug 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a51b771b-275e-4838-86b8-9ef3d2dce15b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claudeBot commented Aug 20, 2026

Copy link
Copy Markdown

Review: records/resolve / records/accept share operations

Went through the new sharing surface (CloudKitService+ShareOperations, CloudKitResponseProcessor+Sharing, the Sources/MistKit/Models/Sharing/ types, openapi.yaml, and the test suites). Overall this is a clean, well-documented addition that follows the repo's established patterns closely (public-DB/web-auth hardcoding mirrors fetchCaller(), exhaustive error-case switches mirror the other Operations.*.Output extensions, request/response separation matches the FieldValueRequest/FieldValueResponse split described in CLAUDE.md).

Potential bug: ShareInfo.sharedRecordName likely never resolves correctly

Sources/MistKit/Models/Sharing/ShareInfo.swift:86:

self.sharedRecordName = record.share?.recordName

This is documented as "The record name of the shared record this share governs," but ShareInfo.init(from:) is only ever invoked with the cloudKit.share record itself (ShareRecordInfo.swift:98-100 passes schema.share, which per ShortGUIDResult's schema is the full cloudKit.shareRecordResponse). So record.share here is that same share record's own share key, not a pointer to the root record being shared.

Per .claude/docs/webservices.md:1660-1672, the share record's own response does carry a share key — but reading that key off the share record itself either resolves to nil in practice or, at best, is self-referential (yielding the share record's own name rather than the root record's). Either way it can't produce "the shared record's name" as documented.

This is untested: ShareModelTests.swift:145 only asserts sharedRecordName == nil for an empty-constructed ShareInfo, and the mock share-record fixture in CloudKitServiceTests.Sharing+Helpers.swift (shareRecord(for:)) never populates a share key, so no test exercises a non-nil value. ShareRecordInfo.rootRecordName (sourced directly from ShortGUIDResult.rootRecordName) is the reliable way to get this today — worth double-checking against a live response whether ShareInfo.sharedRecordName should just be removed, or wired from a different source.

Minor / non-blocking

  • ShareRecordInfo.swift:89-91 falls back to ZoneID.defaultZone.zoneName when zoneID.zoneName is absent. Other conversions in the codebase (e.g. SubscriptionInfo+Schema.swift:141) don't need this fallback because the schema already guarantees zoneName. If CloudKit really can omit it here, a code comment on why would help; otherwise consider matching the stricter pattern.
  • SharePermission/ShareParticipantType/ShareAcceptanceStatus each hand-write 2-3 near-identical init(from:) overloads for the different generated payload enums (ShareParticipant.permissionPayload, ShortGUIDResult.participantPermissionPayload, RecordResponse.publicPermissionPayload, etc.). This is a direct consequence of the OpenAPI generator emitting distinct nested enums per call site rather than a design choice in this PR, so not asking for a change — just noting the duplication in case a future schema tweak (e.g. a shared $ref'd enum component) could collapse it.

Test coverage

Good breadth: request-body ordering/omitted-optionals, full field mapping, missing-root-record, potentialMatchList ambiguous-caller path, shareInfo == nil for a non-share record, and top-level BAD_REQUEST handling for both endpoints. The one gap is the sharedRecordName case noted above.

Security / performance

No concerns — no new secrets/logging, no unbounded loops or allocations, and the hardcoded .public(.requires(.webAuth)) routing correctly removes a database: choice that has no valid alternative (consistent with the project's "no silent policy defaults" convention).

🤖 Generated with Claude Code

@codecov

codecovBot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.17343% with 37 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (v1.0.0-beta.4@4fabb07). Learn more about missing BASE report.

Files with missing linesPatch %Lines
.../CloudKitService/CloudKitService+CreateShare.swift84.21%21 Missing ⚠️
Sources/MistKit/Models/ConversionError.swift70.00%3 Missing ⚠️
...urces/MistKit/Models/Sharing/ShareRecordPair.swift62.50%3 Missing ⚠️
.../MistKit/Models/Sharing/ShareTargetReference.swift72.72%3 Missing ⚠️
...s/MistKit/Models/Sharing/SharePotentialMatch.swift88.23%2 Missing ⚠️
Sources/MistKit/Models/Sharing/ShareInfo.swift96.96%1 Missing ⚠️
...rces/MistKit/Models/Sharing/ShareParticipant.swift95.65%1 Missing ⚠️
...urces/MistKit/Models/Sharing/ShareRecordInfo.swift97.61%1 Missing ⚠️
...PI/Operations/Operations.acceptShares.Output.swift93.75%1 Missing ⚠️
...erations/Operations.resolveShortGUIDs.Output.swift93.75%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## v1.0.0-beta.4 #428 +/- ##
================================================
Coverage ? 80.49% ================================================
Files ? 177 Lines ? 4364 Branches ? 0 ================================================
Hits ? 3513 Misses ? 851 Partials ? 0 
FlagCoverage Δ
mistdemo-spm-macos11.54% <5.53%> (?)
mistdemo-swift-6.2-jammy11.55% <5.53%> (?)
mistdemo-swift-6.2-noble11.55% <5.53%> (?)
mistdemo-swift-6.3-jammy11.55% <5.53%> (?)
mistdemo-swift-6.3-noble11.55% <5.53%> (?)
mistdemo-swift-6.4-jammy11.55% <5.53%> (?)
mistdemo-swift-6.4-noble11.55% <5.53%> (?)
spm78.91% <93.17%> (?)
swift-6.1-jammy78.84% <93.17%> (?)
swift-6.1-noble78.79% <93.17%> (?)
swift-6.2-jammy78.82% <93.17%> (?)
swift-6.2-noble78.93% <93.17%> (?)
swift-6.3-jammy78.79% <93.17%> (?)
swift-6.3-noble79.07% <93.17%> (?)
swift-6.4-jammy78.79% <93.17%> (?)
swift-6.4-noble78.95% <93.17%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@leogdion
leogdion marked this pull request as draft August 20, 2026 19:37
@claude

claudeBot commented Aug 20, 2026

Copy link
Copy Markdown

Code Review — PR #428 (Standards + Spec)

Reviewed via two independent parallel passes: does the diff follow this repo's documented conventions (Standards), and does it faithfully implement issues #41/#42 (Spec)?

Standards

No hard violations of documented CLAUDE.md rules found — ACLs are explicit throughout, every import carries internal/public, Environment correctly picks up Codable for ShareRecordInfo, the .public(.requires(.webAuth)) hard-coding in CloudKitService+ShareOperations.swift matches the existing fetchCaller() pattern, and the openapi.yaml diff lines up with the generated MistKitOpenAPI types (no hand-edits detected).

Judgement-call smells (baseline, not standards breaches — repo conventions already override where relevant):

  • Duplicated code: AcceptCommand.swift's printSummary (~L203-221) and ResolveCommand.swift's (~L317-335) are byte-identical 18-line blocks. Minor — matches the existing one-file-per-command convention.
  • Duplicated code: SharePermission.swift has three near-identical init(from:) overloads re-switching the same four cases (mirrored in ShareAcceptanceStatus.swift/ShareParticipantType.swift). Understandable given generator-produced distinct nested types per schema usage, but ~30 lines could collapse into a shared raw-value mapping helper.
  • Feature envy / odd coupling: AcceptConfig.swift:483 calls ResolveConfig.parseShortGUIDs(from:)AcceptConfig depends on a sibling command's config type for core parsing rather than a shared helper. Works, but reads oddly under the codebase's DI conventions.
  • Copy-paste artifact: CloudKitServiceTests.Sharing+Accept.swift:2 and ...+Resolve.swift:2 both carry the header comment CloudKitServiceTests.Sharing.swift, a leftover from splitting one file into three. Scripts/header.sh should normally fix this.

Spec

(a) Missing/partial requirements: none. Both endpoints, both schema groups, request-side share-creation keys, and response-side share fields are all present. Both curated methods (resolveShares(_:), acceptShares(_:)) exist with the documented no-database:-parameter, .public(.requires(.webAuth)) design applied consistently to both.

(b) Scope beyond the issues' literal text (not flagged as a defect): the MistDemo AcceptCommand/ResolveCommand (+ Config/Error types), WebServer+Shares.swift, WebRequests+Shares.swift, CloudKitService+WebBackend+Shares.swift, and integration test phases go beyond "add the endpoint + schemas + curated methods." ResolveCommand was already a pending CLI stub this PR fills in, and AcceptCommand follows the established one-command-per-operation convention — reasonable dogfooding infrastructure, not gratuitous creep.

(c) Implementation accuracy: endpoint paths/operationIds match (records/resolveresolveShortGUIDs, records/acceptacceptShares). Schema names diverge from the issue's suggested names (RecordInfo/RecordInfosResponseShortGUIDResult/ShortGUIDResultResponse), but field content is a superset of what was asked — nothing is actually missing, only renamed. Share creation is reachable only via MistKitOpenAPI (no curated CloudKitService convenience), matching the PR's own stated "left out deliberately" caveat. potentialMatchList is modeled all-optional as described. No incorrect-implementation defects found.

Summary

Standards: 0 hard violations, 4 judgement-call smells (worst: the SharePermission/ShareAcceptanceStatus/ShareParticipantType triple-duplicated init(from:) switches — minor, generator-shape-driven).
Spec: 0 gaps, 0 wrong implementations, 1 noted-but-reasonable scope note (MistDemo dogfooding infra). Both endpoints correctly implement #41/#42 including the deliberate no-database:-parameter design.

Both self-flagged verification caveats from the issue threads (no live-service call made against a real CloudKit container; potentialMatchList shape unverified) still stand as open risk for both endpoints — not something this review can close, since it requires a live container.

🤖 Generated with Claude Code

@leogdion
leogdionforce-pushed the 41-42-records-resolve-accept branch from 26968f9 to dd8054cCompareAugust 21, 2026 15:02
@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Review

Reviewed against CLAUDE.md conventions (import ACLs, PublicAuthPreference/fetchCaller() precedent, fail-loud conversion philosophy, Swift Testing patterns). Read the merged source in Sources/MistKit/Models/Sharing/, CloudKitService+ShareOperations.swift, CloudKitResponseProcessor+Sharing.swift, the two Operations.*.Output.swift files, openapi.yaml's new schemas, all five sharing test files, and the MistDemo integration/server/CLI wiring, plus the full PR diff.

Bugs / Correctness

None found. resolveShares/acceptShares follow the standard client(for:) → request → responseProcessormapToCloudKitError pattern used elsewhere in the codebase. Field-by-field mapping in ShareRecordInfo.init(from:), ShareInfo.init?(from:), ShareParticipant.init(from:), and SharePotentialMatch.init(from:) all line up with the new openapi.yaml schemas (ShortGUID, ShortGUIDResult, ShareParticipant, ShareTargetReference, ShareReference). No force-unwraps, no off-by-ones; results ?? [] correctly handles a missing results array.

API Design

No issues. Hard-coding .public(.requires(.webAuth)) with no database: parameter matches the existing fetchCaller() precedent exactly (same wrapping pattern, same doc-comment rationale for why there's no database: choice to expose). MistDemo's integration wiring correctly gates ResolveRecordsPhase/AcceptSharesPhase behind the public+web-auth test path and documents their exclusion from the private-database test, so there's no risk of a caller unexpectedly hitting these against credentials that lack web-auth.

Security

None found. ShortGUID values travel in the JSON request body via the generated Components.Schemas.ShortGUID type — no manual string interpolation into URLs. No secrets/tokens appear in any log statement in the touched MistDemo server/CLI files, and no try!/as! force-unwraps in that new demo server code. The asset-upload transport-separation concern (separate URLSession for CDN vs. CloudKit API per this repo's AssetUploader design) doesn't apply here since neither endpoint touches the CDN.

Test Coverage

Good breadth overall: success-path full-field mapping, request-order preservation for shortGUIDs, optional-field omission on the wire (shouldFetchRootRecord/rootRecordDesiredKeys not serialized as null when nil), root-record omission, the potentialMatchList ambiguous-caller path, empty-results handling, BAD_REQUEST.badRequest(reason:) mapping for both operations, ShareInfo lift-out vs. nil-for-plain-record, and Codable/default-value coverage in ShareModelTests.swift.

A couple of small gaps worth a follow-up (non-blocking):

  • ShareInfo.owner / ShareInfo.currentUserParticipant mapping isn't asserted — the shared test fixture shareRecord(for:) (CloudKitServiceTests.Sharing+Helpers.swift) only populates participants, not owner/currentUserParticipant.
  • No test exercises resolveShares([]) / acceptShares([]) with an empty short-GUID array.

Style / Conventions

None found. Every new file correctly uses internal import/public import (no bare imports). New public types are Sendable; Equatable/Hashable conformance is present only where every stored property supports it, consistent with the rest of the codebase. Type-member ordering matches the swiftlint type_contents_order convention used elsewhere.

One minor nit: ShareRecordInfo.environment hand-rolls a switch from the generated environment enum to Environment.development/.production rather than Environment(rawValue: $0.rawValue), even though Environment gained RawRepresentable(String)/Codable conformance in this same PR and the raw values line up exactly. Not incorrect, just slightly more code than necessary — there's no existing precedent either way elsewhere in the codebase, so this is a stylistic call, not a convention violation.

Overall

Solid, well-tested addition that follows the codebase's established patterns closely (error mapping, no-database:-param precedent, fail-loud conversion). No bugs or security concerns found. Good to merge as-is; the two test-coverage gaps above would be nice to close in a fast-follow but aren't blocking.

🤖 Generated with Claude Code

leogdionand others added 2 commits August 21, 2026 15:14
Implements CloudKit Web Services' two sharing endpoints, both documented
only in Apple's archived CloudKit Web Services Reference:
- `records/resolve` (#41) — resolves share short GUIDs into information
about the shared records: root record, `cloudKit.share` record, owner
identity, and the caller's participation.
- `records/accept` (#42) — accepts shares on behalf of the current user,
returning the same result shape with the caller's resulting
participation.
Both take `{ shortGUIDs: [ShortGUID] }` and return
`{ results: [ShortGUIDResult] }`. Apple's reference fixes the path's
database scope to `public`, and both act on behalf of the *current*
user, so — like `fetchCaller()` — they hard-code
`.public(.requires(.webAuth))` and expose no `database:` parameter.
Both validate the request as a whole (a bad short GUID fails the entire
call), so there is no per-item RecordResult-style failure variant.
Spec changes (openapi.yaml, regenerated via Scripts/generate-openapi.sh):
- New paths `records/resolve` + `records/accept`.
- New schemas `ShortGUID`, `ShortGUIDResult`, `ShortGUIDResultResponse`,
`ShareParticipant`, `ShareReference`, `ShareTargetReference`.
- Share request keys on `RecordRequest` (`createShortGUID`, `forRecord`,
`publicPermission`, `participants`) and share response keys on
`RecordResponse` (`shortGUID`, `share`, `publicPermission`,
`participants`, `owner`, `currentUserParticipant`), per the #42 gap
analysis.
Domain models land in Sources/MistKit/Models/Sharing/. `ShareInfo` lifts
the share-specific keys off a `cloudKit.share` record, since `RecordInfo`
models a plain record and carries no sharing metadata. `Environment`
gains `Codable` so `ShareRecordInfo` can synthesize it.
Verified: swift build, swift test (571 tests, 178 suites, all passing),
swift-format, and ./Scripts/lint.sh (0 violations, no unused code).
Closes#41Closes#42
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Flip ResolveCommand off PendingStub, add AcceptCommand, wire integration
phases and web routes so records/resolve and records/accept can be tested.
Co-authored-by: Cursor <cursoragent@cursor.com>
@leogdion

Copy link
Copy Markdown
MemberAuthor

Review/merge order note

Proposed order for the v1.0.0-beta.4 queue: #425#424#427#426#429#428

This PR should go last — and it's still marked draft, so that lines up.

Reasons, none of them about quality:

Testing

Both records/resolve and records/accept are absent from .claude/docs/webservices.mdand from Apple's current online docs — the shapes rest entirely on the archived reference. That makes this the PR in the queue most in need of a live round-trip against CloudKit before merge:

  • a real short GUID resolved with shouldFetchRootRecord: true + rootRecordDesiredKeys, confirming the root record and the cloudKit.share record both come back as modeled;
  • an actual acceptShares call, confirming the returned participation matches ShareAcceptanceStatus as decoded;
  • one deliberately bad short GUID, to confirm the whole-request-fails behavior the description assumes (and therefore that omitting a RecordResult-style per-item failure variant is correct).

The .public(.requires(.webAuth)) hard-coding and the absent database: parameter both read as right to me — same shape as fetchCaller(), and there's no valid alternative for a caller to choose. Worth confirming against a live call that a non-public scope really is rejected server-side, which would settle it.

Supports #437 with a curated createShare path (cloudkit.share wire type), MistDemo sharer/sharee integration, and required share fields that fail loudly when incomplete.
Co-authored-by: Cursor <cursoragent@cursor.com>

@leogdionleogdion left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look for types with similar properties and think about whether we should create a single shared struct or a protocol.

Comment on lines +72 to +74
// swiftlint:disable:next force_unwrapping
// swift-format-ignore: NeverForceUnwrap
URL(string: "https://www.icloud.com/share/\(shortGUID)")!

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this a baseURL or baseURLComponent we can just append to

Comment on lines +39 to +54
/// Contact details CloudKit holds for a potential participant.
public struct ContactInformation: Codable, Sendable, Equatable, Hashable {
/// The candidate's email address, when known.
public let emailAddress: String?
/// The candidate's phone number, when known.
public let phoneNumber: String?

/// Initialize contact information.
/// - Parameters:
/// - emailAddress: The candidate's email address.
/// - phoneNumber: The candidate's phone number.
public init(emailAddress: String? = nil, phoneNumber: String? = nil) {
self.emailAddress = emailAddress
self.phoneNumber = phoneNumber
}
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we already have structure like this? Either way it should not be an inner type

Comment on lines +104 to +144
private static func environment(
from payload: Components.Schemas.ShortGUIDResult.environmentPayload
) -> Environment {
switch payload {
case .development: .development
case .production: .production
}
}

private static func zoneID(from schema: Components.Schemas.ZoneID) -> ZoneID {
ZoneID(
zoneName: schema.zoneName ?? ZoneID.defaultZone.zoneName,
ownerName: schema.ownerName
)
}

private static func sharePair(
from schema: Components.Schemas.RecordResponse?
) throws(ConversionError) -> (RecordInfo?, ShareInfo?) {
guard let schema else { return (nil, nil) }
let share = try RecordInfo(from: schema)
guard let shareInfo = ShareInfo(from: schema) else {
try ConversionError.shareIncomplete.reportAndThrow()
}
return (share, shareInfo)
}

private static func potentialMatches(
from schemas: Components.Schemas.ShortGUIDResult.potentialMatchListPayload?
) throws(ConversionError) -> [SharePotentialMatch] {
let wireMatches = schemas ?? []
var matches: [SharePotentialMatch] = []
matches.reserveCapacity(wireMatches.count)
for matchSchema in wireMatches {
guard let match = SharePotentialMatch(from: matchSchema) else {
try ConversionError.sharePotentialMatchMissingParticipantId.reportAndThrow()
}
matches.append(match)
}
return matches
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static method are usually a good sign of possibly going in an initializer

) throws(ConversionError) -> [SharePotentialMatch] {
let wireMatches = schemas ?? []
var matches: [SharePotentialMatch] = []
matches.reserveCapacity(wireMatches.count)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is reserveCapacity actually a good practice?

return (share, shareInfo)
}

private static func potentialMatches(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be moved to a static method on SharePotentialMatch


private static func sharePair(
from schema: Components.Schemas.RecordResponse?
) throws(ConversionError) -> (RecordInfo?, ShareInfo?) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never use tuples when possible. This should be on a new type with an initializer.

/// (``CloudKitService/acceptShares(_:)``) a share.
public struct ShortGUID: Codable, Sendable, Equatable, Hashable {
/// The value of the short global ID.
public let value: String

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be a Data struct or UUID?

leogdionand others added 2 commits August 27, 2026 17:12
…GUID.Value, and typed conversions.
Lift shared contact details, replace conversion tuples/helpers with proper types and inits, and build invite URLs from a reusable base.
Co-authored-by: Cursor <cursoragent@cursor.com>
Make ShortGUID a String typealias and rename the resolve/accept dictionary shape to ShortGUIDDictionary.
Co-authored-by: Cursor <cursoragent@cursor.com>
@leogdion
leogdion merged commit 9e6b9d6 into v1.0.0-beta.4Aug 27, 2026
1 check passed
@leogdion
leogdion deleted the 41-42-records-resolve-accept branch August 27, 2026 22:09
leogdion added a commit that referenced this pull request Aug 29, 2026
* Add phone-number support to MistDemo web users/discover (#398)
* Remove deprecated API, model server error codes, refactor FieldValue conversion, add cloud toolchain (#424, #421, #378, #358, #295)
* Add confirmed zone metadata (syncToken, atomic) to zone schemas (#427)
* Add records/resolve and records/accept share operations (#41, #42, #428)
* Add custom/shared zone support to the query path (#146) (#426)
* Add changes/database and changes/zone endpoints; deprecate zones/changes (#401, #47, #46)
* Clear two Linux build warnings (#433)
* modifyZones per-zone failures (#431) + zones/changes metaSyncToken (#430) (#443)
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@leogdion