@superfan-app/spotify-auth 0.1.43 → 0.1.45
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/ios/SpotifyOAuthView.swift +112 -50
- package/package.json +1 -1
|
@@ -57,54 +57,87 @@ class SpotifyOAuthView: ExpoView {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
private func setupWebView() {
|
|
60
|
+
// Ensure we're on the main thread for UI setup
|
|
61
|
+
guard Thread.isMainThread else {
|
|
62
|
+
DispatchQueue.main.async { [weak self] in
|
|
63
|
+
self?.setupWebView()
|
|
64
|
+
}
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
secureLog("Setting up WebView configuration...")
|
|
60
69
|
// Create a configuration that prevents data persistence
|
|
61
|
-
let config =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
let dataStore = WKWebsiteDataStore.nonPersistent()
|
|
68
|
-
config.websiteDataStore = dataStore
|
|
69
|
-
|
|
70
|
-
webView = WKWebView(frame: .zero, configuration: config)
|
|
71
|
-
webView.navigationDelegate = self
|
|
72
|
-
webView.allowsBackForwardNavigationGestures = true
|
|
73
|
-
webView.customUserAgent = "SpotifyAuth-iOS/1.0" // Custom UA to identify our app
|
|
74
|
-
|
|
75
|
-
// Add loading indicator
|
|
76
|
-
let activityIndicator = UIActivityIndicatorView(style: .medium)
|
|
77
|
-
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
|
78
|
-
activityIndicator.hidesWhenStopped = true
|
|
79
|
-
|
|
80
|
-
addSubview(webView)
|
|
81
|
-
addSubview(activityIndicator)
|
|
82
|
-
|
|
83
|
-
// Setup constraints
|
|
84
|
-
webView.translatesAutoresizingMaskIntoConstraints = false
|
|
85
|
-
NSLayoutConstraint.activate([
|
|
86
|
-
webView.topAnchor.constraint(equalTo: topAnchor),
|
|
87
|
-
webView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
88
|
-
webView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
89
|
-
webView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
70
|
+
let config: WKWebViewConfiguration = {
|
|
71
|
+
let configuration = WKWebViewConfiguration()
|
|
72
|
+
configuration.processPool = WKProcessPool() // Create a new process pool
|
|
73
|
+
let prefs = WKWebpagePreferences()
|
|
74
|
+
prefs.allowsContentJavaScript = true
|
|
75
|
+
configuration.defaultWebpagePreferences = prefs
|
|
90
76
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
77
|
+
// Ensure cookies and data are not persisted
|
|
78
|
+
let dataStore = WKWebsiteDataStore.nonPersistent()
|
|
79
|
+
configuration.websiteDataStore = dataStore
|
|
80
|
+
secureLog("WebView configuration created with non-persistent data store")
|
|
81
|
+
return configuration
|
|
82
|
+
}()
|
|
83
|
+
|
|
84
|
+
// Initialize webview on main thread with error handling
|
|
85
|
+
do {
|
|
86
|
+
webView = WKWebView(frame: .zero, configuration: config)
|
|
87
|
+
guard webView != nil else {
|
|
88
|
+
throw NSError(domain: "SpotifyAuth", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to initialize WebView"])
|
|
89
|
+
}
|
|
90
|
+
secureLog("WebView successfully initialized")
|
|
91
|
+
|
|
92
|
+
webView.navigationDelegate = self
|
|
93
|
+
webView.allowsBackForwardNavigationGestures = true
|
|
94
|
+
webView.customUserAgent = "SpotifyAuth-iOS/1.0" // Custom UA to identify our app
|
|
95
|
+
|
|
96
|
+
// Add loading indicator
|
|
97
|
+
let activityIndicator = UIActivityIndicatorView(style: .medium)
|
|
98
|
+
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
|
99
|
+
activityIndicator.hidesWhenStopped = true
|
|
100
|
+
|
|
101
|
+
addSubview(webView)
|
|
102
|
+
addSubview(activityIndicator)
|
|
103
|
+
|
|
104
|
+
// Setup constraints
|
|
105
|
+
webView.translatesAutoresizingMaskIntoConstraints = false
|
|
106
|
+
NSLayoutConstraint.activate([
|
|
107
|
+
webView.topAnchor.constraint(equalTo: topAnchor),
|
|
108
|
+
webView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
109
|
+
webView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
110
|
+
webView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
111
|
+
|
|
112
|
+
activityIndicator.centerXAnchor.constraint(equalTo: centerXAnchor),
|
|
113
|
+
activityIndicator.centerYAnchor.constraint(equalTo: centerYAnchor)
|
|
114
|
+
])
|
|
115
|
+
|
|
116
|
+
// Setup modern KVO observation
|
|
117
|
+
observerToken = webView.observe(\.isLoading, options: [.new]) { [weak self] _, _ in
|
|
118
|
+
if let activityIndicator = self?.subviews.first(where: { $0 is UIActivityIndicatorView }) as? UIActivityIndicatorView {
|
|
119
|
+
if self?.webView.isLoading == true {
|
|
120
|
+
activityIndicator.startAnimating()
|
|
121
|
+
} else {
|
|
122
|
+
activityIndicator.stopAnimating()
|
|
123
|
+
}
|
|
102
124
|
}
|
|
103
125
|
}
|
|
126
|
+
} catch {
|
|
127
|
+
secureLog("Failed to setup WebView: \(error.localizedDescription)")
|
|
128
|
+
delegate?.oauthView(self, didFailWithError: SpotifyOAuthError.authorizationError("Failed to initialize web view"))
|
|
104
129
|
}
|
|
105
130
|
}
|
|
106
131
|
|
|
107
132
|
func startOAuthFlow(clientId: String, redirectUri: String, scopes: [String], showDialog: Bool = false, campaign: String? = nil) {
|
|
133
|
+
// Ensure we're on the main thread - WebView setup must be done on the main thread
|
|
134
|
+
if !Thread.isMainThread {
|
|
135
|
+
DispatchQueue.main.async { [weak self] in
|
|
136
|
+
self?.startOAuthFlow(clientId: clientId, redirectUri: redirectUri, scopes: scopes, showDialog: showDialog, campaign: campaign)
|
|
137
|
+
}
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
108
141
|
guard !isAuthenticating else { return }
|
|
109
142
|
isAuthenticating = true
|
|
110
143
|
|
|
@@ -120,18 +153,24 @@ class SpotifyOAuthView: ExpoView {
|
|
|
120
153
|
startAuthTimeout()
|
|
121
154
|
|
|
122
155
|
// Clear any existing cookies/data to ensure a fresh login
|
|
156
|
+
// Wait for completion before initiating the auth request
|
|
123
157
|
WKWebsiteDataStore.default().removeData(
|
|
124
158
|
ofTypes: [WKWebsiteDataTypeCookies, WKWebsiteDataTypeSessionStorage],
|
|
125
159
|
modifiedSince: Date(timeIntervalSince1970: 0)
|
|
126
|
-
) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
160
|
+
) { [weak self] in
|
|
161
|
+
// Ensure we're still in a valid state after the async operation
|
|
162
|
+
guard let self = self, self.isAuthenticating else { return }
|
|
163
|
+
|
|
164
|
+
DispatchQueue.main.async {
|
|
165
|
+
self.initiateAuthRequest(
|
|
166
|
+
clientId: clientId,
|
|
167
|
+
redirectUri: redirectUri,
|
|
168
|
+
scopes: scopes,
|
|
169
|
+
showDialog: showDialog,
|
|
170
|
+
campaign: campaign
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
135
174
|
}
|
|
136
175
|
|
|
137
176
|
private func startAuthTimeout() {
|
|
@@ -167,6 +206,14 @@ class SpotifyOAuthView: ExpoView {
|
|
|
167
206
|
return
|
|
168
207
|
}
|
|
169
208
|
|
|
209
|
+
// Verify webView is properly initialized
|
|
210
|
+
guard let webView = self.webView else {
|
|
211
|
+
secureLog("Error: WebView not initialized when attempting to load auth request")
|
|
212
|
+
isAuthenticating = false
|
|
213
|
+
delegate?.oauthView(self, didFailWithError: SpotifyOAuthError.authorizationError("WebView not initialized"))
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
170
217
|
var queryItems = [
|
|
171
218
|
URLQueryItem(name: "client_id", value: clientId),
|
|
172
219
|
URLQueryItem(name: "response_type", value: "code"),
|
|
@@ -189,7 +236,22 @@ class SpotifyOAuthView: ExpoView {
|
|
|
189
236
|
}
|
|
190
237
|
|
|
191
238
|
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
|
|
192
|
-
|
|
239
|
+
secureLog("Initiating auth request to URL: \(url.absoluteString)")
|
|
240
|
+
|
|
241
|
+
DispatchQueue.main.async {
|
|
242
|
+
guard Thread.isMainThread else {
|
|
243
|
+
assertionFailure("WebView load not on main thread despite DispatchQueue.main.async")
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if webView.isLoading {
|
|
248
|
+
secureLog("Warning: WebView is already loading content, stopping previous load")
|
|
249
|
+
webView.stopLoading()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
webView.load(request)
|
|
253
|
+
self.secureLog("Auth request load initiated")
|
|
254
|
+
}
|
|
193
255
|
}
|
|
194
256
|
|
|
195
257
|
deinit {
|