react-native-apple-shazamkit
Version:
🎵 Powerful music recognition for React Native using Apple's ShazamKit. Identify songs, get metadata, and integrate with Apple Music seamlessly.
551 lines (414 loc) • 14.1 kB
Markdown
# 🎵 React Native Apple ShazamKit
[](https://badge.fury.io/js/react-native-apple-shazamkit)
[](https://opensource.org/licenses/MIT)
[](https://github.com/rizwan92/expo-shazamkit)
A powerful React Native Expo module that brings Apple's ShazamKit audio recognition capabilities to your mobile applications. Identify music, get song metadata, and integrate with Apple Music seamlessly.
## 🎬 Preview
https://user-images.githubusercontent.com/30924086/229935589-ef3e60ae-10f0-4e0d-aebf-a0ce06d8dba2.mov
## ✨ Features
- 🎤 **Real-time Audio Recognition** - Identify songs playing around you
- 🎵 **Rich Metadata** - Get song title, artist, artwork, and more
- 🍎 **Apple Music Integration** - Direct links to Apple Music
- 📚 **Shazam Library** - Add discoveries to user's Shazam library
- 🔧 **Cross-platform** - Works on both iOS and Android
- ⚡ **TypeScript Support** - Full type definitions included
- 🎯 **Expo Compatible** - Works with Expo managed and bare workflows
## 📋 Requirements
- **iOS**: 15.0+ (ShazamKit requirement)
- **Android**: API 21+ (Android 5.0+)
- **Expo SDK**: 49+
- **React Native**: 0.70+
## 📦 Installation
```bash
npx expo install react-native-apple-shazamkit
```
For bare React Native projects, ensure you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
## ⚙️ Setup & Configuration
### iOS Configuration 🍏
#### 1. Update iOS Deployment Target
ShazamKit requires iOS 15.0+. Update your deployment target:
**Using expo-build-properties (Recommended):**
```json
{
"plugins": [
[
"expo-build-properties",
{
"ios": {
"deploymentTarget": "15.0"
},
"android": {
"minSdkVersion": 21
}
}
]
]
}
```
**For bare React Native projects:**
Update `ios/Podfile`:
```ruby
platform :ios, '15.0'
```
#### 2. Enable ShazamKit Service
1. Go to [Apple Developer Console](https://developer.apple.com/account/)
2. Navigate to **Certificates, Identifiers & Profiles** → **Identifiers**
3. Select your app identifier (or create a new one)
4. Under **App Services**, enable **ShazamKit**
5. Save your changes
#### 3. Add Microphone Permission
**Using the plugin (Recommended):**
```json
{
"plugins": [
[
"react-native-apple-shazamkit",
{
"microphonePermission": "This app needs microphone access to identify music"
}
]
]
}
```
**Manual setup for bare React Native:**
Add to `ios/YourApp/Info.plist`:
```xml
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to identify music</string>
```
#### 4. Install iOS Dependencies
```bash
npx pod-install
```
### Android Configuration 🤖
#### 1. Add Microphone Permission
Add to `android/app/src/main/AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
```
#### 2. Add ShazamKit Android Dependency
For **bare React Native** projects, you need to manually add the ShazamKit Android AAR to your app's dependencies.
**Option 1: Automatic setup (Recommended)**
```bash
npx react-native-apple-shazamkit setup-android
```
**Option 2: Manual setup**
1. Copy the AAR file from the module to your app:
```bash
cp node_modules/react-native-apple-shazamkit/android/libs/shazamkit-android-release.aar android/app/libs/
```
2. Add the dependency to your `android/app/build.gradle`:
```gradle
dependencies {
implementation files('libs/shazamkit-android-release.aar')
// ... other dependencies
}
```
**Note**: For Expo managed projects, this is handled automatically during the build process.
#### 3. Request Runtime Permission
```typescript
import { Platform } from "react-native";
import { request, PERMISSIONS, RESULTS } from "react-native-permissions";
const requestMicrophonePermission = async () => {
if (Platform.OS === "android") {
const result = await request(PERMISSIONS.ANDROID.RECORD_AUDIO);
return result === RESULTS.GRANTED;
}
return true; // iOS handles this automatically
};
```
## 🚀 Quick Start
```typescript
import React, { useState } from "react";
import { View, Button, Text, Image, Alert } from "react-native";
import * as Linking from "expo-linking";
import * as ShazamKit from "react-native-apple-shazamkit";
export default function App() {
const [isListening, setIsListening] = useState(false);
const [result, setResult] = useState(null);
const handleIdentifyMusic = async () => {
try {
// Check if ShazamKit is available
if (!(await ShazamKit.isAvailable())) {
Alert.alert("Error", "ShazamKit is not available on this device");
return;
}
setIsListening(true);
setResult(null);
// Start listening for audio
const matches = await ShazamKit.startListening();
if (matches.length > 0) {
setResult(matches[0]);
} else {
Alert.alert("No Match", "Could not identify the song");
}
} catch (error) {
Alert.alert("Error", error.message);
} finally {
setIsListening(false);
}
};
const addToLibrary = async () => {
if (result) {
const response = await ShazamKit.addToShazamLibrary();
Alert.alert(
response.success ? "Success" : "Error",
response.success
? "Added to Shazam library!"
: "Failed to add to library",
);
}
};
return (
<View style={{ flex: 1, justifyContent: "center", padding: 20 }}>
{result && (
<View style={{ alignItems: "center", marginBottom: 30 }}>
<Image
source={{ uri: result.artworkURL }}
style={{ width: 200, height: 200, borderRadius: 10 }}
/>
<Text style={{ fontSize: 24, fontWeight: "bold", marginTop: 15 }}>
{result.title}
</Text>
<Text style={{ fontSize: 18, color: "gray", marginBottom: 20 }}>
{result.artist}
</Text>
<View style={{ flexDirection: "row", gap: 10 }}>
{result.appleMusicURL && (
<Button
title="Open in Apple Music"
onPress={() => Linking.openURL(result.appleMusicURL)}
/>
)}
<Button title="Add to Shazam" onPress={addToLibrary} />
</View>
</View>
)}
<Button
title={isListening ? "Listening..." : "🎵 Identify Music"}
onPress={handleIdentifyMusic}
disabled={isListening}
/>
</View>
);
}
```
## 📖 API Reference
### Methods
#### `isAvailable(): Promise<boolean>`
Checks if ShazamKit is available on the current device.
```typescript
const available = await ShazamKit.isAvailable();
if (!available) {
console.log("ShazamKit not available");
}
```
#### `startListening(): Promise<MatchedItem[]>`
Starts listening for audio and attempts to identify any music. Returns an array of matched songs.
```typescript
try {
const matches = await ShazamKit.startListening();
if (matches.length > 0) {
console.log("Found song:", matches[0].title);
}
} catch (error) {
console.error("Recognition failed:", error);
}
```
#### `stopListening(): void`
Stops the audio recognition process.
```typescript
ShazamKit.stopListening();
```
#### `addToShazamLibrary(): Promise<{ success: boolean }>`
Adds the most recently identified song to the user's Shazam library.
```typescript
const result = await ShazamKit.addToShazamLibrary();
console.log("Added to library:", result.success);
```
### Types
#### `MatchedItem`
```typescript
interface MatchedItem {
/** Song title */
title?: string;
/** Artist name */
artist?: string;
/** Unique Shazam identifier */
shazamID?: string;
/** Apple Music track ID */
appleMusicID?: string;
/** Apple Music URL */
appleMusicURL?: string;
/** Album artwork URL */
artworkURL?: string;
/** Array of genres */
genres?: string[];
/** Web URL (Shazam page) */
webURL?: string;
/** Song subtitle/album */
subtitle?: string;
/** Music video URL */
videoURL?: string;
/** Whether content is explicit */
explicitContent?: boolean;
/** Match offset in seconds */
matchOffset?: number;
}
```
## 🎛️ Advanced Usage
### Error Handling
```typescript
import { ShazamKitError } from "react-native-apple-shazamkit";
try {
const matches = await ShazamKit.startListening();
} catch (error) {
if (error instanceof ShazamKitError) {
switch (error.code) {
case "PERMISSION_DENIED":
// Handle microphone permission denied
break;
case "NOT_AVAILABLE":
// Handle ShazamKit not available
break;
default:
// Handle other errors
break;
}
}
}
```
### Custom Recognition Duration
```typescript
// Listen for a specific duration (implementation may vary)
const listenWithTimeout = async (timeoutMs = 10000) => {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Recognition timeout")), timeoutMs),
);
try {
const result = await Promise.race([
ShazamKit.startListening(),
timeoutPromise,
]);
return result;
} catch (error) {
ShazamKit.stopListening();
throw error;
}
};
```
### Integration with Music Streaming Services
```typescript
const openInMusicApp = (song: MatchedItem) => {
const musicApps = [
{ name: "Apple Music", url: song.appleMusicURL },
{ name: "Spotify", url: `spotify:search:${song.title} ${song.artist}` },
{
name: "YouTube Music",
url: `https://music.youtube.com/search?q=${encodeURIComponent(
`${song.title} ${song.artist}`,
)}`,
},
];
// Show action sheet with music app options
ActionSheetIOS.showActionSheetWithOptions(
{
options: ["Cancel", ...musicApps.map(app => app.name)],
cancelButtonIndex: 0,
},
buttonIndex => {
if (buttonIndex > 0) {
const selectedApp = musicApps[buttonIndex - 1];
Linking.openURL(selectedApp.url);
}
},
);
};
```
## 🔧 Troubleshooting
### Common Issues
#### "ShazamKit is not available"
- Ensure iOS deployment target is 15.0+
- Verify ShazamKit is enabled in Apple Developer Console
- Check device compatibility (ShazamKit requires iOS 15.0+)
#### "Module was compiled with an incompatible version of Kotlin"
See our detailed [Compatibility Guide](./COMPATIBILITY.md) for Kotlin version issues.
#### Microphone permission issues
```typescript
import { check, request, PERMISSIONS, RESULTS } from "react-native-permissions";
const checkMicrophonePermission = async () => {
const permission =
Platform.OS === "ios"
? PERMISSIONS.IOS.MICROPHONE
: PERMISSIONS.ANDROID.RECORD_AUDIO;
const result = await check(permission);
if (result === RESULTS.DENIED) {
const requestResult = await request(permission);
return requestResult === RESULTS.GRANTED;
}
return result === RESULTS.GRANTED;
};
```
#### No matches found
- Ensure audio is clear and music is playing
- Check internet connectivity
- Verify the song is in Shazam's database
- Try listening for a longer duration
#### Android Release Build Error: "Direct local .aar file dependencies are not supported"
This error occurs when building a release AAR for Android. The issue is that the module includes a local AAR dependency which isn't allowed in release builds.
**For bare React Native projects:**
1. Copy the AAR file to your app:
```bash
mkdir -p android/app/libs
cp node_modules/react-native-apple-shazamkit/android/libs/shazamkit-android-release.aar android/app/libs/
```
2. Add the dependency to your `android/app/build.gradle`:
```gradle
dependencies {
implementation files('libs/shazamkit-android-release.aar')
// ... other dependencies
}
```
**For Expo managed projects:**
The AAR is automatically handled during the Expo build process. No additional configuration needed.
**Alternative solution for advanced users:**
If you need more control, you can publish the AAR to a Maven repository and include it as a regular dependency.
### Debug Mode
Enable debug logging to troubleshoot issues:
```typescript
// This is a conceptual example - actual implementation may vary
ShazamKit.setDebugMode(true);
```
## 📱 Platform Differences
| Feature | iOS | Android |
| ----------------------- | --------------------- | ------------------------ |
| Audio Recognition | ✅ Native ShazamKit | ✅ Custom Implementation |
| Apple Music Integration | ✅ Full Support | ✅ Web Links |
| Shazam Library | ✅ Native Integration | ✅ Web Integration |
| Offline Recognition | ✅ Limited | ❌ Requires Internet |
| Background Recognition | ✅ Supported | ⚠️ Limited |
## 🤝 Contributing
We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
### Development Setup
1. Clone the repository
2. Install dependencies: `yarn install`
3. Build the module: `yarn build`
4. Run the example: `cd example && yarn ios`
### Testing
```bash
yarn test
yarn lint
```
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🙏 Acknowledgments
- Apple's ShazamKit framework
- The Expo team for the excellent module infrastructure
- All contributors and users of this library
## 📞 Support
- 🐛 **Bug Reports**: [GitHub Issues](https://github.com/rizwan92/expo-shazamkit/issues)
- 💡 **Feature Requests**: [GitHub Discussions](https://github.com/rizwan92/expo-shazamkit/discussions)
- 📖 **Documentation**: [Wiki](https://github.com/rizwan92/expo-shazamkit/wiki)
- 💬 **Community**: [Discord](https://discord.gg/expo)
---
Made with ❤️ by the React Native community