Connect Apple Music and Spotify-via-Last.fm on iPhone

- Local Expo module (Swift): MusicKit authorization, Music User Token via
  MusicUserTokenProvider, and live Music app now-playing while the app is
  open (MediaPlayer notifications), reported through the device endpoint
- One-tap Apple Music onboarding on iOS; Apple Music row in Settings
- Sign in with Last.fm, plus a guided Spotify-via-Last.fm flow when direct
  Spotify sign-in is capped
- Device registration moved to shared code used by Android and iOS
Not yet compiled for iOS (no Xcode here); needs an EAS build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
2026-10-04 07:12:19 -04:00
co-authored by Claude Opus 5.5
parent ca97f1c3ac
commit 1d4aeee0dd
16 changed files with 522 additions and 78 deletions
@@ -0,0 +1,16 @@
export type AppleMusicAuthorization = 'authorized' | 'denied' | 'restricted' | 'notDetermined' | 'unknown';
export type MusicAppPlayback =
| { state: 'stopped' }
| {
state: 'playing' | 'paused' | 'stopped';
title: string;
artist: string;
album: string;
durationMs: number;
storeId: string;
};
export type AppleMusicModuleEvents = {
onNowPlayingChange: (playback: MusicAppPlayback) => void;
};
@@ -0,0 +1,13 @@
import { NativeModule, requireOptionalNativeModule } from 'expo';
import type { AppleMusicAuthorization, AppleMusicModuleEvents, MusicAppPlayback } from './AppleMusic.types';
declare class AppleMusicModule extends NativeModule<AppleMusicModuleEvents> {
authorizationStatus(): AppleMusicAuthorization;
requestAuthorization(): Promise<AppleMusicAuthorization>;
getUserToken(developerToken: string): Promise<string>;
getNowPlaying(): Promise<MusicAppPlayback>;
}
/** iOS only. null on Android, web and Expo Go, so callers must feature-check. */
export default requireOptionalNativeModule<AppleMusicModule>('AppleMusic');