Git Product home page Git Product logo

asyncimage's Introduction

Article related to this project


AsyncImage

The project demonstrates how to load images asynchronously in SwiftUI.

Usage:

// Image URLs to load
let posters = [
    "https://image.tmdb.org/t/p/original/pThyQovXQrw2m0s9x82twj48Jq4.jpg",
    "https://image.tmdb.org/t/p/original/vqzNJRH4YyquRiWxCCOH0aXggHI.jpg",
    "https://image.tmdb.org/t/p/original/6ApDtO7xaWAfPqfi2IARXIzj8QS.jpg",
    "https://image.tmdb.org/t/p/original/7GsM4mtM0worCtIVeiQt28HieeN.jpg"
].map { URL(string: $0)! }

struct ContentView: View {
    var body: some View {
         List(posters, id: \.self) { url in
             AsyncImage(
                url: url,
                placeholder: { Text("Loading ...") },
                image: { Image(uiImage: $0).resizable() }
             )
            .frame(idealHeight: UIScreen.main.bounds.width / 2 * 3) // 2:3 aspect ratio
         }
    }
}

Result:

How to load image from URL asynchronously in SwiftUI

asyncimage's People

Contributors

v8tr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asyncimage's Issues

macOS Catalyst Support?

Hi,

Thanks for the code. Whilst Ive got this working ok on iOS, when I try and run my app as a Catalyst app the "Loading…." placeholder is never replaced.

Has anyone had any luck running this as a Catalyst app or is it just me?

From what I can tell the image is being downloaded ok, but it doesn't seem to trigger the update to the AsyncImage view in the .assign

My first thought is that is a combine/SwiftUI issue under rather than anything to do with the AsyncImage code.

Cheers,

Richard

Using URLCache instead of NSCache

URLCache is both in-memory and on-disk cache, and it doesn't allocate a chunk of memory for it's data. You can define it's in-memory and on-disk size, which is more flexible. URLCache will persist the cached data until the system runs low on disk space.

Image Caching is not working.

Hello, I run this app on iPhone, it seems that image caching is not worked, as I see that if I force-close the app and reopen it, then images were reloading but not fetch from cache.

Image disappears after app resigns active

Hello,

First of all, thanks for the great code! I'm experiencing an issue when the app enters background, and then becomes active again. The image goes away, replaced by the placeholder, and the image never reloads. I get this error in the console: 2020-06-30 15:53:20.517988-0500 AsyncImage[23959:3569320] [] nw_read_request_report [C1] Receive failed with error "Software caused connection abort"

Anybody find a way to fix this?

image never update next time after get new url from server

import Foundation
import Combine
import Pyramid
import UIKit

class UserViewModel: ObservableObject {
  
  @Published var user: CurrentUser = CurrentUser.dInit
  
  @Published var uploading = false
  
  let provider = Pyramid()
  var cancellationToken: AnyCancellable?
  
  init() {
    self.fetchMySelf()
  }
  
}

extension UserViewModel {
  
  func fetchMySelf() {
    guard let my: CurrentUser = KeychainService.loadCodable(for: .currentUser) else {
      return
    }
    
    cancellationToken = provider.request(
      with: UserAPI.me(my.id),
      scheduler: RunLoop.main,
      class: CurrentUser.self
    )
    .receive(on: RunLoop.main)
    .sink(receiveCompletion: { completionResponse in
      switch completionResponse {
      case .failure(let error):
        print(error)
      case .finished:
        break
      }
    }, receiveValue: { [weak self] res in
      guard let self = self else { return }
      self.user = res
      KeychainService.save(codable: res, for: .currentUser)
    })
  }
  
  func uploadAvatar(_ image: UIImage) {
    uploading = true
    guard let me: CurrentUser = KeychainService.loadCodable(for: .currentUser) else {
      return
    }
    
    AWSS3Helper.uploadImage(image, conversationId: nil, userId: me.id) { [weak self] imageURLString in
      DispatchQueue.main.async {
        self?.uploading = false
        let attachment = Attachment(type: .image, userId: me.id, imageUrlString: imageURLString)
        self?.createAttachment(attachment)
      }
    }
  }
  
  func createAttachment(_ attachment: Attachment) {

    cancellationToken = provider.request(
      with: AttachmentAPI.create(attachment),
      scheduler: RunLoop.main,
      class: Attachment.self
    ).sink(receiveCompletion: { completionResponse in
      switch completionResponse {
      case .failure(let error):
        print(error)
      case .finished:
        break
      }
    }, receiveValue: { res in
      print(#line, self, res)
      self.fetchMySelf()
    })
  }
}

I added the default URL when 1st-time load or I don't have any URL
so when my user want to upload photo then get back the image URL my image never change


struct ProfileView: View {
  
  @State var moveToAuth: Bool = false
  
  @StateObject private var eventViewModel = EventViewModel()
  @StateObject private var me = UserViewModel()
  @ObservedObject var viewModel = AuthViewModel()
  @EnvironmentObject var appState: AppState
  @State private var showingImagePicker = false
  @State private var inputImage: UIImage?
  
  var body: some View {
    
    NavigationView {
      ScrollView {
        AsyncImage(
          url: me.user.imageURL,
          placeholder: { Text("Loading...").frame(width: 100, height: 100, alignment: .center) },
          image: {
            Image(uiImage: $0).resizable()
          }
        )
        .aspectRatio(contentMode: .fit)
        .overlay(
          ProfileImageOverlay(
            showingImagePicker: self.$showingImagePicker,
            inputImage: self.$inputImage
          ).environmentObject(me) ,
          alignment: .bottomTrailing
        )
        
        Divider()
        
        VStack(alignment: .leading) {
          Text("My Events:")
            .font(.system(size: 23, weight: .bold, design: .rounded))
            .padding(.top, 10)
            .padding()
        }
        
        
        LazyVStack {
          ForEach(eventViewModel.myEvents) { event in
            EventRow(event: event)
              .hideRowSeparator()
              .onAppear {
                eventViewModel.fetchMoreMyEventIfNeeded(currentItem: event)
              }
          }
          
          if eventViewModel.isLoadingPage {
            ProgressView()
          }
        }
        
        HStack {
          Button(action: {
            AppUserDefaults.resetAuthData()
            self.viewModel.isAuthorized = false
            self.moveToAuth.toggle()
          }) {
            Text("Logout")
              .font(.title)
              .bold()
          }.background(
            NavigationLink.init(
              destination: AuthView()
                .onAppear(perform: {
                  appState.tabBarIsHidden = true
                })
                .navigationBarTitle(String.empty)
                .navigationBarHidden(true),
              isActive: $moveToAuth,
              label: {}
            )
            
          )
        }
      }
      .navigationBarTitle("Profile", displayMode: .inline)
      .onAppear {
        self.eventViewModel.fetchMoreMyEvents()
      }
      .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
        ImagePicker(image: self.$inputImage)
      }
    }
    .navigationViewStyle(StackNavigationViewStyle())
  }
  
  func loadImage() {
    guard let inputImage = inputImage else { return }
    //image = Image(uiImage: inputImage)
    me.uploadAvatar(inputImage)
    
  }
  
}

Simulator Screen Shot - iPhone 12 Pro Max - 2020-11-19 at 21 13 42

always same photo even when I have new image link

Missing curly brackets for placeholder in blog post

Hi,

in your blog post

in listing

`struct ContentView: View {
let url = URL(string: "https://image.tmdb.org/t/p/original/pThyQovXQrw2m0s9x82twj48Jq4.jpg")!

var body: some View {
    AsyncImage(
        url: url,
        placeholder: Text("Loading ...")
    ).aspectRatio(contentMode: .fit)
}

}`

it should read placeholder: {Text("Loading ...")}.

It is correct in the repository. Thanks a lot for that easy introduction to combine and loading images! I am working on a science app and this helped a lot!

Best,
Sebastian

WidgetExtension compatibility

Hello,
Thanks for the example !

I was trying to use your code for an iOS 14 widget extension but the ImageLoader gets deallocated too soon, any ideas ? maybe a limitation in how WidgetExtension works ?

Screenshot 2020-07-02 at 18 07 43

I cloned your project and not compile

I have this errors:

/Users/josepescobar/Downloads/AsyncImage-master/AsyncImage/ContentView.swift:26:30: Cannot convert value of type 'Text' to expected argument type '_?'

/Users/josepescobar/Downloads/AsyncImage-master/AsyncImage/ContentView.swift:22:10: Cannot convert return expression of type 'List<Never, ForEach<[URL], URL, HStack<some View>>>' to return type 'some View'

Crash during runtime with XCode beta 2 and iOS14 beta

The final version of the app crashed for me under iOS14 beta and XCode beta 2 during runtime, when the loader ObservedObject in AsyncImage underwent a premature deinit() during a view update when the downloaded images began to be displayed.

The fix was to make loader an @StateObject to allow it to persist during view updates. To initialize load in AsyncImage's init() required this line of code:

_loader = StateObject(wrappedValue: ImageLoader(url: url, cache: cache))

Code attempts to load same image multiple times when same image occurs more than once in a view

It seems the intention of code like

guard !isLoading else { return }

but it also seems that there can be multiple instances and no tracking of a current loading state against an URL inside a single cache instance

is to prevent image loading twice or whatever, however, if we have a view that uses the same image several times, opening Charles shows the images is attempting to be loaded more than once.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.