Skip to content

Commit

Permalink
Add image to now playing (#37)
Browse files Browse the repository at this point in the history
* Add our own image repository for caching images

* Add now playing artwork in async way

* Use our own Image Repository to store images

* Cleanup

* Remove `CachedAsyncImage` from packages
  • Loading branch information
robbevp authored May 7, 2023
1 parent c6bc042 commit 3ee17ec
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 141 deletions.
43 changes: 12 additions & 31 deletions accentor.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
FD0C5CBC29644E50000AF7D6 /* CONTRIBUTING.md in Resources */ = {isa = PBXBuildFile; fileRef = FD0C5CB829644E50000AF7D6 /* CONTRIBUTING.md */; };
FD0C5CBD29644E50000AF7D6 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = FD0C5CB929644E50000AF7D6 /* README.md */; };
FD0C5CBE29644E50000AF7D6 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = FD0C5CBA29644E50000AF7D6 /* LICENSE */; };
FD344EF42A079F7D00B7ADA7 /* ImageRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD344EF32A079F7D00B7ADA7 /* ImageRepository.swift */; };
FD344EF72A07AF3D00B7ADA7 /* CachedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD344EF62A07AF3D00B7ADA7 /* CachedImage.swift */; };
FD344EF92A07B07000B7ADA7 /* CachedImageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD344EF82A07B07000B7ADA7 /* CachedImageViewModel.swift */; };
FD89028B29BC873300AF8007 /* Album+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD89028A29BC873300AF8007 /* Album+CoreDataClass.swift */; };
FD89028D29BC968D00AF8007 /* Artist+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD89028C29BC968D00AF8007 /* Artist+CoreDataClass.swift */; };
FD8CAECE2965B98300BADDC4 /* CachedAsyncImage in Frameworks */ = {isa = PBXBuildFile; productRef = FD8CAECD2965B98300BADDC4 /* CachedAsyncImage */; };
FD8CAED12965B9C300BADDC4 /* AlbumImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD8CAED02965B9C300BADDC4 /* AlbumImage.swift */; };
FD8CAED32965BBD800BADDC4 /* ArtistImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD8CAED22965BBD800BADDC4 /* ArtistImage.swift */; };
FDCC753C29646CCD0079B983 /* PlayQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCC753A29646CCD0079B983 /* PlayQueue.swift */; };
FDCC753D29646CCD0079B983 /* PlayQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCC753B29646CCD0079B983 /* PlayQueueItem.swift */; };
FDCC756229646D200079B983 /* PlayService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCC755B29646D1F0079B983 /* PlayService.swift */; };
Expand Down Expand Up @@ -75,10 +75,11 @@
FD0C5CB829644E50000AF7D6 /* CONTRIBUTING.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = "<group>"; };
FD0C5CB929644E50000AF7D6 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
FD0C5CBA29644E50000AF7D6 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
FD344EF32A079F7D00B7ADA7 /* ImageRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRepository.swift; sourceTree = "<group>"; };
FD344EF62A07AF3D00B7ADA7 /* CachedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedImage.swift; sourceTree = "<group>"; };
FD344EF82A07B07000B7ADA7 /* CachedImageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedImageViewModel.swift; sourceTree = "<group>"; };
FD89028A29BC873300AF8007 /* Album+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Album+CoreDataClass.swift"; sourceTree = "<group>"; };
FD89028C29BC968D00AF8007 /* Artist+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Artist+CoreDataClass.swift"; sourceTree = "<group>"; };
FD8CAED02965B9C300BADDC4 /* AlbumImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumImage.swift; sourceTree = "<group>"; };
FD8CAED22965BBD800BADDC4 /* ArtistImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistImage.swift; sourceTree = "<group>"; };
FDCC753A29646CCD0079B983 /* PlayQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayQueue.swift; sourceTree = "<group>"; };
FDCC753B29646CCD0079B983 /* PlayQueueItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayQueueItem.swift; sourceTree = "<group>"; };
FDCC755B29646D1F0079B983 /* PlayService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -126,7 +127,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FD8CAECE2965B98300BADDC4 /* CachedAsyncImage in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -150,8 +150,8 @@
FD8CAECF2965B9B400BADDC4 /* Image */ = {
isa = PBXGroup;
children = (
FD8CAED02965B9C300BADDC4 /* AlbumImage.swift */,
FD8CAED22965BBD800BADDC4 /* ArtistImage.swift */,
FD344EF62A07AF3D00B7ADA7 /* CachedImage.swift */,
FD344EF82A07B07000B7ADA7 /* CachedImageViewModel.swift */,
);
path = Image;
sourceTree = "<group>";
Expand Down Expand Up @@ -186,6 +186,7 @@
isa = PBXGroup;
children = (
FDCC756C29646D310079B983 /* DefaultSettings.swift */,
FD344EF32A079F7D00B7ADA7 /* ImageRepository.swift */,
);
path = Util;
sourceTree = "<group>";
Expand Down Expand Up @@ -362,7 +363,6 @@
);
name = accentor;
packageProductDependencies = (
FD8CAECD2965B98300BADDC4 /* CachedAsyncImage */,
);
productName = accentor;
productReference = FDFFD6E429113CDE003A1501 /* accentor.app */;
Expand Down Expand Up @@ -437,7 +437,6 @@
);
mainGroup = FDFFD6DB29113CDE003A1501;
packageReferences = (
FD8CAECC2965B98300BADDC4 /* XCRemoteSwiftPackageReference "swiftui-cached-async-image" */,
);
productRefGroup = FDFFD6E529113CDE003A1501 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -492,7 +491,6 @@
FDCC753D29646CCD0079B983 /* PlayQueueItem.swift in Sources */,
FDCC75B129646D320079B983 /* Artists.swift in Sources */,
FDD5BD802A064D100011EBF9 /* Home.swift in Sources */,
FD8CAED32965BBD800BADDC4 /* ArtistImage.swift in Sources */,
FDCC75B829646D320079B983 /* PlayerViewModel.swift in Sources */,
FDCC756229646D200079B983 /* PlayService.swift in Sources */,
FD89028B29BC873300AF8007 /* Album+CoreDataClass.swift in Sources */,
Expand All @@ -508,13 +506,15 @@
FDCC753C29646CCD0079B983 /* PlayQueue.swift in Sources */,
FDCC756429646D200079B983 /* AudioService.swift in Sources */,
FDFFD6EF29113CDE003A1501 /* ContentView.swift in Sources */,
FD344EF72A07AF3D00B7ADA7 /* CachedImage.swift in Sources */,
FDFFD6EA29113CDE003A1501 /* Persistence.swift in Sources */,
FD8CAED12965B9C300BADDC4 /* AlbumImage.swift in Sources */,
FDCC756329646D200079B983 /* AuthService.swift in Sources */,
FDD37F48297DD75400967E55 /* Track+CoreDataClass.swift in Sources */,
FD344EF42A079F7D00B7ADA7 /* ImageRepository.swift in Sources */,
FDCC75B529646D320079B983 /* Albums.swift in Sources */,
FDCC75AD29646D320079B983 /* Tracks.swift in Sources */,
FDCC75AE29646D320079B983 /* TrackRow.swift in Sources */,
FD344EF92A07B07000B7ADA7 /* CachedImageViewModel.swift in Sources */,
FDCC756829646D200079B983 /* ArtistService.swift in Sources */,
FDCC756529646D200079B983 /* TrackService.swift in Sources */,
FDCC756729646D200079B983 /* AlbumService.swift in Sources */,
Expand Down Expand Up @@ -887,25 +887,6 @@
};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
FD8CAECC2965B98300BADDC4 /* XCRemoteSwiftPackageReference "swiftui-cached-async-image" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/lorenzofiamingo/swiftui-cached-async-image";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
FD8CAECD2965B98300BADDC4 /* CachedAsyncImage */ = {
isa = XCSwiftPackageProductDependency;
package = FD8CAECC2965B98300BADDC4 /* XCRemoteSwiftPackageReference "swiftui-cached-async-image" */;
productName = CachedAsyncImage;
};
/* End XCSwiftPackageProductDependency section */

/* Begin XCVersionGroup section */
FDFFD6EB29113CDE003A1501 /* accentor.xcdatamodeld */ = {
isa = XCVersionGroup;
Expand Down

This file was deleted.

133 changes: 133 additions & 0 deletions accentor/Util/ImageRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//
// ImageRepository.swift
// accentor
//
// Created by Robbe Van Petegem on 07/05/2023.
//

import Foundation

// NOTE: it isn't exactly great that we need to duplicate all this code, just make the difference between NSImage and UIImage
// Make figure out if there is a better way of doing this?

#if os(macOS)
import AppKit
#else
import UIKit
#endif

#if os(macOS)
protocol ImageRepositoryProtocol {
func getImage(imageURL: URL) async -> NSImage?
func downloadImage(imageURL: URL) async -> NSImage?
func loadImageFromCache(imageURL: URL) async -> NSImage?
}
#else
protocol ImageRepositoryProtocol {
func getImage(imageURL: URL) async -> UIImage?
func downloadImage(imageURL: URL) async -> UIImage?
func loadImageFromCache(imageURL: URL) async -> UIImage?
}
#endif

#if os(macOS)
public class ImageRepository: ImageRepositoryProtocol {

let cache = URLCache.shared

func getImage(imageURL: URL) async -> NSImage? {
let request = URLRequest(url: imageURL)

if (self.cache.cachedResponse(for: request) != nil) {
let image = self.loadImageFromCache(imageURL: imageURL)
if (image != nil) { return image! }
}

return await self.downloadImage(imageURL: imageURL)
}

func downloadImage(imageURL: URL) async -> NSImage? {
do {
let request = URLRequest(url: imageURL)
let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
return nil
}

guard let image = NSImage(data: data) else {
return nil
}

// Only cache the data if we got a correct image
let cachedData = CachedURLResponse(response: response, data: data)
self.cache.storeCachedResponse(cachedData, for: request)

return image

} catch {
return nil
}
}

func loadImageFromCache(imageURL: URL) -> NSImage? {
let request = URLRequest(url: imageURL)

guard let data = self.cache.cachedResponse(for: request)?.data else {
return nil
}

return NSImage(data: data)
}
}
#else
public class ImageRepository: ImageRepositoryProtocol {

let cache = URLCache.shared

func getImage(imageURL: URL) async -> UIImage? {
let request = URLRequest(url: imageURL)

if (self.cache.cachedResponse(for: request) != nil) {
let image = self.loadImageFromCache(imageURL: imageURL)
if (image != nil) { return image! }
}

return await self.downloadImage(imageURL: imageURL)
}

func downloadImage(imageURL: URL) async -> UIImage? {
do {
let request = URLRequest(url: imageURL)
let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
return nil
}

guard let image = UIImage(data: data) else {
return nil
}

// Only cache the data if we got a correct image
let cachedData = CachedURLResponse(response: response, data: data)
self.cache.storeCachedResponse(cachedData, for: request)

return image

} catch {
return nil
}
}

func loadImageFromCache(imageURL: URL) -> UIImage? {
let request = URLRequest(url: imageURL)

guard let data = self.cache.cachedResponse(for: request)?.data else {
return nil
}

return UIImage(data: data)
}
}
#endif
8 changes: 6 additions & 2 deletions accentor/View/Albums/AlbumCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
//

import SwiftUI
import CachedAsyncImage

struct AlbumCard: View {
var album: Album

var body: some View {
VStack(alignment: .leading) {
AlbumImage(album: album)
CachedImage(imageURL: album.image250) {
ZStack {
Rectangle().fill(.gray)
Image(systemName: "music.note").font(.largeTitle)
}
}.aspectRatio(1, contentMode: .fit)
Text(album.title ?? "")
Text(album.albumArtistsText)
Spacer()
Expand Down
8 changes: 6 additions & 2 deletions accentor/View/Artists/ArtistCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
//

import SwiftUI
import CachedAsyncImage

struct ArtistCard: View {
var artist: Artist

var body: some View {
VStack(alignment: .leading) {
ArtistImage(artist: artist)
CachedImage(imageURL: artist.image250) {
ZStack {
Rectangle().fill(.gray)
Image(systemName: "music.mic").font(.largeTitle)
}
}.aspectRatio(1, contentMode: .fit)
Text(artist.name!)
Spacer()
}
Expand Down
7 changes: 6 additions & 1 deletion accentor/View/Artists/ArtistView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ struct ArtistView: View {
VStack(alignment: .leading) {
HStack {
if (artist.image250 != nil) {
AsyncImage(url: URL(string: artist.image250!))
CachedImage(imageURL: artist.image250) {
ZStack {
Rectangle().fill(.gray)
Image(systemName: "music.mic").font(.largeTitle)
}
}
}
Text(artist.name ?? "")
}
Expand Down
6 changes: 3 additions & 3 deletions accentor/View/Home/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ struct Home: View {
ScrollView(.horizontal) {
LazyHStack(spacing: 5) {
ForEach(recentlyReleasedAlbums) { item in
AlbumCard(album: item).frame(minWidth: 130, maxWidth: 200)
AlbumCard(album: item).frame(width: 200)
}
}
}
Text("Recently added albums")
ScrollView(.horizontal) {
LazyHStack(spacing: 5) {
ForEach(recentlyAddedAlbums) { item in
AlbumCard(album: item).frame(minWidth: 130, maxWidth: 200)
AlbumCard(album: item).frame(width: 200)
}
}
}
Text("Random albums")
ScrollView(.horizontal) {
LazyHStack(spacing: 5) {
ForEach(recentlyAddedAlbums.shuffled()) { item in
AlbumCard(album: item).frame(minWidth: 130, maxWidth: 200)
AlbumCard(album: item).frame(width: 200)
}
}
}
Expand Down
35 changes: 0 additions & 35 deletions accentor/View/Image/AlbumImage.swift

This file was deleted.

Loading

0 comments on commit 3ee17ec

Please sign in to comment.