<commit_before>//
//  AsyncSwiftUIImageView.swift
//  AsyncImageView
//
//  Created by <NAME> on 5/1/20.
//  Copyright © 2020 <NAME>. All rights reserved.
//

import SwiftUI
import Combine
import ReactiveSwift

public struct AsyncSwiftUIImageView<
    Data: RenderDataType,
    ImageViewData: ImageViewDataType,
    Renderer: RendererType,
    PlaceholderRenderer: RendererType>: View
    where
    ImageViewData.RenderData == Data,
    Renderer.Data == Data,
    Renderer.Error == Never,
    PlaceholderRenderer.Data == Data,
    PlaceholderRenderer.Error == Never,
Renderer.RenderResult == PlaceholderRenderer.RenderResult {
    private typealias ImageLoader = AsyncImageLoader<Data, ImageViewData, Renderer, PlaceholderRenderer>

    private let renderer: Renderer
    private let placeholderRenderer: PlaceholderRenderer?
    private let uiScheduler: ReactiveSwift.Scheduler
    private let requestsSignal: Signal<Data?, Never>
    private let requestsObserver: Signal<Data?, Never>.Observer
    
    private let imageCreationScheduler: ReactiveSwift.Scheduler
    
    public init(
        renderer: Renderer,
        placeholderRenderer: PlaceholderRenderer? = nil,
        uiScheduler: ReactiveSwift.Scheduler = UIScheduler(),
        imageCreationScheduler: ReactiveSwift.Scheduler = QueueScheduler())
    {
        self.renderer = renderer
        self.placeholderRenderer = placeholderRenderer
        self.uiScheduler = uiScheduler
        self.imageCreationScheduler = imageCreationScheduler

        (self.requestsSignal, self.requestsObserver) = Signal.pipe()
    }

    @State private var renderResult: Renderer.RenderResult?
    @State private var disposable: Disposable?
    
    public var data: ImageViewData? {
        didSet {
            self.requestImage()
        }
    }
    
    @State
    private var size: CGSize = .zero {
        didSet {
            if self.size != oldValue {
                self.requestImage()
            }
        }
    }
    
    public var body: some View {
        ZStack {
            self.imageView
            
            Color.clear
                .modifier(SizeModifier())
                .onPreferenceChange(ImageSizePreferenceKey.self) { imageSize in
                    self.size = imageSize
                }
        }
        .onAppear {
            self.disposable?.dispose()
            self.disposable = ImageLoader.createSignal(
                requestsSignal: self.requestsSignal,
                renderer: self.renderer,
                placeholderRenderer: self.placeholderRenderer,
                uiScheduler: self.uiScheduler,
                imageCreationScheduler: self.imageCreationScheduler
            )
            .observeValues {
                self.renderResult = $0
            }
        }
        .onDisappear {
            self.disposable?.dispose()<commit_msg>`AsyncSwiftUIImageView`: always request image `onAppear`<commit_after>//
//  AsyncSwiftUIImageView.swift
//  AsyncImageView
//
//  Created by <NAME> on 5/1/20.
//  Copyright © 2020 <NAME>. All rights reserved.
//

import SwiftUI
import Combine
import ReactiveSwift

public struct AsyncSwiftUIImageView<
    Data: RenderDataType,
    ImageViewData: ImageViewDataType,
    Renderer: RendererType,
    PlaceholderRenderer: RendererType>: View
    where
    ImageViewData.RenderData == Data,
    Renderer.Data == Data,
    Renderer.Error == Never,
    PlaceholderRenderer.Data == Data,
    PlaceholderRenderer.Error == Never,
Renderer.RenderResult == PlaceholderRenderer.RenderResult {
    private typealias ImageLoader = AsyncImageLoader<Data, ImageViewData, Renderer, PlaceholderRenderer>

    private let renderer: Renderer
    private let placeholderRenderer: PlaceholderRenderer?
    private let uiScheduler: ReactiveSwift.Scheduler
    private let requestsSignal: Signal<Data?, Never>
    private let requestsObserver: Signal<Data?, Never>.Observer
    
    private let imageCreationScheduler: ReactiveSwift.Scheduler
    
    public init(
        renderer: Renderer,
        placeholderRenderer: PlaceholderRenderer? = nil,
        uiScheduler: ReactiveSwift.Scheduler = UIScheduler(),
        imageCreationScheduler: ReactiveSwift.Scheduler = QueueScheduler())
    {
        self.renderer = renderer
        self.placeholderRenderer = placeholderRenderer
        self.uiScheduler = uiScheduler
        self.imageCreationScheduler = imageCreationScheduler

        (self.requestsSignal, self.requestsObserver) = Signal.pipe()
    }

    @State private var renderResult: Renderer.RenderResult?
    @State private var disposable: Disposable?
    
    public var data: ImageViewData? {
        didSet {
            self.requestImage()
        }
    }
    
    @State
    private var size: CGSize = .zero {
        didSet {
            if self.size != oldValue {
                self.requestImage()
            }
        }
    }
    
    public var body: some View {
        ZStack {
            self.imageView
            
            Color.clear
                .modifier(SizeModifier())
                .onPreferenceChange(ImageSizePreferenceKey.self) { imageSize in
                    self.size = imageSize
                }
        }
        .onAppear {
            self.disposable?.dispose()
            self.disposable = ImageLoader.createSignal(
                requestsSignal: self.requestsSignal,
                renderer: self.renderer,
                placeholderRenderer: self.placeholderRenderer,
                uiScheduler: self.uiScheduler,
                imageCreationScheduler: self.imageCreationScheduler
            )
            .observeValues {
                self.renderResult = $0
            }

            self.requestImage()
        }
        .onDisappear {
            self.disposable?.dispose()
{'fraction_non_alphanumeric': 0.07617728531855955, 'fraction_numerical': 0.0031163434903047093, 'mean_word_length': 2.2023281596452327, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5924958', 'n_tokens_mistral': 1539, 'n_tokens_neox': 1458, 'n_words': 330}