I'm using this code to download my files and display progress in a my collection view item. But if I hide the app while some items are downloading and after open app again, the progress in the item label will stop, but it won't stop downloading. How to update the user interface after the my app opens again?
import UIKit
enum DownloadStatus {
case none
case inProgress
case completed
case failed
}
struct item {
var number: Int!
var downloadStatus: DownloadStatus = .none
var progress: Float = 0.0
init(number: Int) { self.number = number }
}
var downloadQueue = [Int: [Int]]()
var masterIndex = 0
extension URLSession {
func getSessionDescription () -> Int { return Int(self.sessionDescription!)! } // Item ID
func getDebugDescription () -> Int { return Int(self.debugDescription)! } // Collection ID
}
class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
static var shared = DownloadManager()
var identifier : Int = -1
var collectionId : Int = -1
var folderPath : String = ""
typealias ProgressHandler = (Int, Int, Float) -> ()
var onProgress : ProgressHandler? {
didSet { if onProgress != nil { let _ = activate() } }
}
override init() {
super.init()
}
func activate() -> URLSession {
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background.\(NSUUID.init())")
let urlSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
urlSession.sessionDescription = String(identifier)
urlSession.accessibilityHint = String(collectionId)
return urlSession
}
private func calculateProgress(session : URLSession, completionHandler : @escaping (Int, Int, Float) -> ()) {
session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
let progress = downloads.map({ (task) -> Float in
if task.countOfBytesExpectedToReceive > 0 {
return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
} else {
return 0.0
}
})
completionHandler(session.getSessionDescription(), Int(session.accessibilityHint!)!, progress.reduce(0.0, +))
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
let stringNumb = (session.accessibilityHint ?? "hit")
let someNumb = Int(stringNumb as String)
let string1 = (session.sessionDescription ?? "hit")
let some1 = Int(string1 as String)
if let idx = downloadQueue[someNumb!]?.firstIndex(of: some1!) {
downloadQueue[someNumb!]?.remove(at: idx)
print("remove:\(downloadQueue)")
}
let fileName = downloadTask.originalRequest?.url?.lastPathComponent
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
var destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appending("/\(folderPath)"))
do {
try fileManager.createDirectory(at: destinationURLForFile, withIntermediateDirectories: true, attributes: nil)
destinationURLForFile.appendPathComponent(String(describing: fileName!))
try fileManager.moveItem(at: location, to: destinationURLForFile)
} catch (let error) {
print(error)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
if let onProgress = onProgress {
calculateProgress(session: session, completionHandler: onProgress)
}
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let stringNumb = (session.accessibilityHint ?? "hit")
let someNumb = Int(stringNumb as String)
let string1 = (session.sessionDescription ?? "hit")
let some1 = Int(string1 as String)
if let idx = downloadQueue[someNumb!]?.firstIndex(of: some1!) {
downloadQueue[someNumb!]?.remove(at: idx)
print("remove:\(downloadQueue)")
}
}
}
public struct Item: Decodable, Hashable {
let index: Int
let title: String
let image: String
let backgroundColor: String
let borderColor: String
}
public struct Section: Decodable, Hashable {
let index: Int
let identifier: String
let title: String
let subtitle: String
let item: [Item]
}
class CollectionController: UIViewController, UICollectionViewDelegate {
typealias ProgressHandler = (Int, Float) -> ()
var onProgress : ProgressHandler?
var items = [item]()
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Section, Item>?
let sections: [Section] = [.init(index: 0, identifier: "carouselCell", title: "title", subtitle: "sub", item: [
Item(index: 0, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 1, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 2, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 3, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 4, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 5, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 6, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 7, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 8, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 9, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 10, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 11, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 12, title: "Hello", image: "", backgroundColor: "", borderColor: "")
])]
override func viewDidLoad() {
super.viewDidLoad()
createCollectionView()
setupScrollView()
let count = dataSource!.snapshot().numberOfItems
for index in 0...count {
items.append(item(number: index))
}
}
func setupScrollView() {
collectionView.collectionViewLayout = createCompositionalLayout()
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { [weak self] collectionView, indexPath, item in
guard let self = self else { return UICollectionViewCell() }
switch self.sections[indexPath.section].identifier {
case "carouselCell":
let cell = self.configure(CarouselCell.self, with: item, for: indexPath)
if indexPath.row < self.items.count { // <- update UI base on self.items
if self.items[indexPath.row].state == .downloading {
cell.title.text = "\(String(format: "%.f%%", self.items[indexPath.row].progress * 100))"
}
}
return cell
default:
return self.configure(CarouselCell.self, with: item, for: indexPath)
}
}
}
func configure<T: SelfConfiguringCell>(_ cellType: T.Type, with item: Item, for indexPath: IndexPath) -> T {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else { fatalError(" — \(cellType)") }
cell.configure(with: item)
return cell
}
func reloadData() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(sections)
for section in sections { snapshot.appendItems(section.item, toSection: section) }
dataSource?.apply(snapshot)
}
func createCollectionView() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCompositionalLayout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.delegate = self
collectionView.contentInsetAdjustmentBehavior = .never
view.addSubview(collectionView)
collectionView.register(CarouselCell.self, forCellWithReuseIdentifier: CarouselCell.reuseIdentifier)
createDataSource()
reloadData()
}
func createCompositionalLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupWidth = (layoutEnvironment.container.contentSize.width * 1.05)/3
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth), heightDimension: .absolute(groupWidth))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: (layoutEnvironment.container.contentSize.height/2) - (groupWidth/2), leading: 0, bottom: 0, trailing: 0)
section.interGroupSpacing = 20
section.orthogonalScrollingBehavior = .groupPagingCentered
return section
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let directory: String = path[0]
let fileManager = FileManager()
let destination = URL(fileURLWithPath: directory.appendingFormat("/\(indexPath.row+1)"))
var queueArray = downloadQueue[indexPath.row+1] ?? [Int]()
queueArray.append(indexPath.row+1)
downloadQueue[indexPath.row+1] = queueArray
let url = URL(string: "https://file-examples.com/storage/fe91352fe66730de9982024/2017/04/file_example_MP4_480_1_5MG.mp4")!
let downloadManager = DownloadManager()
downloadManager.identifier = indexPath.row+1
downloadManager.collectionId = indexPath.row+1
downloadManager.folderPath = "\(indexPath.row+1)"
let downloadTaskLocal = downloadManager.activate().downloadTask(with: url)
downloadTaskLocal.resume()
//var item = items[indexPath.row] <- only update local value
downloadManager.onProgress = { (row, tableId, progress) in
let row = row - 1
//print("downloadManager.onProgress:\(row), \(tableId), \(String(format: "%.f%%", progress * 100))")
DispatchQueue.main.async {
if progress <= 1.0 {
self.items[row].progress = progress
if progress == 1.0 {
self.items[row].downloadStatus = .completed
} else {
//cell.title.text = "\(String(format: "%.f%%", progress * 100))"
self.items[row].downloadStatus = .inProgress
}
self.reloadItem(indexPath: .init(row: row, section: 0))
}
}
}
}
func reloadItem(indexPath: IndexPath) {
guard let needReloadItem = dataSource!.itemIdentifier(for: indexPath) else { return }
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(sections)
for section in sections { snapshot.appendItems(section.item, toSection: section) }
dataSource?.apply(snapshot)
snapshot.reloadItems([needReloadItem]) // <- reload items
dataSource?.apply(snapshot, animatingDifferences: false)
}
}
protocol SelfConfiguringCell: UICollectionViewCell {
static var reuseIdentifier: String { get }
func configure(with: Item)
}
class CarouselCell: UICollectionViewCell, SelfConfiguringCell {
static var reuseIdentifier: String { "carouselCell" }
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(label)
contentView.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
}
func configure(with item: Item) {
}
required init?(coder: NSCoder) {
fatalError("zzzzz")
}
}
In your code, you are not update downloadStatus
of item
when download task
finish or throwing error.
To fix your problem, I suggest to add 2 closure to notify when download success or error. Here is my example:
import UIKit
enum DownloadStatus {
case none
case inProgress
case completed
case failed
}
struct item {
var number: Int!
var downloadStatus: DownloadStatus = .none
var progress: Float = 0.0
init(number: Int) { self.number = number }
}
var downloadQueue = [Int: [Int]]()
var masterIndex = 0
extension URLSession {
func getSessionDescription () -> Int { return Int(self.sessionDescription!)! } // Item ID
func getDebugDescription () -> Int { return Int(self.debugDescription)! } // Collection ID
}
class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
static var shared = DownloadManager()
var identifier : Int = -1
var collectionId : Int = -1
var folderPath : String = ""
typealias ProgressHandler = (Int, Int, Float) -> ()
var onProgress : ProgressHandler? {
didSet { if onProgress != nil { let _ = activate() } }
}
var onSuccess: ((Int) -> Void)? // <- get called when download success
var onError: ((Error?, Int) -> Void)? // <- get called when download error
override init() {
super.init()
}
func activate() -> URLSession {
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background.\(NSUUID.init())")
let urlSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
urlSession.sessionDescription = String(identifier)
urlSession.accessibilityHint = String(collectionId)
return urlSession
}
private func calculateProgress(session : URLSession, completionHandler : @escaping (Int, Int, Float) -> ()) {
session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
let progress = downloads.map({ (task) -> Float in
if task.countOfBytesExpectedToReceive > 0 {
return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
} else {
return 0.0
}
})
completionHandler(session.getSessionDescription(), Int(session.accessibilityHint!)!, progress.reduce(0.0, +))
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
let stringNumb = (session.accessibilityHint ?? "hit")
let someNumb = Int(stringNumb as String)
let string1 = (session.sessionDescription ?? "hit")
let some1 = Int(string1 as String)
if let idx = downloadQueue[someNumb!]?.firstIndex(of: some1!) {
downloadQueue[someNumb!]?.remove(at: idx)
print("remove:: did finish :: \(downloadQueue)")
}
onSuccess?(collectionId) // call when download success
let fileName = downloadTask.originalRequest?.url?.lastPathComponent
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
var destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appending("/\(folderPath)"))
do {
try fileManager.createDirectory(at: destinationURLForFile, withIntermediateDirectories: true, attributes: nil)
destinationURLForFile.appendPathComponent(String(describing: fileName!))
try fileManager.moveItem(at: location, to: destinationURLForFile)
} catch (let error) {
print(error)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
if let onProgress = onProgress {
calculateProgress(session: session, completionHandler: onProgress)
}
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let stringNumb = (session.accessibilityHint ?? "hit")
let someNumb = Int(stringNumb as String)
let string1 = (session.sessionDescription ?? "hit")
let some1 = Int(string1 as String)
onError?(error, collectionId) // call when download error
if let idx = downloadQueue[someNumb!]?.firstIndex(of: some1!) {
downloadQueue[someNumb!]?.remove(at: idx)
print("remove when complete:\(downloadQueue)")
}
}
}
public struct Item: Decodable, Hashable {
let index: Int
let title: String
let image: String
let backgroundColor: String
let borderColor: String
}
public struct Section: Decodable, Hashable {
let index: Int
let identifier: String
let title: String
let subtitle: String
let item: [Item]
}
class CollectionController: UIViewController, UICollectionViewDelegate {
typealias ProgressHandler = (Int, Float) -> ()
var onProgress : ProgressHandler?
var items = [item]()
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Section, Item>?
let sections: [Section] = [.init(index: 0, identifier: "carouselCell", title: "title", subtitle: "sub", item: [
Item(index: 0, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 1, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 2, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 3, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 4, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 5, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 6, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 7, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 8, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 9, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 10, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 11, title: "Hello", image: "", backgroundColor: "", borderColor: ""),
Item(index: 12, title: "Hello", image: "", backgroundColor: "", borderColor: "")
])]
override func viewDidLoad() {
super.viewDidLoad()
createCollectionView()
setupScrollView()
let count = dataSource!.snapshot().numberOfItems
for index in 0...count {
items.append(item(number: index))
}
}
func setupScrollView() {
collectionView.collectionViewLayout = createCompositionalLayout()
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { [weak self] collectionView, indexPath, item in
guard let self = self else { return UICollectionViewCell() }
switch self.sections[indexPath.section].identifier {
case "carouselCell":
let cell = self.configure(CarouselCell.self, with: item, for: indexPath)
if indexPath.row < self.items.count { // <- update UI base on self.items
let item = self.items[indexPath.row]
switch item.downloadStatus {
case .inProgress:
cell.label.text = "\(String(format: "%.f%%", self.items[indexPath.row].progress * 100))"
case .completed:
cell.label.text = "Completed"
case .failed:
cell.label.text = "FAIL"
case .none:
break
}
}
return cell
default:
return self.configure(CarouselCell.self, with: item, for: indexPath)
}
}
}
func configure<T: SelfConfiguringCell>(_ cellType: T.Type, with item: Item, for indexPath: IndexPath) -> T {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else { fatalError(" — \(cellType)") }
cell.configure(with: item)
return cell
}
func reloadData() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(sections)
for section in sections { snapshot.appendItems(section.item, toSection: section) }
dataSource?.apply(snapshot)
}
func createCollectionView() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCompositionalLayout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.delegate = self
collectionView.contentInsetAdjustmentBehavior = .never
view.addSubview(collectionView)
collectionView.register(CarouselCell.self, forCellWithReuseIdentifier: CarouselCell.reuseIdentifier)
createDataSource()
reloadData()
}
func createCompositionalLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupWidth = (layoutEnvironment.container.contentSize.width * 1.05)/3
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth), heightDimension: .absolute(groupWidth))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: (layoutEnvironment.container.contentSize.height/2) - (groupWidth/2), leading: 0, bottom: 0, trailing: 0)
section.interGroupSpacing = 20
section.orthogonalScrollingBehavior = .groupPagingCentered
return section
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let directory: String = path[0]
var queueArray = downloadQueue[indexPath.row+1] ?? [Int]()
queueArray.append(indexPath.row+1)
downloadQueue[indexPath.row+1] = queueArray
let url = URL(string: "https://file-examples.com/storage/fec85039006734629a992d7/2017/04/file_example_MP4_640_3MG.mp4")!
let downloadManager = DownloadManager()
downloadManager.identifier = indexPath.row+1
downloadManager.collectionId = indexPath.row+1
downloadManager.folderPath = "\(indexPath.row+1)"
let downloadTaskLocal = downloadManager.activate().downloadTask(with: url)
downloadTaskLocal.resume()
//var item = items[indexPath.row] <- only update local value
downloadManager.onError = { [weak self] error, row in
guard let self = self else { return }
DispatchQueue.main.async {
// change download status to .failed when download error
self.items[row - 1].downloadStatus = .failed
self.reloadItem(indexPath: .init(row: row - 1, section: 0))
}
}
downloadManager.onSuccess = { [weak self] row in
guard let self = self else { return }
DispatchQueue.main.async {
// change download status to .completed when download success
self.items[row - 1].downloadStatus = .completed
self.reloadItem(indexPath: .init(row: row - 1, section: 0))
}
}
downloadManager.onProgress = { [weak self] (row, tableId, progress) in
guard let self = self else { return }
let row = row - 1
//print("downloadManager.onProgress:\(row), \(tableId), \(String(format: "%.f%%", progress * 100))")
DispatchQueue.main.async {
if progress <= 1.0 {
self.items[row].progress = progress
if progress == 1.0 {
self.items[row].downloadStatus = .completed
} else {
//cell.title.text = "\(String(format: "%.f%%", progress * 100))"
self.items[row].downloadStatus = .inProgress
}
self.reloadItem(indexPath: .init(row: row, section: 0))
}
}
}
}
func reloadItem(indexPath: IndexPath) {
guard let needReloadItem = dataSource!.itemIdentifier(for: indexPath) else { return }
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(sections)
for section in sections { snapshot.appendItems(section.item, toSection: section) }
dataSource?.apply(snapshot)
snapshot.reloadItems([needReloadItem]) // <- reload items
dataSource?.apply(snapshot, animatingDifferences: false)
}
}
protocol SelfConfiguringCell: UICollectionViewCell {
static var reuseIdentifier: String { get }
func configure(with: Item)
}
class CarouselCell: UICollectionViewCell, SelfConfiguringCell {
static var reuseIdentifier: String { "carouselCell" }
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(label)
contentView.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
}
func configure(with item: Item) {
}
required init?(coder: NSCoder) {
fatalError("zzzzz")
}
}
Also as I debug, method func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
is not get called when app is in background
.