Skip to content

Add Find My Friends helper for newer macOS - #65

Open
qayshp wants to merge 5 commits into
BlueBubblesApp:masterfrom
qayshp:codex/findmy-friends-upstream
Open

Add Find My Friends helper for newer macOS#65
qayshp wants to merge 5 commits into
BlueBubblesApp:masterfrom
qayshp:codex/findmy-friends-upstream

Conversation

@qayshp

@qayshp qayshp commented Jul 15, 2026

Copy link
Copy Markdown

Summary

  • add a dedicated com.apple.findmy helper for Friends on macOS 15 and later
  • use FindMyLocateObjCWrapper to enumerate friends and refresh locations
  • return one deterministic, allowlisted record per stable friend handle
  • preserve slow or offline friends with null location fields and explicit partial-response metadata
  • queue completed responses across a server disconnect and register before flushing them
  • provide a side-effect-free universal build for Intel and Apple Silicon

Behavior

A refresh coordinator seeds every friend from the current Find My cache, then requests an updated location. Slow refreshes do not remove friends from the response. Friend-list and per-handle deadlines resolve once, ignore duplicate or late callbacks, and report timed-out or unidentifiable records explicitly.

Private-framework descriptions are never serialized. Payloads contain only named handle, coordinate, address, timestamp, status, and accuracy fields. Friend wrapper objects are resolved to the underlying FMLHandle before calling private-framework methods.

Project cleanup

  • remove unused AppKit and private-framework link entries
  • remove duplicate CocoaAsyncSocket references and obsolete linker settings
  • remove nonexistent Swift bridging/module settings
  • remove empty build phases, unused workspace metadata, and an unused prefix-header resource
  • correct the product reference and declare macOS as the supported platform
  • keep the vendored CocoaAsyncSocket dependency used by the existing project layout

Building does not install into /Applications, terminate Find My, or modify a BlueBubbles installation.

Validation

  • ./FindMy/MacOS-15+/Tests/run-tests.sh passes
  • ./FindMy/MacOS-15+/scripts/build-universal.sh passes for x86_64 and arm64
  • the result has install name @rpath/BlueBubblesFindMyHelper.dylib
  • static analysis reports no first-party helper diagnostics; remaining diagnostics are confined to the vendored CocoaAsyncSocket source
  • the exact packaged artifact has SHA-256 99e5e9596c0c095f08f5083353053d77fbf458e54727f8160dabe3873c4fa39a
  • Find My loaded that artifact on macOS 15.7.7
  • authenticated GET/refresh/GET calls returned HTTP 200 with all 8 Friends records and no private/debug fields
  • location availability followed Find My runtime state: an earlier run returned 5 coordinate-bearing and 3 null-location records; the final fresh-process check returned the same 8 friends with null current locations

Companion server PR: BlueBubblesApp/bluebubbles-server#824

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Maintainer review: this demonstrates the macOS 16 Friends path, but it needs production hardening before merge. Remove machine-local build settings and opaque framework descriptions from the wire format, make timeout responses complete and explicitly partial when necessary, and provide a repeatable universal artifact build. Please add serializer/transaction tests covering a nil handle, no location, cached location, live location, and refresh timeout.

Comment thread FindMy/MacOS-16+/BlueBubblesHelper.xcodeproj/project.pbxproj Outdated
Comment thread FindMy/MacOS-16+/BlueBubblesHelper/BlueBubblesHelper.m Outdated
Comment thread FindMy/MacOS-16+/BlueBubblesHelper/BlueBubblesHelper.m Outdated
Comment thread FindMy/MacOS-15+/BlueBubblesHelper/BlueBubblesHelper.m Outdated
@qayshp

qayshp commented Jul 21, 2026

Copy link
Copy Markdown
Author

FYI @zlshames If instead of looking at cache decryption as in BlueBubblesApp/bluebubbles-server#810, I have this work-in-progress branch and PR adding Find My support on newer Mac OS to the Private API.

For the FindMy part, it fetches and reads from the DB using the decryption keys, but does it also poll for changes? We are trying to fix the findmy implementation in the private API plugin, and i wonder if this can be the baseline, while the papi helper does the live updates. Trying to figure out how to incorporate it.

BlueBubblesApp/bluebubbles-server#810 (comment)

While it is working and I am using it locally, it is not up to par for wider use yet.

@qayshp
qayshp marked this pull request as ready for review July 21, 2026 07:49
@zlshames

Copy link
Copy Markdown
Member

FYI @zlshames If instead of looking at cache decryption as in BlueBubblesApp/bluebubbles-server#810, I have this work-in-progress branch and PR adding Find My support on newer Mac OS to the Private API.

For the FindMy part, it fetches and reads from the DB using the decryption keys, but does it also poll for changes? We are trying to fix the findmy implementation in the private API plugin, and i wonder if this can be the baseline, while the papi helper does the live updates. Trying to figure out how to incorporate it.

BlueBubblesApp/bluebubbles-server#810 (comment)

While it is working and I am using it locally, it is not up to par for wider use yet.

I was testing it, but I think we are going to go the papi route instead. There's a lot more setup and gotchas to account for with the cache decryption method

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Second maintainer-style pass on c0114f6: I do not see a remaining blocking finding in the updated diff.

The earlier concerns are addressed: machine-local build settings and copied private headers are gone; the private API surface is limited to selectors this helper uses; payloads are explicitly allowlisted; nil locations and unidentifiable records are handled without exporting opaque descriptions; timeout responses are deterministic and explicitly partial; and the build produces and verifies an x86_64/arm64 artifact with the expected install name.

I reran the payload tests and universal build after the cleanup. I also validated this exact helper through the companion packaged server: Find My loaded the dylib from that app bundle, and the Friends refresh returned 8 records, 5 with coordinates and 3 without a current location.

Non-blocking test gap: the payload/partial metadata is tested, but the asynchronous coordinator itself is still covered by live validation rather than a fake FindMyLocateSession. Before calling this fully regression-proof, I would add tests for friend-list timeout fallback, per-handle timeout, duplicate or late completion callbacks, and reconnect during an in-flight refresh.

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final maintainer pass on 324b757c: the asynchronous coverage gap from my previous review is now closed.

The refresh orchestration is isolated behind a fakeable session protocol and tested for friend-list fallback, per-handle deadlines, duplicate and late completions, deterministic partial responses, reconnect queue ordering, and stale socket callbacks. During live validation, the first extraction exposed a real private-framework contract issue: a friend wrapper also answered identifier, but Swift required the nested FMLHandle. The implementation now performs strict FMLHandle selection when that class is available, and the test fixture reproduces that shape so the crash cannot regress silently.

The helper tests and universal build pass. The exact rebuilt dylib was then packaged, loaded into Find My, reconnected after a controlled Find My restart, and returned 8 Friends records through the Android-facing route, including 5 with coordinates and 3 without. Final logs contain no Swift array element cast, timeout, framing, or transaction error.

I do not see another helper change needed before merge.

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final maintainer-style pass after synchronizing with upstream/master:

Commit e916e83 removes project cruft that would make this harder to review: unused AppKit/private-framework links, duplicate socket references, nonexistent Swift settings, empty phases, workspace metadata, and an unused prefix-header resource. Runtime private-framework loading remains explicit in the helper, and the vendored CocoaAsyncSocket dependency remains because it is compiled and matches the repository's existing dependency layout.

The first-party helper has no static-analyzer diagnostics. Unit tests and the universal x86_64/arm64 build pass, the install name is correct, and the exact artifact was loaded by Find My from the packaged server app. Live GET/refresh/GET returned HTTP 200 with all 8 Friends records and no private/debug fields.

I do not see another actionable code or project-configuration finding before maintainer review. Remaining analyzer/whitespace diagnostics are confined to the untouched vendored CocoaAsyncSocket sources.

Sign up for free to 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.

2 participants