CachedAsyncImage is the simplest way to add cache to your AsyncImage.

Overview

SwiftUI CachedAsyncImage ๐Ÿ—ƒ๏ธ

CachedAsyncImage is AsyncImage, but with cache capabilities.

Usage

CachedAsyncImage has the exact same API and behavior as AsyncImage, so you just have to change this:

AsyncImage(url: logoURL)

to this:

CachedAsyncImage(url: logoURL)

In addition to AsyncImage initializers, you have the possibilities to specify the cache you want to use (by default URLCache.shared is used), and to use URLRequest instead of URL:

CachedAsyncImage(urlRequest: logoURLRequest, urlCache: .imageCache)
// URLCache+imageCache.swift

extension URLCache {
    
    static let imageCache = URLCache(memoryCapacity: 512*1000*1000, diskCapacity: 10*1000*1000*1000)
}

Remember when setting the cache the response (in this case our image) must be no larger than about 5% of the disk cache (See this discussion).

Installation

  1. In Xcode, open your project and navigate to File โ†’ Swift Packages โ†’ Add Package Dependency...
  2. Paste the repository URL (https://github.com/lorenzofiamingo/swiftui-cached-async-image) and click Next.
  3. Click Finish.

Other projects

SwiftUI MapItemPicker ๐Ÿ—บ๏ธ

SwiftUI PhotosPicker ๐ŸŒ‡

SwiftUI VerticalTabView ๐Ÿ”

SwiftUI SharedObject ๐Ÿฑ

Comments
  • URLCache returns nothing before restart

    URLCache returns nothing before restart

    Thanks for the library. I try to use it in a scroll view with transaction animation when an image is loaded:

    CachedAsyncImage(
        url: imageURL,
        urlCache: .imageCache,
        transaction: Transaction(animation: .default)
    ) { phase in
        switch phase {
        case .empty:
            ProgressView()
                .frame(height: 100)
        case .success(let image):
            image
                  .resizable()
        case .failure:
            Image(systemName: "wifi.slash")
        @unknown default:
            EmptyView()
        }
    }
    .frame(maxHeight: 200)
    
    private extension URLCache {
        static let imageCache = URLCache(
            memoryCapacity: 512 * 1000 * 1000,
            diskCapacity: 10 * 1000 * 1000 * 1000
        )
    }
    
    

    But once an image is loaded it continues to animate cell appearance (and other View content) when scrolling down and up again. URLCache doesn't return anything before the app relaunch.

    opened by denis-obukhov 12
  • transaction: Transaction(animation: .easeInOut) does not appear to work

    transaction: Transaction(animation: .easeInOut) does not appear to work

    Great job on this! I was having a huge problem with AsyncImage redownloading images in lazy stacks that this lib completely eliminates.

    The one difference I noticed is that transaction animations don't appear to work. I had a nice ease animation with AsyncImage that does not trigger when I swap it with CachedAsyncImage. Here's my body

    var body: some View { GeometryReader { geo in AsyncImage( url: url, transaction: Transaction(animation: .easeInOut) ) { phase in switch phase { case .empty: Rectangle() .fill(Color.gray1) case let .success(image): image.resizable() .scaledToFill() case .failure: Rectangle() .fill(Color.gray1) @unknown default: EmptyView() } } .frame(width: geo.size.width, height: geo.size.height) .clipped() } } }

    opened by recurrence 6
  • Is 404 counted as an error?

    Is 404 counted as an error?

    It seems after I switched from AsyncImage to your CachedAsyncImage, 404 is no longer an error. Thus when the code tried to load an URL that returned 404, the code would never reach phase.error, thus a placeholder is always showing.

    opened by livid 3
  • Use provided transition on cache hit

    Use provided transition on cache hit

    When provided with an URL that ends up being redirected (301), no cache hit occurs during initialization, as URLCache.cachedResponse expects the redirected URL to be provided instead. This leads to _phase being set to .empty, even though the ensuing .load call will result in a cache hit, because URLSession.data does behave properly.

    ... that's my theory, at least ๐Ÿ˜ฌ

    I saw, that f24cfe71158f428e9a396a19335221f0fe19e10e did away with animating on cache hit. What was the reason for that change?

    opened by PhilipTrauner 2
  • "EMPTY" printed on empty image state

    There is a print statement here that I don't think should be included because having no URL is a valid state and the print doesn't seem to have any purpose. The current inclusion of the statement spams my console since I use this library to asynchronously load a list of users.

    I'd personally like to see it removed or at least have it made toggleable so I don't have to see it.

    As of 3/1/22 the master branch has this issue.

    opened by ImTheSquid 2
  • Transaction doesn't work

    Transaction doesn't work

    Good work, thanks, Lorenzo! Im using library to fetch and cache images like this:

           CachedAsyncImage(
            url: URL(string: path),
            urlCache: .imageCache,
            transaction: Transaction(animation: .easeInOut)) { phase in
                
                switch phase {
                case .empty:
                    ProgressView()
                        .progressViewStyle(.circular)
                case .success(let image):
                    image
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                case .failure:
                    Image("")
                @unknown default:
                    Image("")
                }
            }
            .frame(
                width: frameForBaseImage().0,
                height: frameForBaseImage().1
            )
            .cornerRadius(filesGroupingSelection == .yearGrouping ? 4 : 8)
    

    But transaction doesn't work at all(

    opened by polkandra 1
  • Question -  Where in the code does caching occur?

    Question - Where in the code does caching occur?

    First off, amazing work! Thanks ๐Ÿ™

    I was debugging an issue I have with images no caching correctly (likely due to size or cookies?) and it got me going through the code.

    Could you point me to where the caching occurs? I only see one storeCachedResponse that in my case never gets triggered because there aren't any redirects.

    Does the session automatically do it in this line: let (data, _, metrics) = try await session.data(for: request)? I was under the likely incorrect impression we had to explicitly call storeCachedResponse.

    opened by alfonsogarza 1
  • Question - Is an in-memory cache?

    Question - Is an in-memory cache?

    I mean, will the image still be cached after I exit and re-enter the app, or is just while the app is "alive"? For example I open the app today, and then I open it again in the next month, will the image be still cached?

    Thank you!

    opened by sebasira 1
  • Archive Failing

    Archive Failing

    Whenever I archive with your SPM it fails. I was doing a little reading into the matter, and it seems that you're missing some things in your Package.swift file that is causing the problem.

    platforms: [ .iOS(.v15) ],

    opened by hobbesthetige 1
  • ScrollView + LazyVGrid jumps around when scrolling backward

    ScrollView + LazyVGrid jumps around when scrolling backward

    I have a LazyVGrid inside a Scrollview.. scrolling from start to finish works great , as I scroll backward the scrollview jumps around.. I think the issue is the scrollview content size for each grid item is changing. If I hard code the frame of the image it no longer hitches. I think I should be able to do that as all my images are the same size.. but I didn't run into this issue when trying other image caching frameworks like SDWebImageSwiftUI for example. Just thought you might want to know if it's something that can be looked at.

    
    ScrollView {
    
        LazyVGrid(columns: columnLayout) {
    
            ForEach(settings.wallpaperDataFiltered, id: \.guid) { wallpaper in
    
                ZStack {
                    NavigationLink(destination: WallpaperUI()) {
                       
                        KFImage.url(URL(string: wallpaper.thumbUrl))
                                  .fade(duration: 0.25)
                                  .resizable()
                                  .aspectRatio(contentMode: .fit)
                            
                    }
                
                }
    
            }
    
        }
    
    }
    
    opened by danielkramer 0
  • Loading from cache slowing down animations

    Loading from cache slowing down animations

    Hello @lorenzofiamingo, thank you for this great library! However when the image is really big, it seems like loading from cache slows down main UI thread. Could this operation be moved to background thread by any chance? When initially loading from server, navigation happens immediately and shows loading indicator, which looks good. But when it is already in cache, this is how the animation looks like. In any case have a beautiful day! https://user-images.githubusercontent.com/9447630/177713459-6e510665-a336-4b95-a173-f6a76a0445be.mp4

    opened by f3dm76 1
Owner
Lorenzo Fiamingo
Lorenzo Fiamingo
ZImageCropper is a simplest way to crop image to any shapes you like.

ZImageCropper ZImageCropper is a simplest way to crop image to any shapes you like. Example To run the example project, clone the repo, and run pod in

Mohammad Zaid Pathan 219 Dec 17, 2022
A simplest & base on protocol & swifty way to browse photo or video with hero animation.

JFHeroBrowser Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installation JFHe

้€ธ้ฃŽ 22 Dec 19, 2022
AsyncImageExample An example project for AsyncImage. Loading images in SwiftUI article.

AsyncImageExample An example project for AsyncImage. Loading images in SwiftUI article. Note: The project works in Xcode 13.0 beta (13A5154h).

Artem Novichkov 4 Dec 31, 2021
Backport of SwiftUI.AsyncImage to iOS 14, macOS 11, tvOS 14 and watchOS 7 and earlier.

SBPAsyncImage Backport of SwiftUI.AsyncImage to iOS 14, macOS 11, tvOS 14 and watchOS 7 and earlier. AsyncImage is a view that asynchronously loads an

Yutaro Muta 48 Dec 16, 2022
A simple and flexible way to add source of overlapping circular pictures, currently supports horizontal overlapping or distant pictures with great layout flexibility.

THIS PROJECT IS NO LONGER MAINTAINED. STILL ONE ONLY BEST UI SOLUTION FOR UIKIT DEVELOPERS. SOON WILL COME UP WITH SWIFTUI STILL CONTRIBUTORS ARE WELC

Kiran Jasvanee 673 Dec 19, 2022
A lightweight generic cache for iOS written in Swift with extra love for images.

Haneke is a lightweight generic cache for iOS and tvOS written in Swift 4. It's designed to be super-simple to use. Here's how you would initalize a J

Haneke 5.2k Dec 11, 2022
๐Ÿš€SwiftUI Image downloader with performant LRU mem/disk cache.

Progressive concurrent image downloader for SwiftUI, with neat API and performant LRU mem/disk cache.

Cheng Zhang 42 Sep 24, 2022
Asynchronous image downloader with cache support as a UIImageView category

This library provides an async image downloader with cache support. For convenience, we added categories for UI elements like UIImageView, UIButton, M

null 24.4k Jan 5, 2023
DGImageView - Asynchronous image downloader with cache. Supports gif too

DGImageView Installation Usage DGImageView Asynchronous image downloader with cache. Supports gif, memory cache, disk cache features. Installation Xco

donggyu 1 Jan 1, 2022
WhiteAndFluffyTest - Scroll images and add them to your favourites via image page

Image service application Scroll images and add them to your favourites via imag

Ruben Egikian 0 Feb 4, 2022
add text(multiple line support) to imageView, edit, rotate or resize them as you want, then render the text on image

StickerTextView is an subclass of UIImageView. You can add multiple text to it, edit, rotate, resize the text as you want with one finger, then render the text on Image.

Textcat 478 Dec 17, 2022
TextDrawer, is a UIView allows you to add text, with gesture, on UIView, or UIImage

TextDrawer TextDrawer, is a UIView allows you to add text, with gesture, on UIView, or UIImage. About Annotating Images TextDrawer is the easiest way

Remi ROBERT 106 Dec 18, 2022
A Xcode plugin to add highlight to the instances of selected symbol.

Auto Highlight Symbol About Xcode 8 Xcode 8 does't support plugins anymore, but there is a workaround, use at your own risk. Xcode can highlight insta

Nelson 83 Jan 3, 2023
Shows your photo library grouped by events, to easily export them to your computer

Groupir Shows your photo library grouped by events, to easily export them to your computer Features Currently supported features: reading your photo l

Stanislas Chevallier 0 Dec 15, 2021
๐Ÿ“ธ A wrapper for AVCaptureSession - The way easier to use the Camera.

Capturer A wrapper for AVCaptureSession - The way easier to use the Camera. Setting up let captureBody = CaptureBody( configuration: .init { $0.

Muukii 11 Jul 13, 2022
URLImage is a package that holds an easy way of showing images from an URL.

URLImage Overview URLImage is a package that holds an easy way of showing images from an URL. Usually this processes should take the following process

Ramรณn Dias 1 Nov 1, 2021
A better way to operate QR Code in Swift, support iOS, macOS, watchOS and tvOS.

EFQRCode is a lightweight, pure-Swift library for generating stylized QRCode images with watermark or icon, and for recognizing QRCode from images, in

EFPrefix 4.3k Jan 2, 2023
A powerful new way to Reddit on iOS.

Slide for Reddit Slide is a powerful open-source, ad-free, Swift-based Reddit browser for iOS. Feel free to join us on the official subreddit for disc

Haptic Apps 460 Dec 28, 2022
Lightbox is a convenient and easy to use image viewer for your iOS app

Lightbox is a convenient and easy to use image viewer for your iOS app, packed with all the features you expect: Paginated image slideshow. V

HyperRedink 1.5k Dec 22, 2022