Skip to content

Commit faaef90

Browse files
App Clip: Add loading and error states (#2745)
2 parents 27e676d + 76a520e commit faaef90

File tree

2 files changed

+52
-12
lines changed

2 files changed

+52
-12
lines changed

Pocket Casts App Clip/NowPlayingView.swift

+50-10
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,41 @@ import StoreKit
55
import PocketCastsUtils
66

77
struct NowPlayingView: View {
8+
9+
enum ScreenState: Int {
10+
case loading
11+
case ready
12+
case failed
13+
}
14+
815
@State var presentAppStoreOverlay: Bool = false
16+
@State var state: ScreenState = .loading
917

1018
var body: some View {
1119
VStack {
12-
NowPlayingPlayerItemViewControllerRepresentable()
13-
.onAppear {
14-
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
15-
presentAppStoreOverlay = true
20+
Spacer()
21+
HStack { Spacer() }
22+
switch state {
23+
case .loading:
24+
ProgressView()
25+
.progressViewStyle(.circular)
26+
.tint(UIColor.label.color)
27+
case .ready:
28+
NowPlayingPlayerItemViewControllerRepresentable()
29+
.onAppear {
30+
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
31+
presentAppStoreOverlay = true
32+
}
1633
}
17-
}
34+
case .failed:
35+
EmptyView()
36+
}
37+
Spacer()
1838
}
39+
.background(state != .ready ? UIColor.systemBackground.color : PlayerColorHelper.playerBackgroundColor01().color)
1940
.appStoreOverlay(isPresented: $presentAppStoreOverlay, configuration: {
2041
SKOverlay.AppClipConfiguration(position: .bottom)
2142
})
22-
.background(Color(uiColor: PlayerColorHelper.playerBackgroundColor01()))
2343
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
2444
handle(userActivity: userActivity)
2545
}
@@ -32,7 +52,10 @@ struct NowPlayingView: View {
3252
let path = components.path,
3353
path != "/get",
3454
path != "/get/"
35-
else { return }
55+
else {
56+
showErrorMessage(userActivity: userActivity)
57+
return
58+
}
3659

3760
// NOTE: This doesn't handle the redeem URL. See `AppDelegate.handleContinue(_ userActivity: NSUserActivity)` for this logic
3861

@@ -46,46 +69,63 @@ struct NowPlayingView: View {
4669
PodcastManager.shared.importSharedItemFromUrl(importPath) { shareItem in
4770
guard let shareItem else {
4871
FileLog.shared.addMessage("App Clip: Missing Share Item")
72+
showErrorMessage(userActivity: userActivity)
4973
return
5074
}
5175

5276
guard let episodeUUID = shareItem.episodeHeader?.uuid else {
5377
FileLog.shared.addMessage("App Clip: No episode found in share item")
78+
showErrorMessage(userActivity: userActivity)
5479
return
5580
}
5681

5782
guard let podcastUUID = shareItem.podcastHeader?.uuid else {
5883
FileLog.shared.addMessage("App Clip: No podcast found in share item")
84+
showErrorMessage(userActivity: userActivity)
5985
return
6086
}
6187

6288

6389
loadEpisode(episodeUuid: episodeUUID, podcastUuid: podcastUUID) {
6490
guard let episode = DataManager.sharedManager.findEpisode(uuid: episodeUUID) else {
6591
FileLog.shared.addMessage("App Clip: Could not find Episode")
92+
showErrorMessage(userActivity: userActivity)
6693
return
6794
}
6895

6996
FileLog.shared.addMessage("App Clip: Loaded episode: \(episode.title ?? "unknown")")
70-
97+
state = .ready
7198
PlaybackManager.shared.load(episode: episode, autoPlay: true, overrideUpNext: false)
7299
Analytics.track(.playbackPlay, source: AnalyticsSource.handleUserActivity, properties: ["url": incomingURL.absoluteString, "podcast": podcastUUID, "episode": episode.uuid])
73100
}
74101
}
75102
}
76103

104+
private func showErrorMessage(userActivity: NSUserActivity) {
105+
userActivity.invalidate()
106+
state = .failed
107+
DispatchQueue.main.async {
108+
let rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController
109+
SJUIUtils.showAlert(title: L10n.podcastShareErrorTitle, message: L10n.podcastShareErrorMsg, from: rootViewController)
110+
}
111+
}
112+
77113
private func loadEpisode(episodeUuid: String, podcastUuid: String, timestamp: TimeInterval? = nil, completion: @escaping () -> Void) {
78114
if let podcast = DataManager.sharedManager.findPodcast(uuid: podcastUuid, includeUnsubscribed: true) {
79115
ServerPodcastManager.shared.updatePodcastIfRequired(podcast: podcast) { _ in
80-
completion()
116+
DispatchQueue.main.async {
117+
completion()
118+
}
81119
}
82120

83121
return
84122
}
85123

86124
ServerPodcastManager.shared.addFromUuid(podcastUuid: podcastUuid, subscribe: false, completion: { success in
87125
if success, let _ = DataManager.sharedManager.findPodcast(uuid: podcastUuid, includeUnsubscribed: true) {
88-
completion()
126+
DispatchQueue.main.async {
127+
completion()
128+
}
89129
} else {
90130
DispatchQueue.main.async {
91131
let rootViewController = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController

podcasts/PlayerColorHelper.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ struct PlayerColorHelper {
1717
return ThemeColor.playerBackground02(podcastColor: podcastBackgroundColor, for: theme)
1818
}
1919

20-
static func playerHighlightColor01(for theme: Theme.ThemeType,
20+
static func playerHighlightColor01(for theme: Theme.ThemeType = Theme.sharedTheme.activeTheme,
2121
episode: BaseEpisode? = PlaybackManager.shared.currentEpisode()) -> UIColor {
2222
guard let podcastColor = tint(for: episode, with: theme) else { return UIColor.white }
2323

2424
return ThemeColor.playerHighlight01(podcastColor: podcastColor)
2525
}
2626

27-
static func playerHighlightColor02(for theme: Theme.ThemeType,
27+
static func playerHighlightColor02(for theme: Theme.ThemeType = Theme.sharedTheme.activeTheme,
2828
episode: BaseEpisode? = PlaybackManager.shared.currentEpisode()) -> UIColor {
2929
guard let podcastColor = tint(for: episode, with: theme) else { return UIColor.white }
3030

0 commit comments

Comments
 (0)