@wwdrew/expo-spotify-sdk 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +378 -138
- package/android/build.gradle +10 -0
- package/android/consumer-rules.pro +5 -0
- package/android/libs/SETUP.md +29 -0
- package/android/src/main/java/expo/modules/spotifysdk/ExpoSpotifySDKModule.kt +146 -9
- package/android/src/main/java/expo/modules/spotifysdk/SpotifyAppRemoteCoordinator.kt +556 -0
- package/android/src/main/java/expo/modules/spotifysdk/SpotifyErrors.kt +112 -0
- package/build/ExpoSpotifySDKModule.web.d.ts +23 -0
- package/build/ExpoSpotifySDKModule.web.d.ts.map +1 -1
- package/build/ExpoSpotifySDKModule.web.js +74 -5
- package/build/ExpoSpotifySDKModule.web.js.map +1 -1
- package/build/app-remote/error.d.ts +8 -0
- package/build/app-remote/error.d.ts.map +1 -0
- package/build/app-remote/error.js +10 -0
- package/build/app-remote/error.js.map +1 -0
- package/build/app-remote/index.d.ts +79 -0
- package/build/app-remote/index.d.ts.map +1 -0
- package/build/app-remote/index.js +110 -0
- package/build/app-remote/index.js.map +1 -0
- package/build/auth/error.d.ts +8 -0
- package/build/auth/error.d.ts.map +1 -0
- package/build/auth/error.js +10 -0
- package/build/auth/error.js.map +1 -0
- package/build/auth/index.d.ts +124 -0
- package/build/auth/index.d.ts.map +1 -0
- package/build/auth/index.js +162 -0
- package/build/auth/index.js.map +1 -0
- package/build/content/error.d.ts +8 -0
- package/build/content/error.d.ts.map +1 -0
- package/build/content/error.js +10 -0
- package/build/content/error.js.map +1 -0
- package/build/content/index.d.ts +38 -0
- package/build/content/index.d.ts.map +1 -0
- package/build/content/index.js +56 -0
- package/build/content/index.js.map +1 -0
- package/build/error.d.ts +24 -0
- package/build/error.d.ts.map +1 -0
- package/build/error.js +25 -0
- package/build/error.js.map +1 -0
- package/build/hooks/index.d.ts +90 -0
- package/build/hooks/index.d.ts.map +1 -0
- package/build/hooks/index.js +326 -0
- package/build/hooks/index.js.map +1 -0
- package/build/images/error.d.ts +8 -0
- package/build/images/error.d.ts.map +1 -0
- package/build/images/error.js +10 -0
- package/build/images/error.js.map +1 -0
- package/build/images/index.d.ts +36 -0
- package/build/images/index.d.ts.map +1 -0
- package/build/images/index.js +59 -0
- package/build/images/index.js.map +1 -0
- package/build/index.d.ts +44 -56
- package/build/index.d.ts.map +1 -1
- package/build/index.js +49 -141
- package/build/index.js.map +1 -1
- package/build/player/error.d.ts +8 -0
- package/build/player/error.d.ts.map +1 -0
- package/build/player/error.js +10 -0
- package/build/player/error.js.map +1 -0
- package/build/player/index.d.ts +140 -0
- package/build/player/index.d.ts.map +1 -0
- package/build/player/index.js +136 -0
- package/build/player/index.js.map +1 -0
- package/build/uri/index.d.ts +55 -0
- package/build/uri/index.d.ts.map +1 -0
- package/build/uri/index.js +57 -0
- package/build/uri/index.js.map +1 -0
- package/build/user/error.d.ts +8 -0
- package/build/user/error.d.ts.map +1 -0
- package/build/user/error.js +10 -0
- package/build/user/error.js.map +1 -0
- package/build/user/index.d.ts +44 -0
- package/build/user/index.d.ts.map +1 -0
- package/build/user/index.js +97 -0
- package/build/user/index.js.map +1 -0
- package/ios/ExpoSpotifySDKModule.swift +227 -5
- package/ios/SpotifyAppRemoteCoordinator.swift +939 -0
- package/ios/SpotifyAuthCoordinator.swift +53 -13
- package/ios/SpotifyTokenRefreshClient.swift +28 -0
- package/package.json +4 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
1
2
|
import Foundation
|
|
2
3
|
import SpotifyiOS
|
|
3
4
|
|
|
@@ -9,7 +10,7 @@ enum SpotifyError: Error {
|
|
|
9
10
|
case authInProgress
|
|
10
11
|
case userCancelled
|
|
11
12
|
case spotifyNotInstalled
|
|
12
|
-
case underlying(Error)
|
|
13
|
+
case underlying(message: String, cause: Error)
|
|
13
14
|
|
|
14
15
|
var code: String {
|
|
15
16
|
switch self {
|
|
@@ -23,22 +24,54 @@ enum SpotifyError: Error {
|
|
|
23
24
|
|
|
24
25
|
var message: String {
|
|
25
26
|
switch self {
|
|
26
|
-
case .invalidConfiguration(let m):
|
|
27
|
-
case .authInProgress:
|
|
28
|
-
case .userCancelled:
|
|
29
|
-
case .spotifyNotInstalled:
|
|
30
|
-
case .underlying(let
|
|
27
|
+
case .invalidConfiguration(let m): return m
|
|
28
|
+
case .authInProgress: return "Another authentication request is already in progress"
|
|
29
|
+
case .userCancelled: return "Authentication was cancelled by the user"
|
|
30
|
+
case .spotifyNotInstalled: return "The Spotify app is not installed on this device"
|
|
31
|
+
case .underlying(let message, _): return message
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// The original error that caused this failure, if any. Surfaced as the
|
|
36
|
+
/// JS-facing exception's `cause` so debugging / Sentry breadcrumbs keep the
|
|
37
|
+
/// full chain rather than collapsing into "undefined reason".
|
|
38
|
+
var underlyingCause: Error? {
|
|
39
|
+
switch self {
|
|
40
|
+
case .underlying(_, let cause): return cause
|
|
41
|
+
default: return nil
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
/// `Exception` subclass that projects a `SpotifyError`'s `code` and `message`
|
|
47
|
+
/// through `expo-modules-core`'s exception bridge so JS receives the structured
|
|
48
|
+
/// code and a meaningful reason — not the default "undefined reason" placeholder.
|
|
49
|
+
///
|
|
50
|
+
/// Without this wrapper an `AsyncFunction` rejection collapses to
|
|
51
|
+
/// `FunctionCallException` → `cause.reason = "undefined reason"` because the
|
|
52
|
+
/// previously-used `GenericException<String>` does not override `reason`.
|
|
53
|
+
final class SpotifyAuthException: Exception, @unchecked Sendable {
|
|
54
|
+
private let spotifyCode: String
|
|
55
|
+
private let spotifyMessage: String
|
|
56
|
+
|
|
57
|
+
init(_ error: SpotifyError, file: String = #fileID, line: UInt = #line, function: String = #function) {
|
|
58
|
+
self.spotifyCode = error.code
|
|
59
|
+
self.spotifyMessage = error.message
|
|
60
|
+
super.init(file: file, line: line, function: function)
|
|
61
|
+
self.cause = error.underlyingCause
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override var code: String { spotifyCode }
|
|
65
|
+
override var reason: String { spotifyMessage }
|
|
66
|
+
}
|
|
67
|
+
|
|
35
68
|
/// `actor` ensures `pending` is mutated only on the actor's serial executor;
|
|
36
69
|
/// no manual locks needed.
|
|
37
70
|
actor SpotifyAuthCoordinator {
|
|
38
71
|
static let shared: SpotifyAuthCoordinator? = SpotifyAuthCoordinator.create()
|
|
39
72
|
|
|
40
73
|
private let sptConfiguration: SPTConfiguration
|
|
41
|
-
private let sessionManager: SPTSessionManager
|
|
74
|
+
nonisolated(unsafe) private let sessionManager: SPTSessionManager
|
|
42
75
|
private let bridge: SpotifySessionDelegateBridge
|
|
43
76
|
private var pending: CheckedContinuation<SPTSession, Error>?
|
|
44
77
|
|
|
@@ -72,7 +105,15 @@ actor SpotifyAuthCoordinator {
|
|
|
72
105
|
/// Synchronous URL-handler dispatcher used by the AppDelegate / SceneDelegate
|
|
73
106
|
/// subscribers to forward redirects to the SPTSessionManager.
|
|
74
107
|
nonisolated func handleOpenURL(_ url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
|
|
75
|
-
|
|
108
|
+
let openSession: () -> Bool = { [self] in
|
|
109
|
+
MainActor.assumeIsolated {
|
|
110
|
+
sessionManager.application(UIApplication.shared, open: url, options: options)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if Thread.isMainThread {
|
|
114
|
+
return openSession()
|
|
115
|
+
}
|
|
116
|
+
return DispatchQueue.main.sync(execute: openSession)
|
|
76
117
|
}
|
|
77
118
|
|
|
78
119
|
func authenticate(
|
|
@@ -167,11 +208,10 @@ final class SpotifySessionDelegateBridge: NSObject, SPTSessionManagerDelegate {
|
|
|
167
208
|
if description.contains("cancel") {
|
|
168
209
|
return SpotifyError.userCancelled
|
|
169
210
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
))
|
|
211
|
+
// Keep the original NSError as `cause` so the structured underlying-chain
|
|
212
|
+
// is preserved (Sentry, debug breadcrumbs); the rendered string goes to
|
|
213
|
+
// `message` so JS callers get a single human-readable line.
|
|
214
|
+
return SpotifyError.underlying(message: describeError(nsError), cause: nsError)
|
|
175
215
|
}
|
|
176
216
|
|
|
177
217
|
/// Build a diagnostic string from an NSError that includes the domain,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
1
2
|
import Foundation
|
|
2
3
|
|
|
3
4
|
enum SpotifyRefreshError: Error {
|
|
@@ -28,6 +29,33 @@ enum SpotifyRefreshError: Error {
|
|
|
28
29
|
return m
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
/// The original error that caused this failure, if any. Surfaced as the
|
|
34
|
+
/// JS-facing exception's `cause`.
|
|
35
|
+
var underlyingCause: Error? {
|
|
36
|
+
switch self {
|
|
37
|
+
case .network(let err): return err
|
|
38
|
+
default: return nil
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// `Exception` subclass that projects a `SpotifyRefreshError`'s `code` and
|
|
44
|
+
/// `message` through `expo-modules-core` so JS sees the structured taxonomy
|
|
45
|
+
/// instead of "undefined reason". Mirrors `SpotifyAuthException`.
|
|
46
|
+
final class SpotifyRefreshException: Exception, @unchecked Sendable {
|
|
47
|
+
private let spotifyCode: String
|
|
48
|
+
private let spotifyMessage: String
|
|
49
|
+
|
|
50
|
+
init(_ error: SpotifyRefreshError, file: String = #fileID, line: UInt = #line, function: String = #function) {
|
|
51
|
+
self.spotifyCode = error.code
|
|
52
|
+
self.spotifyMessage = error.message
|
|
53
|
+
super.init(file: file, line: line, function: function)
|
|
54
|
+
self.cause = error.underlyingCause
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override var code: String { spotifyCode }
|
|
58
|
+
override var reason: String { spotifyMessage }
|
|
31
59
|
}
|
|
32
60
|
|
|
33
61
|
struct SpotifyRefreshResult {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wwdrew/expo-spotify-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Expo module wrapping the native Spotify iOS (v5) and Android (v4) SDKs for OAuth authentication",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Expo module wrapping the native Spotify iOS (v5) and Android (v4) SDKs for OAuth authentication and App Remote playback control",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -51,5 +51,6 @@
|
|
|
51
51
|
"expo": "*",
|
|
52
52
|
"react": "*",
|
|
53
53
|
"react-native": "*"
|
|
54
|
-
}
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {}
|
|
55
56
|
}
|