Skip to content

Instantly share code, notes, and snippets.

@sonsongithub
Created August 11, 2020 15:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sonsongithub/a97ad01bd715bfa1e6d4a6c4a4802c12 to your computer and use it in GitHub Desktop.
Save sonsongithub/a97ad01bd715bfa1e6d4a6c4a4802c12 to your computer and use it in GitHub Desktop.
Try to start download image according to scrolling the list.
//
// ContentView.swift
// testswiftui
//
// Created by sonson on 2020/08/07.
//
import SwiftUI
import UIKit
class MyDownloadTask {
var info: Info
var task: URLSessionTask?
init(anInfo: Info) {
self.info = anInfo
}
}
class DownloadQueue: ObservableObject {
@Published var queue: [MyDownloadTask] = []
let session: URLSession
var isDownloading = false
var currentTask: MyDownloadTask?
let semaphore = DispatchSemaphore(value: 1)
init() {
// URLSessionConfigurationの設定を変更
let config = URLSessionConfiguration.default
config.waitsForConnectivity = true
// 新しくURLSessionインスタンスを作成
self.session = URLSession(configuration: config)
}
func cancel(info: Info) {
self.semaphore.signal()
if let index = queue.firstIndex(where: { (myTask) -> Bool in
myTask.info.id == info.id
}) {
print("remove " + info.url.absoluteString + " from queue")
queue.remove(at: index)
}
if let currentTask = currentTask {
if currentTask.info.id == info.id {
currentTask.task?.cancel()
print("cancel " + info.url.absoluteString)
pop()
}
}
self.semaphore.wait()
}
func pop() {
semaphore.signal()
if isDownloading {
semaphore.wait()
return
}
isDownloading = true
guard let myTask = queue.popLast() else {
print("Queue=" + String(queue.count))
isDownloading = false
semaphore.wait()
return
}
semaphore.wait()
let task = self.session.dataTask(with: myTask.info.url) { (data, response, error) in
var tempImage: UIImage? = nil
if let data = data {
print(data.count)
if let image = UIImage(data: data) {
tempImage = image
} else {
tempImage = UIImage(systemName: "xmark.octagon")
}
} else {
tempImage = UIImage(systemName: "xmark.octagon")
}
DispatchQueue.main.async {
myTask.info.isDownloaded = true
myTask.info.image = tempImage
}
print("Finish downloading")
self.semaphore.signal()
self.isDownloading = false
self.currentTask = nil
self.semaphore.wait()
self.pop()
}
self.semaphore.signal()
print("Start downloading" + myTask.info.url.absoluteString)
myTask.task = task
currentTask = myTask
task.resume()
self.semaphore.wait()
}
func push(info: Info) -> Void {
semaphore.signal()
guard queue.first(where: { (task) -> Bool in
task.info.id == info.id
}) == nil else {
semaphore.wait()
return
}
semaphore.wait()
let myTask = MyDownloadTask(anInfo: info)
semaphore.signal()
queue.append(myTask)
print(queue.count)
semaphore.wait()
pop()
}
}
class Info: ObservableObject, Identifiable {
@Published var message = ""
@Published var id: String
@Published var url: URL
@Published var isDownloaded = false
@Published var image: UIImage?
init(message: String, id: String, url: URL) {
self.message = message
self.id = id
self.url = url
}
}
// copy and paste
// https://masamichi.me/development/2019/10/15/use-activityindicator-swiftui.html
struct ActivityIndicator: UIViewRepresentable {
typealias UIViewType = UIActivityIndicatorView
@Binding var isAnimating: Bool
let style: UIActivityIndicatorView.Style
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> ActivityIndicator.UIViewType {
UIActivityIndicatorView(style: style)
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
}
}
struct ItemRow : View {
@ObservedObject var info: Info
@State var isLoading: Bool = true
var body: some View {
if info.isDownloaded {
VStack() {
Image(uiImage: info.image ?? UIImage(systemName: "link")!)
.resizable()
.scaledToFit()
.frame(width: 250, height: 250, alignment: .center)
}
} else {
VStack() {
ActivityIndicator(isAnimating: $isLoading, style: .medium)
.frame(width: 250, height: 250, alignment: .center)
}
}
}
}
struct ContentView: View {
@ObservedObject var queue = DownloadQueue()
static func getContents() -> [Info] {
do {
if let url = Bundle.main.url(forResource: "img", withExtension: "json") {
let data = try Data(contentsOf: url)
let imageURLs: [URL] = try JSONDecoder().decode([URL].self, from: data)
return imageURLs.enumerated().map {
Info(message: "hoge", id: String($0.offset), url: $0.element)
}
}
} catch {
print(error)
}
return []
}
var model = getContents()
init() {}
var body: some View {
VStack() {
Text("Download Queue = " + String(queue.queue.count))
ScrollView {
LazyVStack {
ForEach(model) { info in
ItemRow(info: info).onAppear(perform: {
if !info.isDownloaded {
queue.push(info: info)
}
}).onDisappear(perform: {
if !info.isDownloaded {
queue.cancel(info: info)
}
})
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment