Hi I have a question for developer in here, I was reading an iOS programming book from big nerd ranch. I was interested how to structure and technique to create an app. and I was trying to implement it, and it was a dependency inversion and the code is like this
this is the code in app delegate from the book
let rootViewController = window!.rootViewController as! UINavigationController
let photosViewController = rootViewController.topViewController as! PhotoViewController
photosViewController.store = PhotoStore()
and this is the photoViewController class
class PhotoInfoViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
var photo: Photo! {
didSet {
navigationItem.title = photo.title
}
}
var store: PhotoStore!
override func viewDidLoad() {
super.viewDidLoad()
store.fetchImage(for: photo) { (result) in
switch result {
case let .success(image):
self.imageView.image = image
case let .failure(error):
print("Error fetching image for photo: \(error)")
}
}
}
}
and this is the photo store class that use for dependency inversion code
enum ImageResult {
case success(UIImage)
case failure(Error)
}
enum PhotoError: Error {
case imageCreationError
}
enum PhotoResult {
case success([Photo])
case failure(Error)
}
class PhotoStore {
private let session: URLSession = {
return URLSession(configuration: .default)
}()
let imageStore = ImageStore()
func fetchInterestingPhoto(completion: @escaping (PhotoResult) -> Void) {
let url = FlickerAPI.interestingPhotoURL
let request = URLRequest(url: url)
let task = session.dataTask(with: request) { (data, response, error) in
let result = self.processPhotosRequest(data: data, error: error)
OperationQueue.main.addOperation {
completion(result)
}
}
task.resume()
}
private func processPhotosRequest(data: Data?, error: Error?) -> PhotoResult {
guard let jsonData = data else { return .failure(error!) }
return FlickerAPI.photos(fromJSON: jsonData)
}
func fetchImage(for photo: Photo, completion: @escaping (ImageResult) -> Void) {
let photoKey = photo.photoID
if let image = imageStore.image(forKey: photoKey) {
OperationQueue.main.addOperation {
completion(.success(image))
}
return
}
let photoURL = photo.remoteURL
let request = URLRequest(url: photoURL)
let task = session.dataTask(with: request) { (data, response, error) in
let result = self.processImageRequest(data: data, error: error)
if case let .success(image) = result {
self.imageStore.setImage(image, forKey: photoKey)
}
OperationQueue.main.addOperation {
completion(result)
}
}
task.resume()
}
private func processImageRequest(data: Data?, error: Error?) -> ImageResult {
guard
let imageData = data,
let image = UIImage(data: imageData)
else {
// Couldn't create an image
if data == nil {
return .failure(error!)
} else {
return .failure(PhotoError.imageCreationError)
}
}
return .success(image)
}
}
What I'm asking is, if I have a tab bar controller and have lets say 2 view controller one of them is fetching a news data, and other controller fetching a weather data. how I can implemented this dependency inversion since the root view controller in app delegate will be tab bar controller that's why I confuse to implement it? since the book only use a single view controller.
Bear in mind this is a hack and in my opinion, it shouldn't be done like this as the dependencies should be declared in the init
. But because you're probably creating the TabBar from the Storyboard. You only have this option which is getting the TabBar from the window. and the TabBarController
has an array of the viewControllers that form the TabBar. You simply need to iterate over and try to cast them as a PhotoViewController
. If it is, then you set the store:
let rootViewController = window!.rootViewController as! UITabBarController
for viewController in rootViewController.viewControllers {
switch viewController {
case let photoViewController as PhotoViewController:
photoViewController.store = PhotoStore()
default:
break
}
}