이미지 prefetch하기

SteadySlower·2022년 8월 5일
0

구현하고자 하는 기능

우리 단어장은 텍스트 뿐만 아니라 이미지도 지원합니다. 텍스트에 비하면 이미지는 네트워크를 통해 불러오는 시간이 훨씬 오래 걸리며 무엇보다 처음에 전부 fetch해오는 것이 아니라 url만 fetch 해와서 사용자에게 보여지는 순간에 실제 이미지를 불러옵니다.

단어 카드의 뒷면에 이미지가 있는 경우 단어를 탭해서 뒷면으로 돌려야 이미지 데이터를 fetch 해오기 때문에 아래처럼 1초 가량의 딜레이가 있어 보입니다.

우리는 이미지를 prefetch해서 이 딜레이를 줄여보도록 하겠습니다.

구현

prefetch 함수 만들기

먼저 WordCell의 ViewModel 안에 이미지를 prefetch하는 함수를 만듭니다. KingFisher 라이브러리는 이미지를 미리 패치 해오는 객체를 제공합니다. 해당 객체에 url 전달하면 미리 이미지를 가져와서 메모리에 저장해둘 수 있습니다.

// WordCell의 ViewModel

func prefetchImage() {
    guard word.hasImage == true else { return }
	    //👉 이미지 없으면 그냥 리턴
    var urls = [URL]()
    if !word.frontImageURL.isEmpty { urls.append(URL(string: word.frontImageURL)!) }
    if !word.backImageURL.isEmpty { urls.append(URL(string: word.backImageURL)!) }
    let prefetcher = ImagePrefetcher(urls: urls) {
        skippedResources, failedResources, completedResources in
        print("prefetched image: \(completedResources)")
    }
    prefetcher.start()
}

언제 prefetch를 실행할 것인가?

위에서 구현한 메소드는 WordCell이 init될 때 실행하도록 합시다.

// WordCell의 initializer
init(wordBook: WordBook, word: Binding<Word>) {
    self.viewModel = ViewModel(wordBook: wordBook, word: word)
    viewModel.prefetchImage()
}

근데 이렇게 하면 모든 이미지가 prefetch 되는 것이 아닌가요?

아직 Scene에 보이지 않은 Cell의 이미지까지 prefetch 해오는 것은 자원 낭비입니다. 하지만 우리 앱에서는 그런 걱정을 할 필요가 없습니다. 왜냐하면 각 Cell들이 LazyVStack 안에 있기 때문입니다.

LazyVStack은 Cell이 실제 화면에 보여질 때 init하므로 실제 화면에 띄워진 단어의 이미지만 prefetch 해오게 됩니다.

LazyVStack(spacing: 32) {
    ForEach(0..<viewModel.words.count, id: \.self) { index in
        WordCell(wordBook: viewModel.wordBook, word: $viewModel.words[index], shuffleProvider: viewModel.shufflePublisher)
            .frame(width: deviceWidth * 0.9, height: viewModel.words[index].hasImage ? 200 : 100)
    }
}

결과

뒷면의 이미지를 미리 불러오면서 딜레이가 전혀 없어진 모습입니다.

profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.

0개의 댓글