V8tr / AsyncImage

Asynchronous Image Loading from URL in SwiftUI
https://www.vadimbulavin.com/
The Unlicense
274 stars 36 forks source link

image never update next time after get new url from server #12

Open saroar opened 3 years ago

saroar commented 3 years ago
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

Alsegavrilov commented 3 years ago

Did you find a solution?

shles commented 3 years ago

I'm also interested

saroar commented 3 years ago

@shles @Alsegavrilov I don't remember how I solve it I also add my own code little bit you can check here https://github.com/AddaMeSPB/AddaMeIOS/tree/feature/EventDetaulsUI%2BChatUI/Addame/Addame/Sources/AsyncImageLoder

Alsegavrilov commented 3 years ago

I've changed @StateObject to @ObservedObject (in struct AsyncImage) and in one case it helps.