Skip to content

Commit 209f400

Browse files
authored
Merge pull request #180 from analogcode/dev
Add startup LoaderController to prefetch stations
2 parents 4d052f9 + 2dede90 commit 209f400

File tree

5 files changed

+187
-6
lines changed

5 files changed

+187
-6
lines changed

SwiftRadio.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
CE6ECD03292F358C008B3C16 /* UIViewController+Email.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6ECD02292F358C008B3C16 /* UIViewController+Email.swift */; };
6868
CE6ECD04292F358C008B3C16 /* UIViewController+Email.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6ECD02292F358C008B3C16 /* UIViewController+Email.swift */; };
6969
CE963ECC29135A6F004F299E /* StationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE963ECB29135A6F004F299E /* StationsManager.swift */; };
70+
CE9EE8DE293BB41300F62041 /* BaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9EE8DD293BB41300F62041 /* BaseController.swift */; };
71+
CE9EE8DF293BB41300F62041 /* BaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9EE8DD293BB41300F62041 /* BaseController.swift */; };
72+
CE9EE8E1293C048A00F62041 /* LoaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9EE8E0293C048A00F62041 /* LoaderController.swift */; };
73+
CE9EE8E2293C048A00F62041 /* LoaderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9EE8E0293C048A00F62041 /* LoaderController.swift */; };
7074
CEA82F4A2921F260009E9FA0 /* Info-CarPlay.plist in Resources */ = {isa = PBXBuildFile; fileRef = CEA82F492921F260009E9FA0 /* Info-CarPlay.plist */; };
7175
CED6353B293081ED002B216F /* Handoffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED6353A293081ED002B216F /* Handoffable.swift */; };
7276
CED6353C293081ED002B216F /* Handoffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED6353A293081ED002B216F /* Handoffable.swift */; };
@@ -119,6 +123,8 @@
119123
CE6ECCFF292F215F008B3C16 /* Storyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storyboard.swift; sourceTree = "<group>"; };
120124
CE6ECD02292F358C008B3C16 /* UIViewController+Email.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Email.swift"; sourceTree = "<group>"; };
121125
CE963ECB29135A6F004F299E /* StationsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationsManager.swift; sourceTree = "<group>"; };
126+
CE9EE8DD293BB41300F62041 /* BaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseController.swift; sourceTree = "<group>"; };
127+
CE9EE8E0293C048A00F62041 /* LoaderController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderController.swift; sourceTree = "<group>"; };
122128
CEA82F492921F260009E9FA0 /* Info-CarPlay.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-CarPlay.plist"; sourceTree = "<group>"; };
123129
CED6353A293081ED002B216F /* Handoffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Handoffable.swift; sourceTree = "<group>"; };
124130
CEDABBEA291217AF00C0367F /* UIImageView+Cache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Cache.swift"; sourceTree = "<group>"; };
@@ -260,6 +266,8 @@
260266
CE8062F6291EF5B3008BD097 /* ViewControllers */ = {
261267
isa = PBXGroup;
262268
children = (
269+
CE9EE8DD293BB41300F62041 /* BaseController.swift */,
270+
CE9EE8E0293C048A00F62041 /* LoaderController.swift */,
263271
94452E541AD7086800BFE7A5 /* AboutViewController.swift */,
264272
94D1D0A41AD6D6230022CA11 /* InfoDetailViewController.swift */,
265273
94452E4E1AD6F24700BFE7A5 /* PopUpMenuViewController.swift */,
@@ -453,6 +461,7 @@
453461
files = (
454462
94D260911B45D20000DE671C /* Config.swift in Sources */,
455463
94D30EA71AD07A880024FE96 /* StationTableViewCell.swift in Sources */,
464+
CE9EE8DE293BB41300F62041 /* BaseController.swift in Sources */,
456465
53113F39230C720900462C0E /* ShareActivity.swift in Sources */,
457466
9409E1401ABF78B000312E2B /* NowPlayingViewController.swift in Sources */,
458467
CE963ECC29135A6F004F299E /* StationsManager.swift in Sources */,
@@ -474,6 +483,7 @@
474483
CEDABBED29121BBA00C0367F /* UIImage+Cache.swift in Sources */,
475484
942A3F371AE43DF80011396E /* StationsViewController.swift in Sources */,
476485
94AC70AE1AD05C6200652982 /* RadioStation.swift in Sources */,
486+
CE9EE8E1293C048A00F62041 /* LoaderController.swift in Sources */,
477487
CEDABBEB291217AF00C0367F /* UIImageView+Cache.swift in Sources */,
478488
);
479489
runOnlyForDeploymentPostprocessing = 0;
@@ -484,6 +494,7 @@
484494
files = (
485495
CE6A3E31291F376D0058C82A /* Config.swift in Sources */,
486496
CE6A3E32291F376D0058C82A /* StationTableViewCell.swift in Sources */,
497+
CE9EE8DF293BB41300F62041 /* BaseController.swift in Sources */,
487498
CE6A3E33291F376D0058C82A /* ShareActivity.swift in Sources */,
488499
CE6A3E34291F376D0058C82A /* NowPlayingViewController.swift in Sources */,
489500
CE6A3E35291F376D0058C82A /* StationsManager.swift in Sources */,
@@ -505,6 +516,7 @@
505516
CE6A3E3E291F376D0058C82A /* UIImage+Cache.swift in Sources */,
506517
CE6A3E3F291F376D0058C82A /* StationsViewController.swift in Sources */,
507518
CE6A3E40291F376D0058C82A /* RadioStation.swift in Sources */,
519+
CE9EE8E2293C048A00F62041 /* LoaderController.swift in Sources */,
508520
CE6A3E41291F376D0058C82A /* UIImageView+Cache.swift in Sources */,
509521
);
510522
runOnlyForDeploymentPostprocessing = 0;

SwiftRadio/Coordinators/MainCoordinator.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ class MainCoordinator: NavigationCoordinator {
1414
let navigationController: UINavigationController
1515

1616
func start() {
17-
let stationsVC = Storyboard.viewController as StationsViewController
18-
stationsVC.delegate = self
19-
navigationController.setViewControllers([stationsVC], animated: false)
17+
let loaderVC = LoaderController()
18+
loaderVC.delegate = self
19+
navigationController.setViewControllers([loaderVC], animated: false)
2020
}
2121

2222
init(navigationController: UINavigationController) {
@@ -51,6 +51,16 @@ class MainCoordinator: NavigationCoordinator {
5151
}
5252
}
5353

54+
// MARK: - LoaderControllerDelegate
55+
56+
extension MainCoordinator: LoaderControllerDelegate {
57+
func didFinishLoading(_ controller: LoaderController, stations: [RadioStation]) {
58+
let stationsVC = Storyboard.viewController as StationsViewController
59+
stationsVC.delegate = self
60+
navigationController.setViewControllers([stationsVC], animated: false)
61+
}
62+
}
63+
5464
// MARK: - StationsViewControllerDelegate
5565

5666
extension MainCoordinator: StationsViewControllerDelegate {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// BaseController.swift
3+
// SwiftRadio
4+
//
5+
// Created by Fethi El Hassasna on 2022-12-03.
6+
// Copyright © 2022 matthewfecher.com. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
class BaseController: UIViewController {
12+
13+
let backgroundImageView: UIImageView = {
14+
let image = UIImage(named: "background")
15+
let imageView = UIImageView(image: image)
16+
imageView.contentMode = .scaleAspectFill
17+
imageView.translatesAutoresizingMaskIntoConstraints = false
18+
return imageView
19+
}()
20+
21+
override func loadView() {
22+
super.loadView()
23+
setupViews()
24+
}
25+
26+
func setupViews() {
27+
view.addSubview(backgroundImageView)
28+
29+
NSLayoutConstraint.activate([
30+
backgroundImageView.topAnchor.constraint(equalTo: view.topAnchor),
31+
backgroundImageView.rightAnchor.constraint(equalTo: view.rightAnchor),
32+
backgroundImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
33+
backgroundImageView.leftAnchor.constraint(equalTo: view.leftAnchor)
34+
])
35+
}
36+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//
2+
// LoaderController.swift
3+
// SwiftRadio
4+
//
5+
// Created by Fethi El Hassasna on 2022-12-03.
6+
// Copyright © 2022 matthewfecher.com. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
protocol LoaderControllerDelegate: AnyObject {
12+
func didFinishLoading(_ controller: LoaderController, stations: [RadioStation])
13+
}
14+
15+
class LoaderController: BaseController {
16+
17+
weak var delegate: LoaderControllerDelegate?
18+
19+
private let manager = StationsManager.shared
20+
21+
private let activityIndicatorView: UIActivityIndicatorView = {
22+
let view = UIActivityIndicatorView(style: .white)
23+
view.translatesAutoresizingMaskIntoConstraints = false
24+
return view
25+
}()
26+
27+
private let errorTitleLabel: UILabel = {
28+
let label = UILabel()
29+
label.textAlignment = .center
30+
label.font = UIFont.preferredFont(forTextStyle: .headline)
31+
label.numberOfLines = 0
32+
label.text = "Something went wrong!"
33+
return label
34+
}()
35+
36+
private let errorMessageLabel: UILabel = {
37+
let label = UILabel()
38+
label.textAlignment = .center
39+
label.numberOfLines = 0
40+
label.font = UIFont.preferredFont(forTextStyle: .footnote)
41+
return label
42+
}()
43+
44+
private let stackView: UIStackView = {
45+
let view = UIStackView()
46+
view.translatesAutoresizingMaskIntoConstraints = false
47+
view.axis = .vertical
48+
view.alignment = .center
49+
view.spacing = 16
50+
return view
51+
}()
52+
53+
override func viewDidAppear(_ animated: Bool) {
54+
super.viewDidAppear(animated)
55+
fetchStations()
56+
}
57+
58+
private func handle(_ error: Error) {
59+
stackView.isHidden = false
60+
errorMessageLabel.text = error.localizedDescription
61+
}
62+
63+
private func fetchStations() {
64+
stackView.isHidden = true
65+
activityIndicatorView.startAnimating()
66+
67+
manager.fetch { [weak self] result in
68+
guard let self = self else { return }
69+
70+
self.activityIndicatorView.stopAnimating()
71+
72+
switch result {
73+
case .success(let stations):
74+
self.delegate?.didFinishLoading(self, stations: stations)
75+
case .failure(let error):
76+
self.handle(error)
77+
}
78+
}
79+
}
80+
81+
override func setupViews() {
82+
super.setupViews()
83+
84+
// Logo Image
85+
let logoImage = UIImage(named: "logo")
86+
let logoImageView = UIImageView(image: logoImage)
87+
logoImageView.translatesAutoresizingMaskIntoConstraints = false
88+
89+
view.addSubview(logoImageView)
90+
91+
NSLayoutConstraint.activate([
92+
logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
93+
logoImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
94+
])
95+
96+
// Activity Indicator
97+
view.addSubview(activityIndicatorView)
98+
99+
NSLayoutConstraint.activate([
100+
activityIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
101+
activityIndicatorView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 32)
102+
])
103+
104+
// Retry button
105+
let retryButton = UIButton(type: .system)
106+
retryButton.setTitle("Try again", for: .normal)
107+
retryButton.addTarget(self, action: #selector(handleRetry), for: .touchUpInside)
108+
109+
// Stack view
110+
stackView.addArrangedSubview(errorTitleLabel)
111+
stackView.addArrangedSubview(errorMessageLabel)
112+
stackView.addArrangedSubview(retryButton)
113+
114+
view.addSubview(stackView)
115+
116+
NSLayoutConstraint.activate([
117+
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
118+
stackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 32),
119+
stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7)
120+
])
121+
}
122+
123+
@objc private func handleRetry() {
124+
fetchStations()
125+
}
126+
}

SwiftRadio/ViewControllers/StationsViewController.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ class StationsViewController: UIViewController, Handoffable {
6262
player.addObserver(self)
6363
manager.addObserver(self)
6464

65-
// Load Data
66-
manager.fetch()
67-
6865
// Setup TableView
6966
tableView.backgroundColor = .clear
7067
tableView.backgroundView = nil

0 commit comments

Comments
 (0)