I have the following (truncated) code:
Code Block swift struct HomeView: View { @State private var wallpapers: Loadable<[Wallpaper]> = .notRequested @State private var currentPage = 1 @Environment(\.container) private var container var body: some View { content .onAppear { loadWallpapers() } } private var content: some View { VStack { wallpapersList(data: wallpapers.value ?? []) /* ... snip ... */ } } private func wallpapersList(data: [Wallpaper]) -> some View { ScrollView { LazyVStack(spacing: 5) { ForEach(data) { w in networkImage(url: w.thumbs.original) .onAppear { loadNextPage(current: w.id) } } } } } private func networkImage(url: String) -> some View { /* I use https://github.com/onevcat/Kingfisher to handle image loading */ KFImage(URL(string: url)) /* ... snip ...*/ } private func loadWallpapers() { container.interactors.wallpapers.load(data: $wallpapers, page: currentPage) } private func loadNextPage(current: String) { /* ... snip ... */ } }
Code Block swift struct WallpapersInteractor: PWallpapersInteractor { let state: Store<AppState> let agent: PWallpapersAgent func load(data: LoadableSubject<[Wallpaper]>, page: Int) { let store = CancelBag() data.wrappedValue.setLoading(store: store) Just.withErrorType((), Error.self) .flatMap { _ in agent.loadWallpapers(page: page) /* network call */ } .map { response in response.data } .sink { subCompletion in if case let .failure(error) = subCompletion { data.wrappedValue.setFailed(error: error) } } receiveValue: { if var currentWallpapers = data.wrappedValue.value { currentWallpapers.append(contentsOf: $0) /* /!\ */ data.wrappedValue.setLoaded(value: currentWallpapers) } else { data.wrappedValue.setLoaded(value: $0) } } .store(in: store) } }
Because I append the new data to my Binding every time I request a new batch of images, the memory consumption quickly becomes stupidly high.
I tried to remove data from the array using .removeFirst(pageSize) once I get to the third page so that my array contains at most 2 * pageSize elements (pageSize being 64 in this case). But doing so makes my list all jumpy because the content goes up, which creates more problems than it solves.
I tried searching for a solution but I surprisingly didn't find anything on this particular topic, am I missing something obvious ?
Memory consumption grows with the list size grows, even if each row has no image. But the rate is very low.
When images included, memory consumption grows very rapidly.
LazyVStack adds rows on demand, that may update the view structure (so called view graph) which is causing the high memory consumption.
Each KFImage holds an instance of ImageBinding, which has a property image of type KFCrossPlatformImage?. That is not freed even if the row is disappeared. (KFCrossPlatformImage is UIImage on iOS.)