Skip to content

Commit 14dc1e8

Browse files
author
burak.uzunboy
committed
Response image addition
1 parent 629e792 commit 14dc1e8

3 files changed

Lines changed: 174 additions & 3 deletions

File tree

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ let package = Package(
2222
.target(
2323
name: "UsefulNetworkLayer",
2424
dependencies: ["CoreUsefulSDK"]),
25-
// .testTarget(
26-
// name: "iOSUsefulNetworkLayerTests",
27-
// dependencies: ["UsefulNetworkLayer", "CoreUsefulSDK"]),
25+
.testTarget(
26+
name: "iOSUsefulNetworkLayerTests",
27+
dependencies: ["UsefulNetworkLayer", "CoreUsefulSDK"]),
2828
]
2929
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// UIImage+ResponseImage.swift
3+
// MyPet
4+
//
5+
// Created by Burak Uzunboy on 23.05.20.
6+
// Copyright © 2020 BUZ. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
/**
12+
Extends UIImage with Network Layer compatible `ResponseImage` class. With that, image class can directly respond to URLs.
13+
*/
14+
extension UIImage {
15+
16+
/// Returns immediately if image is available on the cache, otherwise requests from Network Layer and calls completion block.
17+
@discardableResult
18+
class func fromURL(_ url: URL, completion: @escaping (_ image: UIImage?)->()) -> UIImage? {
19+
let api = APIConfiguration(url: url, responseBodyObject: ResponseImage.self, cachingTime: NetworkLayer.CachingTime(seconds: 60*60*24))
20+
21+
api.request { (result) in
22+
switch result {
23+
case .error(_):
24+
completion(nil)
25+
case .success(let response):
26+
completion(response.responseBody.image)
27+
}
28+
}
29+
30+
if let img = UIImage.fromCache(url) {
31+
return img
32+
}
33+
return nil
34+
}
35+
36+
/// Returns immediately the image if it is available on the cache.
37+
class func fromCache(_ url: URL) -> UIImage? {
38+
let api = APIConfiguration(url: url, responseBodyObject: ResponseImage.self)
39+
guard let response = NetworkLayer.cache?.cachedResponse(for: URLRequest(url: api.requestURL)) else { return nil }
40+
return UIImage(data: response.data)
41+
}
42+
43+
/// Downloads image and saves to the cache for future uses.
44+
class func download(_ url: URL) {
45+
let api = APIConfiguration(url: url, responseBodyObject: ResponseImage.self, priority: .veryLow, isMainOperation: false)
46+
api.request { (_) in }
47+
}
48+
49+
}
50+
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//
2+
// UIImageView+ImageURL.swift
3+
// MyPet
4+
//
5+
// Created by Burak Uzunboy on 23.05.20.
6+
// Copyright © 2020 BUZ. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension UIImageView {
12+
13+
/// Creates and sets Image to the ImageView from given remote URL.
14+
///
15+
/// During the loading process, this method is also capable to add activity indicator.
16+
/// Change tag value if other method will be called also to change image of the imageview to override again.
17+
public func imageFromUrl(urlString: String?, fallback: UIImage?, errorCompletion: ((_ error: NSError)->())? = nil){
18+
guard let urlStr = urlString, let url = URL(string: urlStr) else {
19+
self.image = fallback
20+
return
21+
}
22+
23+
return self.imageFromUrl(url: url, fallback: fallback, errorCompletion: errorCompletion, completion: nil)
24+
}
25+
26+
/**
27+
Sets image to the image view.
28+
- parameter url: URL of the image source
29+
- parameter completion: Returns the image that set to imageview even it fallbacks to error. If `fallback` is set, at worst it will return the fallback.
30+
- parameter errorCompletion: This block will be called if any error happens
31+
*/
32+
public func imageFromUrl(url: URL?, fallback: UIImage?,
33+
errorCompletion: ((_ error: NSError)->())? = nil,
34+
completion: ((_ image: UIImage?)->())?) {
35+
guard let url = url else {
36+
self.image = fallback
37+
completion?(image)
38+
return
39+
}
40+
41+
let dateTag = Int(Date().timeIntervalSince1970 * 1000)
42+
self.tag = dateTag
43+
self.image = nil
44+
45+
// if image can be gathered from cache directly, use it.
46+
if let image = UIImage.fromCache(url) {
47+
self.image = image
48+
completion?(image)
49+
return
50+
}
51+
52+
let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
53+
activityIndicator.color = .orange
54+
activityIndicator.center = self.center
55+
activityIndicator.startAnimating()
56+
self.addSubview(activityIndicator)
57+
58+
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
59+
activityIndicator.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
60+
activityIndicator.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
61+
62+
let request = APIConfiguration(url: url,
63+
responseBodyObject: ResponseImage.self,
64+
priority: .high, timeOut: 10)
65+
66+
request.request { (result) in
67+
DispatchQueue.main.async { [weak self] in
68+
guard let self = self else { return }
69+
activityIndicator.removeFromSuperview()
70+
switch result {
71+
case .error(let error):
72+
errorCompletion?(error.error)
73+
if let fallback = fallback {
74+
self.image = fallback
75+
}
76+
completion?(fallback)
77+
case .success(let image):
78+
if self.tag == dateTag {
79+
self.image = image.responseBody.image
80+
completion?(image.responseBody.image)
81+
}
82+
}
83+
}
84+
}
85+
}
86+
87+
}
88+
89+
/// Wrapper Class to convert Data to UIImage.
90+
class ResponseImage: ResponseBodyParsable {
91+
92+
static var shouldUseCustomInitializer: Bool { true }
93+
/// holds `UIImage` object inside.
94+
var image: UIImage
95+
96+
required init?(_ data: Data) {
97+
guard let image = UIImage(data: data) else {
98+
return nil
99+
}
100+
101+
self.image = image
102+
}
103+
104+
func encode(to encoder: Encoder) throws {
105+
106+
}
107+
108+
required init(from decoder: Decoder) throws {
109+
self.image = UIImage()
110+
}
111+
112+
required init?(_ response: Any?) {
113+
return nil
114+
}
115+
116+
func cachingEndsAt() -> Date? {
117+
return Date().addingTimeInterval(60 * 60)
118+
}
119+
120+
}
121+

0 commit comments

Comments
 (0)