Skip to content

feat: add profile and contacts fetching from pubky#476

Open
ben-kaufman wants to merge 20 commits intomasterfrom
feat/pubky-profile
Open

feat: add profile and contacts fetching from pubky#476
ben-kaufman wants to merge 20 commits intomasterfrom
feat/pubky-profile

Conversation

@ben-kaufman
Copy link
Contributor

@ben-kaufman ben-kaufman commented Mar 5, 2026

Summary

Integrates Pubky decentralized identity into Bitkit, allowing users to connect their Pubky profile via Pubky Ring authentication. Once connected, the user's profile name and avatar appear on the home screen header, a full profile page shows their bio, links, and shareable QR code, and a contacts section lists people they follow on the Pubky network.

What's included

  • Authentication flow via Pubky Ring deep links (pubkyauth://) with relay-based session exchange, session persistence in Keychain, and automatic restoration on app launch
  • Profile page displaying name, bio, links, and a QR code with the profile picture overlaid
  • Contacts list with alphabetically grouped sections, search, and a "My Profile" header row
  • Contact detail page showing a contact's name, truncated public key, bio, links, and copy/share actions
  • Home screen integration showing the authenticated user's name and avatar in the header
  • PubkyService — service layer wrapping paykit-ffi (profile/contacts/payments) and bitkit-core (auth relay, PKDNS file fetching)
  • PubkyProfileManager — manages auth state, session lifecycle, and profile data with all FFI/Keychain/disk operations offloaded to background threads
  • ContactsManager — fetches contacts in parallel via withTaskGroup, groups them alphabetically, and resets on sign-out
  • PubkyImage component for loading pubky:// URIs with two-tier (memory + disk) caching
  • Suggestion card auto-dismiss when user is already authenticated
  • QR component — moved QR code generation off the main thread

New dependencies

  • paykit-rs (SPM, pinned revision) — Pubky SDK for profile, contacts, and payment operations
  • CoreBluetooth framework (linker flag, required by paykit-rs)

New files

File Purpose
Services/PubkyService.swift FFI bridge to paykit-ffi and bitkit-core
Managers/PubkyProfileManager.swift Auth state, session persistence, profile loading
Managers/ContactsManager.swift Contact fetching, parallel profile loading, alphabetical grouping
Models/PubkyProfile.swift Profile data model with placeholder factory
Components/PubkyImage.swift pubky:// image loader with disk+memory cache
Views/Profile/ProfileView.swift Full profile page (name, bio, links, QR)
Views/Profile/PubkyRingAuthView.swift Pubky Ring auth flow UI
Views/Contacts/ContactsIntroView.swift Contacts onboarding screen
Views/Contacts/ContactsListView.swift Grouped, searchable contacts list
Views/Contacts/ContactDetailView.swift Contact detail (bio, links, copy/share)

Modified files

File Change
AppScene.swift Inject PubkyProfileManager and ContactsManager, initialize Paykit early, eager-load contacts on auth
MainNavView.swift Wire up profile/contacts/auth navigation routes
Header.swift Show profile name + avatar, navigate to profile
Suggestions.swift Auto-dismiss profile card when authenticated
QR.swift Async QR generation via .task(id:)
Info.plist LSApplicationQueriesSchemes for pubkyauth, ATS config
Keychain.swift Add paykitSession entry type
NavigationViewModel.swift Add .pubkyRingAuth, .contactsIntro, .contactDetail(publicKey:) routes
ProfileIntro.swift Updated layout and styling
Colors.swift Add pubkyBrand color
Localizable.strings Profile and contacts translation keys

Test plan

  • Fresh install: tap Profile in drawer → shows intro → continue → auth screen
  • Auth screen: shows "Download Pubky Ring" if Ring not installed, "Authorize" if installed
  • Authorize → opens Pubky Ring → approve → returns to Bitkit → navigates to profile page
  • Profile page shows name, bio, links, QR code with profile picture overlay
  • Home header shows profile name and avatar after authentication
  • Kill and relaunch app → session restored, profile/contacts shown without re-auth
  • Sign out from profile page → returns to unauthenticated state, contacts cleared
  • Profile suggestion card auto-dismisses when authenticated
  • Fresh install: tap Contacts in drawer → shows intro → continue → auth screen (if not yet authenticated)
  • After authenticating via Profile or Contacts intro → contacts list loads automatically
  • Contacts list shows "My Profile" row, alphabetical sections, and search
  • Search filters by name or public key; "My Profile" row hides during search
  • Tap a contact → detail page shows name, truncated key, bio, links
  • Copy button copies full public key; share button opens system share sheet
  • Contacts with no profile show a placeholder name (truncated public key)
ScreenRecording_03-04-2026.23-49-21_1.MP4

@ovitrif ovitrif added this to the 2.2.0 milestone Mar 9, 2026
@ovitrif ovitrif changed the title Feat: add profile fetching from pubky feat: add profile fetching from pubky Mar 10, 2026
ovitrif
ovitrif previously approved these changes Mar 11, 2026
Copy link
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

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

LGTM with some nit comments.

all tests worked great, tested:

  • open profile from topbar + drawer
  • 2x auth successes
  • button to download ring
  • share pk button(s)
  • signout button

@claude

This comment has been minimized.

@piotr-iohk
Copy link
Collaborator

Some observations (on version before force-push, as the current is not building):

  • authorize when no pubky ring installed results in App error, (in Android there is a popup message informing pubkyring not installed with option to download)
Screen.Recording.2026-03-16.at.12.37.15.mov
  • same as Android: "Hardcoded production domains — no staging support"
  • same as Android: Unrecoverable "Unable to load profile" state. Although restoring wallet is a workaround that resolves the "Unable to load profile" state unlike in Android

@piotr-iohk
Copy link
Collaborator

piotr-iohk commented Mar 16, 2026

Same thing with staging as for Android: synonymdev/bitkit-android#824 (comment)

... and then same as synonymdev/bitkit-android#824 (comment) ✅ :)

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 16, 2026

What are the plans for landing this, 2.2 is correct?

@pwltr This is indeed what we agreed on afterward-ish the sprint planning: unless someone sees an issue with it. We ship this without the announcements, as it's just adding some niceyiness to users who like to explore, without really enabling any small part of the bigger picture yet.

So it's just aesthetics from user's pov FWIW, but app is nicer and more friendly than without it ( (ie: I can see my profile picture at the top of the app if I auth with my pubky).

ovitrif
ovitrif previously approved these changes Mar 16, 2026
Copy link
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

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

Re-approving from my side, still worth considering & addressing the remaining remarks from teammates 🙏🏻 , but nothing we can't solve in a new PR imo

@ben-kaufman
Copy link
Contributor Author

Everything should be fixed, e2e tests issue not related, it fails the same way on other PRs too cc @piotr-iohk

@piotr-iohk
Copy link
Collaborator

Everything should be fixed, e2e tests issue not related, it fails the same way on other PRs too cc @piotr-iohk

Yes, there is some divergency, because v60 redesign is merged on iOS and not merged on Android yet.

@pwltr
Copy link
Contributor

pwltr commented Mar 17, 2026

What are the plans for landing this, 2.2 is correct?

@pwltr This is indeed what we agreed on afterward-ish the sprint planning: unless someone sees an issue with it. We ship this without the announcements, as it's just adding some niceyiness to users who like to explore, without really enabling any small part of the bigger picture yet.

So it's just aesthetics from user's pov FWIW, but app is nicer and more friendly than without it ( (ie: I can see my profile picture at the top of the app if I auth with my pubky).

Currently the UX is very poor for users that are not on pubky.app yet (probably >90% of users). Since you can't create a profile from Bitkit and you can't signup to the Synonym homeserver from within Pubky Ring without an invite code (it says to ask the Synonym team?) you just get stuck in somewhere along the flow without any hand holding. That is after going to the app store and downloading an additional app which can be argued is also not explained clearly enough as to why it is necessary.

Not sure what the solution will be but imo this is not a good experience and doesn't add anything for most users.

Simulator.Screen.Recording.-.iPhone.17.-.2026-03-17.at.10.27.05.mov

@aldertnl
Copy link

For additional guidance on scroll behavior, see this prototype demo recording:
https://github.com/user-attachments/assets/edd73093-0404-4061-af31-6cf865565a08

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 17, 2026

Currently the UX is very poor for users that are not on pubky.app yet (probably >90% of users). Since you can't create a profile from Bitkit and you can't signup to the Synonym homeserver from within Pubky Ring without an invite code (it says to ask the Synonym team?) you just get stuck in somewhere along the flow without any hand holding. That is after going to the app store and downloading an additional app which can be argued is also not explained clearly enough as to why it is necessary.

Not sure what the solution will be but imo this is not a good experience and doesn't add anything for most users.

[recording]

Thanks for the insights @pwltr, it looks to me that any of the possible solutions require extra work.

Remaining options:

  1. Use feature flags to merge this sooner
  2. Keep this work on a feature branch
  3. Require team to find a resolution
    1. address the issues with current broken flow for users without a pubky.app account
    2. brainstorm on release plans
    3. etc

Out of the 3 above, the option of feature flags seems more straightforward and with the most predictable outcome.

Wdyt?

@piotr-iohk
Copy link
Collaborator

There is a small inconsistency: iOS shows a single button - either "Authorize" (if Ring is installed) or "Download" (if not). Android always shows both buttons side by side. I believe Figma shows the two-button layout.

Screenshot 2026-03-17 at 17 39 29

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 17, 2026

There is a small inconsistency: iOS shows a single button - either "Authorize" (if Ring is installed) or "Download" (if not). Android always shows both buttons side by side. I believe Figma shows the two-button layout.

[side-by-side screenshots]

It's being discussed with Aldert in the Figma v60 design file, it seems to me we'll go with the iOS variant, showing only the relevant button.

@piotr-iohk
Copy link
Collaborator

As far as UX, I think I mentioned this briefly on android PR (but concerns both ofc), but even for users who already have Ring installed and a profile set up, the flow drops you into Ring for authorization with no automatic return to Bitkit. After approving, you're left staring at a checkmark in Ring and have to manually switch back. Would be great if Ring redirected back to Bitkit after successful (or denied) authorization.

According to Claude:

Ring already has the pattern for this — sessionAction.ts accepts a callback param and calls Linking.openURL(callbackUrl) to redirect back to the caller. The auth flow (ConfirmAuth.tsx) doesn't use this pattern though.

Two options:

  • Quick (Ring-side only): After successful auth in ConfirmAuth.tsx, auto-close/minimize Ring to return the user to Bitkit (which is already polling the relay in the background).
  • Proper: Extend pubkyauth:// URL to support an optional callback param (e.g. pubkyauth:///?relay=...&secret=...&caps=...&callback=bitkit://auth-complete). After auth, Ring opens the callback URL — same as sessionAction.ts already does. Would need changes in bitkit-core (append callback) and Ring (read + use it).

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 17, 2026

As far as UX, I think I mentioned this briefly on android PR (but concerns both ofc), but even for users who already have Ring installed and a profile set up, the flow drops you into Ring for authorization with no automatic return to Bitkit. After approving, you're left staring at a checkmark in Ring and have to manually switch back. Would be great if Ring redirected back to Bitkit after successful (or denied) authorization.

This is ongoing discussion, I totally agree with you and I have been rooting for Ring to use that widely used pattern that involves a callback url.

I discussed this with the Pubky team but the feedback I got is that it's not a high priority; so I have let it be that way at that time.

I think we have to reconsider this, so I will check with them for middle-ground options.

@aldertnl
Copy link

discussed this with the Pubky team but the feedback I got is that it's not a high priorit

FWIW I would say it is a high priority from product UX perspective

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 17, 2026

discussed this with the Pubky team but the feedback I got is that it's not a high priorit

FWIW I would say it is a high priority from product UX perspective

I agree, but I think the current task on the Pubky roadmap includes the PWA updates, so we could start by splitting out what we need for Bitkit integration.

@coreyphillips
Copy link

Regarding callback support in Ring please see the draft PR here. I'll resume work on it shortly after I wrap up my current set of tasks.

@pwltr
Copy link
Contributor

pwltr commented Mar 17, 2026

Out of the 3 above, the option of feature flags seems more straightforward and with the most predictable outcome.

I think merge to avoid having to do conflict resolution over an extended period but revert to "Coming Soon" state before, ie. menu links navigate to "Coming soon" screens and remove the profile name from the header. Full on feature flag system seems overkill to me, but let me know your thoughts.

@piotr-iohk
Copy link
Collaborator

Out of the 3 above, the option of feature flags seems more straightforward and with the most predictable outcome.

I think merge to avoid having to do conflict resolution over an extended period but revert to "Coming Soon" state before, ie. menu links navigate to "Coming soon" screens and remove the profile name from the header. Full on feature flag system seems overkill to me, but let me know your thoughts.

One benefit of a feature flag over hard-coded hiding is that we can develop and run E2E tests for it (with the flag enabled in E2E builds). Otherwise we'd have to write those tests later when the feature goes live, with no automated coverage in the meantime.

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 18, 2026

I was thinking of a barebones feature flag support approach: build flags that read from env vars.
Those would work great with E2E tests setup

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.

6 participants