Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions SimpleFlickrBrowser/Extensions/String.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Created by Maxim Berezhnoy on 26/11/2020.
//
// Copyright (c) 2020 rencevio. All rights reserved.

import Foundation.NSDate

extension String {
func toInt() -> Int? {
Int(self)
}

func toDate(formatter: DateFormatter) -> Date? {
formatter.date(from: self)
}
}
288 changes: 186 additions & 102 deletions SimpleFlickrBrowser/SimpleFlickrBrowser.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions SimpleFlickrBrowser/SimpleFlickrBrowser/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let dependencies = RootDependencies()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let viewController = dependencies.createBrowserViewController()
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let viewController = dependencies.createFeedViewController()

let navigationController = UINavigationController(rootViewController: viewController)

Expand All @@ -21,4 +21,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

return true
}

func application(_: UIApplication, handleEventsForBackgroundURLSession _: String, completionHandler: @escaping () -> Void) {
BackgroundURLSession.shared.set(onBackgroundEventsHandledCallback: completionHandler)
}
}
13 changes: 12 additions & 1 deletion SimpleFlickrBrowser/SimpleFlickrBrowser/Models/Photo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@
//
// Copyright (c) 2020 rencevio. All rights reserved.

import struct Foundation.Data
import struct Foundation.Date
import struct Foundation.URL

struct Photo {
struct Metadata {
let views: Int
let tags: [String]
let ownerName: String
let dateTaken: Date
}

typealias ID = String

let id: ID
let imageURL: URL
let imageData: Data
let fullSizeImageURL: URL
let metadata: Metadata
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// Created by Maxim Berezhnoy on 29/11/2020.
//
// Copyright (c) 2020 rencevio. All rights reserved.

import Foundation

extension URLSession {
static let sharedBackground = BackgroundURLSession.shared
}

final class BackgroundURLSession {
static let shared = BackgroundURLSession()

private var onBackgroundEventsHandled: (() -> Void)?

private let operatingQueue = DispatchQueue(label: "\(BackgroundURLSession.self)OperatingQueue")

private let identifier = "\(BackgroundURLSession.self)Identifier"

private var runningTasks = [URLSessionTask: Completion]()

private lazy var session: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: identifier)

return URLSession(configuration: configuration, delegate: sessionDelegate, delegateQueue: nil)
}()

private lazy var sessionDelegate = {
BackgroundURLSessionDelegate(onTaskFinished: onTaskFinished(), onAllTasksFinished: onAllTasksFinished())
}()

private init() {}

func set(onBackgroundEventsHandledCallback: @escaping () -> Void) {
DispatchQueue.main.async { [weak self] in
self?.onBackgroundEventsHandled = onBackgroundEventsHandledCallback
}
}

private func onTaskFinished() -> (URLSessionTask, Data?, Error?) -> Void {
{ [weak self] task, data, error in
self?.operatingQueue.async { [weak self] in
guard let self = self, let taskCallback = self.runningTasks[task] else {
return
}

self.runningTasks.removeValue(forKey: task)

taskCallback(data, error)
}
}
}

private func onAllTasksFinished() -> () -> Void {
{
DispatchQueue.main.async { [weak self] in
self?.onBackgroundEventsHandled?()
self?.onBackgroundEventsHandled = nil
}
}
}
}

// MARK: - NetworkSession

extension BackgroundURLSession: NetworkSession {
func getData(from url: URL, _ completion: @escaping Completion) {
let task = session.downloadTask(with: url)

operatingQueue.async { [weak self] in
self?.runningTasks[task] = completion
}

task.resume()
}
}

private final class BackgroundURLSessionDelegate: NSObject {
private let onTaskFinished: (URLSessionTask, Data?, Error?) -> Void
private let onAllTasksFinished: () -> Void

init(onTaskFinished: @escaping (URLSessionTask, Data?, Error?) -> Void, onAllTasksFinished: @escaping () -> Void) {
self.onTaskFinished = onTaskFinished
self.onAllTasksFinished = onAllTasksFinished
}
}

// MARK: - URLSessionDelegate

extension BackgroundURLSessionDelegate: URLSessionDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession _: URLSession) {
onAllTasksFinished()
}
}

// MARK: - URLSessionTaskDelegate

extension BackgroundURLSessionDelegate: URLSessionTaskDelegate {
func urlSession(_: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
onTaskFinished(task, nil, error)
}
}

// MARK: - URLSessionDownloadDelegate

extension BackgroundURLSessionDelegate: URLSessionDownloadDelegate {
func urlSession(_: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
do {
let data = try Data(contentsOf: location)

onTaskFinished(downloadTask, data, nil)
} catch {
onTaskFinished(downloadTask, nil, error)
}
}
}
11 changes: 6 additions & 5 deletions SimpleFlickrBrowser/SimpleFlickrBrowser/Network/HttpClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Foundation

protocol HttpCommunicator {
typealias Completion = (Result<Data, Http.RequestError>) -> Void
func get(url: URL, completion: @escaping Completion)
func get(url: URL, background: Bool, completion: @escaping Completion)
}

enum Http {
Expand All @@ -18,9 +18,12 @@ enum Http {

final class Client: HttpCommunicator {
private let urlSession = URLSession.shared
private let backgroundUrlSession = URLSession.sharedBackground

func get(url: URL, completion: @escaping Completion) {
let task = urlSession.dataTask(with: url) { data, _, error in
func get(url: URL, background: Bool, completion: @escaping Completion) {
let session: NetworkSession = background ? backgroundUrlSession : urlSession

session.getData(from: url) { data, error in
guard let data = data else {
if let error = error {
completion(.failure(.httpError(error)))
Expand All @@ -33,8 +36,6 @@ enum Http {

completion(.success(data))
}

task.resume()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Created by Maxim Berezhnoy on 29/11/2020.
//
// Copyright (c) 2020 rencevio. All rights reserved.

import Foundation

protocol NetworkSession {
typealias Completion = (Data?, Error?) -> Void

func getData(from url: URL, _ completion: @escaping Completion)
}

extension URLSession: NetworkSession {
func getData(from url: URL, _ completion: @escaping Completion) {
dataTask(with: url) { data, _, error in
completion(data, error)
}.resume()
}
}
2 changes: 0 additions & 2 deletions SimpleFlickrBrowser/SimpleFlickrBrowser/Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ final class RootDependencies {
photoDataProvider = photoDataProviderFactory.createPhotoProvider()

let photoCollectionFetcherFactory = FlickrCollectionFetcherFactory()
photoCollectionFetcher = photoCollectionFetcherFactory.createCollectionFetcher()
photoCollectionFetcher = photoCollectionFetcherFactory.createCollectionFetcher(photoDataProvider: photoDataProvider)
}

func createBrowserViewController() -> BrowserViewController {
let browserFactory = BrowserFactory()
func createFeedViewController() -> FeedViewController {
let feedCellFactory = FeedCellFactory()
let feedFactory = FeedFactory(feedCellFactory: feedCellFactory)

return browserFactory.createViewController(
return feedFactory.createViewController(
photoDataProvider: photoDataProvider,
photoCollectionFetcher: photoCollectionFetcher
)
Expand Down

This file was deleted.

This file was deleted.

Loading