Skip to content

Commit

Permalink
Merge pull request #6 from crelies/dev
Browse files Browse the repository at this point in the history
Improvements
  • Loading branch information
Chris authored Feb 23, 2020
2 parents 9fe0b6b + afe7706 commit 6fa244d
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 58 deletions.
16 changes: 16 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "ViewInspector",
"repositoryURL": "https://github.com/nalexn/ViewInspector.git",
"state": {
"branch": null,
"revision": "ec943ed718cd293b95f17a2b81e8917d6ed70752",
"version": "0.3.8"
}
}
]
},
"version": 1
}
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ let package = Package(
name: "RemoteImage",
targets: ["RemoteImage"]),
],
dependencies: [
.package(url: "https://github.com/nalexn/ViewInspector.git", from: "0.3.8")
],
targets: [
.target(
name: "RemoteImage",
dependencies: []),
.testTarget(
name: "RemoteImageTests",
dependencies: ["RemoteImage"]),
dependencies: ["RemoteImage", "ViewInspector"]),
]
)
22 changes: 20 additions & 2 deletions Sources/RemoteImage/private/Models/RemoteImageState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,28 @@
// Copyright © 2019 Christian Elies. All rights reserved.
//

import Foundation
#if canImport(UIKit)
import UIKit

enum RemoteImageState: Hashable {
case error(_ error: NSError)
case image(_ image: PlatformSpecificImageType)
case image(_ image: UIImage)
case loading
}

extension RemoteImageState {
var error: NSError? {
guard case let RemoteImageState.error(error) = self else {
return nil
}
return error
}

var image: UIImage? {
guard case let RemoteImageState.image(uiImage) = self else {
return nil
}
return uiImage
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Christian Elies on 15.12.19.
//

#if canImport(UIKit)
import Combine

protocol RemoteImageServiceProtocol where Self: ObservableObject {
Expand All @@ -14,3 +15,4 @@ protocol RemoteImageServiceProtocol where Self: ObservableObject {
var state: RemoteImageState { get set }
func fetchImage(ofType type: RemoteImageType)
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
// Created by Christian Elies on 14.12.19.
//

import Foundation
#if canImport(UIKit)
import UIKit

struct DefaultRemoteImageCache {
let cache = NSCache<AnyObject, PlatformSpecificImageType>()
let cache = NSCache<AnyObject, UIImage>()
}

extension DefaultRemoteImageCache: RemoteImageCache {
func object(forKey key: AnyObject) -> PlatformSpecificImageType? { cache.object(forKey: key) }
func object(forKey key: AnyObject) -> UIImage? { cache.object(forKey: key) }

func setObject(_ object: PlatformSpecificImageType, forKey key: AnyObject) { cache.setObject(object, forKey: key) }
func setObject(_ object: UIImage, forKey key: AnyObject) { cache.setObject(object, forKey: key) }

func removeObject(forKey key: AnyObject) { cache.removeObject(forKey: key) }

func removeAllObjects() { cache.removeAllObjects() }
}
#endif
17 changes: 0 additions & 17 deletions Sources/RemoteImage/public/Models/PlatformSpecificImageType.swift

This file was deleted.

8 changes: 5 additions & 3 deletions Sources/RemoteImage/public/Protocols/RemoteImageCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
// Created by Christian Elies on 14.12.19.
//

import Foundation
#if canImport(UIKit)
import UIKit

public protocol RemoteImageCache {
func object(forKey key: AnyObject) -> PlatformSpecificImageType?
func setObject(_ object: PlatformSpecificImageType, forKey key: AnyObject)
func object(forKey key: AnyObject) -> UIImage?
func setObject(_ object: UIImage, forKey key: AnyObject)
func removeObject(forKey key: AnyObject)
func removeAllObjects()
}
#endif
8 changes: 5 additions & 3 deletions Sources/RemoteImage/public/Services/RemoteImageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
// Copyright © 2019 Christian Elies. All rights reserved.
//

#if canImport(UIKit)
import Combine
import Foundation
import UIKit

public typealias RemoteImageCacheKeyProvider = (RemoteImageType) -> AnyObject

Expand Down Expand Up @@ -52,7 +53,7 @@ extension RemoteImageService {
let urlRequest = URLRequest(url: url)

cancellable = dependencies.remoteImageURLDataPublisher.dataPublisher(for: urlRequest)
.map { PlatformSpecificImageType(data: $0.data) }
.map { UIImage(data: $0.data) }
.receive(on: RunLoop.main)
.sink(receiveCompletion: { completion in
switch completion {
Expand Down Expand Up @@ -80,7 +81,7 @@ extension RemoteImageService {
dependencies.photoKitService.getPhotoData(localIdentifier: localIdentifier) { result in
switch result {
case .success(let data):
if let image = PlatformSpecificImageType(data: data) {
if let image = UIImage(data: data) {
Self.cache.setObject(image, forKey: cacheKey)
self.state = .image(image)
} else {
Expand All @@ -92,3 +93,4 @@ extension RemoteImageService {
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Christian Elies on 29.10.19.
//

#if canImport(UIKit)
import Foundation

public final class RemoteImageServiceFactory {
Expand All @@ -13,3 +14,4 @@ public final class RemoteImageServiceFactory {
return RemoteImageService(dependencies: dependencies)
}
}
#endif
36 changes: 12 additions & 24 deletions Sources/RemoteImage/public/Views/RemoteImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2019 Christian Elies. All rights reserved.
//

#if canImport(SwiftUI) && canImport(UIKit)
import Combine
import SwiftUI

Expand All @@ -17,33 +18,18 @@ public struct RemoteImage<ErrorView: View, ImageView: View, LoadingView: View>:

@ObservedObject private var service = RemoteImageServiceFactory.makeRemoteImageService()

public var body: AnyView {
switch service.state {
case .error(let error):
return AnyView(
errorView(error)
)
case .image(let image):
#if canImport(UIKit)
return AnyView(
self.imageView(Image(uiImage: image))
)
#elseif os(macOS)
return AnyView(
self.imageView(Image(nsImage: image))
)
#else
return AnyView(
Text("Cannot render image: unsupported platform")
)
#endif
case .loading:
return AnyView(
loadingView()
public var body: some View {
Group {
if service.state == .loading {
loadingView()
.onAppear {
self.service.fetchImage(ofType: self.type)
}
)
} else {
service.state.error.map { errorView($0) }

service.state.image.map { self.imageView(Image(uiImage: $0)) }
}
}
}

Expand All @@ -69,3 +55,5 @@ struct RemoteImage_Previews: PreviewProvider {
}
}
#endif

#endif
11 changes: 11 additions & 0 deletions Tests/RemoteImageTests/Extensions/RemoteImage+Inspectable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// RemoteImage+Inspectable.swift
// RemoteImageTests
//
// Created by Christian Elies on 22.02.20.
//

@testable import RemoteImage
import ViewInspector

extension RemoteImage: Inspectable {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// Created by Christian Elies on 14.12.19.
//

#if canImport(UIKit)
@testable import RemoteImage
import UIKit
import XCTest

final class DefaultRemoteImageCacheTests: XCTestCase {
Expand All @@ -17,14 +19,14 @@ final class DefaultRemoteImageCacheTests: XCTestCase {

func testSetImage() {
let key = "Test" as NSString
let image = PlatformSpecificImageType()
let image = UIImage()
remoteImageCache.setObject(image, forKey: key)
XCTAssertEqual(remoteImageCache.object(forKey: key), image)
}

func testRemoveImage() {
let key = "Test" as NSString
let image = PlatformSpecificImageType()
let image = UIImage()
remoteImageCache.setObject(image, forKey: key)
XCTAssertEqual(remoteImageCache.object(forKey: key), image)
remoteImageCache.removeObject(forKey: key)
Expand All @@ -36,3 +38,4 @@ final class DefaultRemoteImageCacheTests: XCTestCase {
("testRemoveImage", testRemoveImage)
]
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Christian Elies on 15.12.19.
//

#if canImport(UIKit)
@testable import RemoteImage
import XCTest

Expand All @@ -14,3 +15,4 @@ final class RemoteImageServiceFactoryTests: XCTestCase {
XCTAssertEqual(service.state, .loading)
}
}
#endif
4 changes: 2 additions & 2 deletions Tests/RemoteImageTests/Services/RemoteImageServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ final class RemoteImageServiceTests: XCTestCase {
return
}

guard let image = PlatformSpecificImageType(systemName: "paperplane.fill") else {
guard let image = UIImage(systemName: "paperplane.fill") else {
XCTFail("Could not create mock image")
return
}
Expand Down Expand Up @@ -254,7 +254,7 @@ final class RemoteImageServiceTests: XCTestCase {
}

func testFetchPHAssetCached() {
guard let image = PlatformSpecificImageType(systemName: "paperplane.fill") else {
guard let image = UIImage(systemName: "paperplane.fill") else {
XCTFail("Could not create mock image")
return
}
Expand Down
38 changes: 38 additions & 0 deletions Tests/RemoteImageTests/Views/RemoteImageTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// RemoteImageTests.swift
// RemoteImageTests
//
// Created by Christian Elies on 22.02.20.
//

#if canImport(UIKit)
@testable import RemoteImage
import SwiftUI
import ViewInspector
import XCTest

final class RemoteImageTests: XCTestCase {
private let fileManager: FileManager = .default
private lazy var mockImageURL = URL(fileURLWithPath: "\(fileManager.currentDirectoryPath)/mock.jpeg")
private let errorStateViewString = "Error"
private lazy var errorView = Text(errorStateViewString)
private let loadingStateViewString = "Loading ..."
private lazy var loadingView = Text(loadingStateViewString)

func testLoadingState() {
let view = RemoteImage(type: .url(mockImageURL),
errorView: { _ in self.errorView },
imageView: { image in image },
loadingView: { self.loadingView })

do {
let inspectableView = try view.body.inspect()
let group = try inspectableView.group().first
let textString = try group?.text().string()
XCTAssertEqual(textString, loadingStateViewString)
} catch {
XCTFail("\(error)")
}
}
}
#endif

0 comments on commit 6fa244d

Please sign in to comment.