Skip to content

Commit

Permalink
Updated setImage to setImageUrl for Improved API and cancelling logic (
Browse files Browse the repository at this point in the history
…#17)

* Modified setImage to setImageUrl, added computed var for easier api, added tests.

* Change with to url

* Removed duplicate url in naming

* Only support normal for UIButton extension since we can't monitor multiple download receipts with the current implementation
  • Loading branch information
gugges committed Oct 21, 2020
1 parent 387998e commit af6905b
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Example/Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

imageView.ok.setImage(with: URL(string: "https://www.gstatic.com/webp/gallery/4.webp")!)
imageView.ok.setImage(url: URL(string: "https://www.gstatic.com/webp/gallery/4.webp")!)
}
}

19 changes: 16 additions & 3 deletions Sources/Extensions/UIButton+ImageDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,24 @@
import UIKit

public extension ObjectWrapper where T: UIButton {

var imageUrl: URL? {
get {
imageDownloaderReceipt?.url
}
set {
setImage(url: newValue)
}
}

func setImage(with url: URL,
for state: UIControl.State = .normal,
func setImage(url: URL?,
imageDownloader: ImageDownloading = ImageDownloader.shared,
completionHandler: ImageDownloader.CompletionHandler? = nil) {
guard let url = url else {
cancelImageDownload(imageDownloader: imageDownloader)
return
}

if imageDownloaderReceipt != nil {
assertionFailure("Active Download In Progress, Cancel Before Starting a New Request")
}
Expand All @@ -25,7 +38,7 @@ public extension ObjectWrapper where T: UIButton {
switch result {
case .success(let image):
DispatchQueue.executeAsyncOnMain {
self.object.setImage(image, for: state)
self.object.setImage(image, for: .normal)
}

case .failure:
Expand Down
16 changes: 15 additions & 1 deletion Sources/Extensions/UIImageView+ImageDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@
import UIKit

public extension ObjectWrapper where T: UIImageView {

var imageUrl: URL? {
get {
imageDownloaderReceipt?.url
}
set {
setImage(url: newValue)
}
}

func setImage(with url: URL,
func setImage(url: URL?,
imageDownloader: ImageDownloading = ImageDownloader.shared,
completionHandler: ImageDownloader.CompletionHandler? = nil) {
guard let url = url else {
cancelImageDownload(imageDownloader: imageDownloader)
return
}

if imageDownloaderReceipt != nil {
assertionFailure("Active Download In Progress, Cancel Before Starting a New Request")
}
Expand Down
70 changes: 53 additions & 17 deletions Tests/UIButtonImageDownloaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ final class UIButtonImageDownloaderTests: XCTestCase {
button = UIButton(type: .custom)
}

func test_setImage_itSetsTheImageDownloadReceipt() {
func test_setImageUrl_itSetsTheImageDownloadReceipt() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), self.expectedImageData)
}

XCTAssertNil(button.ok.imageDownloaderReceipt)

button.ok.setImage(with: url, imageDownloader: imageDownloader, completionHandler: nil)
button.ok.setImage(url: url, imageDownloader: imageDownloader, completionHandler: nil)

XCTAssertNotNil(button.ok.imageDownloaderReceipt)
}

func test_setImage_whenSuccess_itNilsTheImageDownloadReceipt() {
func test_setImageUrl_whenSuccess_itNilsTheImageDownloadReceipt() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), self.expectedImageData)
Expand All @@ -53,7 +53,7 @@ final class UIButtonImageDownloaderTests: XCTestCase {

let expectation = self.expectation(description: "Nil Receipt on Completion")

button.ok.setImage(with: url, for: .normal, imageDownloader: imageDownloader) { (result, receipt) in
button.ok.setImage(url: url, imageDownloader: imageDownloader) { (result, receipt) in
switch result {
case .success:
break
Expand All @@ -71,7 +71,7 @@ final class UIButtonImageDownloaderTests: XCTestCase {
wait(for: [expectation], timeout: 5)
}

func test_setImage_whenFailure_itNilsTheImageDownloadReceipt() {
func test_setImageUrl_whenFailure_itNilsTheImageDownloadReceipt() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), Data())
Expand All @@ -81,7 +81,7 @@ final class UIButtonImageDownloaderTests: XCTestCase {

let expectation = self.expectation(description: "Nil Receipt on Completion")

button.ok.setImage(with: url, for: .normal, imageDownloader: imageDownloader) { (result, receipt) in
button.ok.setImage(url: url, imageDownloader: imageDownloader) { (result, receipt) in
switch result {
case .success:
XCTFail()
Expand All @@ -107,7 +107,7 @@ final class UIButtonImageDownloaderTests: XCTestCase {

XCTAssertNil(button.ok.imageDownloaderReceipt)

button.ok.setImage(with: url, imageDownloader: imageDownloader, completionHandler: nil)
button.ok.setImage(url: url, imageDownloader: imageDownloader, completionHandler: nil)

XCTAssertNotNil(button.ok.imageDownloaderReceipt)

Expand All @@ -116,7 +116,7 @@ final class UIButtonImageDownloaderTests: XCTestCase {
XCTAssertNil(button.ok.imageDownloaderReceipt)
}

func test_setImage_whenSuccessAndCompletionHandler_itForwardsCompletionHandler() {
func test_setImageUrl_whenSuccessAndCompletionHandler_itForwardsCompletionHandler() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), self.expectedImageData)
Expand All @@ -136,12 +136,12 @@ final class UIButtonImageDownloaderTests: XCTestCase {
expectation.fulfill()
}

button.ok.setImage(with: url, imageDownloader: imageDownloader, completionHandler: completionHandler)
button.ok.setImage(url: url, imageDownloader: imageDownloader, completionHandler: completionHandler)

wait(for: [expectation], timeout: 20)
}

func test_setImage_whenFailureAndCompletionHandler_itForwardsCompletionHandler() {
func test_setImageUrl_whenFailureAndCompletionHandler_itForwardsCompletionHandler() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), Data())
Expand All @@ -161,41 +161,40 @@ final class UIButtonImageDownloaderTests: XCTestCase {
expectation.fulfill()
}

button.ok.setImage(with: url, imageDownloader: imageDownloader, completionHandler: completionHandler)
button.ok.setImage(url: url, imageDownloader: imageDownloader, completionHandler: completionHandler)

wait(for: [expectation], timeout: 20)
}

func test_setImage_whenSuccessAndNoCompletionHandler_itSetsTheImageForState() {
func test_setImageUrl_whenSuccessAndNoCompletionHandler_itSetsTheImageForState() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), self.expectedImageData)
}

XCTAssertNil(button.imageView?.image)

button.ok.setImage(with: url, for: .highlighted, imageDownloader: imageDownloader, completionHandler: nil)
button.ok.setImage(url: url, imageDownloader: imageDownloader, completionHandler: nil)

let expectation = XCTestExpectation(description: "Image Downloader UIButton Success Response")

DispatchQueue.main.asyncAfter(deadline: .now() + MockAsyncUrlProtocol.deadline + 0.1) {
XCTAssertNil(self.button.image(for: .normal))
XCTAssertNotNil(self.button.image(for: .highlighted))
XCTAssertNotNil(self.button.image(for: .normal))
expectation.fulfill()
}

wait(for: [expectation], timeout: 20)
}

func test_setImage_whenFailureAndNoCompletionHandler_itDoesNotSetTheImage() {
func test_setImageUrl_whenFailureAndNoCompletionHandler_itDoesNotSetTheImage() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), Data())
}

let expectation = XCTestExpectation(description: "Image Downloader UIImageView Failure Response")

button.ok.setImage(with: url, imageDownloader: imageDownloader, completionHandler: nil)
button.ok.setImage(url: url, imageDownloader: imageDownloader, completionHandler: nil)

DispatchQueue.main.asyncAfter(deadline: .now() + MockAsyncUrlProtocol.deadline + 0.1) {
XCTAssertNil(self.button.imageView?.image)
Expand All @@ -204,4 +203,41 @@ final class UIButtonImageDownloaderTests: XCTestCase {

wait(for: [expectation], timeout: 20)
}

func test_setImageUrl_whenNil_itCancelsTheImageDownloadButLeavesTheImage() {
MockUrlProtocol.requestHandler = { request in
XCTAssertEqual(request.url, self.url)
return (HTTPURLResponse(), self.expectedImageData)
}

XCTAssertNil(button.ok.imageDownloaderReceipt)

let mockImageDownloader: MockImageDownloader = .init()

button.ok.setImage(url: url, imageDownloader: mockImageDownloader, completionHandler: nil)

button.imageView?.image = expectedImage

XCTAssertNotNil(button.ok.imageDownloaderReceipt)
XCTAssertEqual(mockImageDownloader.cancelCallCount, 0)

button.ok.setImage(url: nil, imageDownloader: mockImageDownloader)

XCTAssertNil(button.ok.imageDownloaderReceipt)
XCTAssertEqual(mockImageDownloader.cancelCallCount, 1)
XCTAssertNotNil(button.imageView?.image)
}

func test_imageUrl_whenNilUrl_itCancelsTheDownload() {
XCTAssertNil(button.ok.imageDownloaderReceipt)

button.ok.imageUrl = url

XCTAssertNotNil(button.ok.imageDownloaderReceipt)
XCTAssertEqual(button.ok.imageUrl, url)

button.ok.imageUrl = nil

XCTAssertNil(button.ok.imageDownloaderReceipt)
}
}
Loading

0 comments on commit af6905b

Please sign in to comment.