mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-29 06:56:46 -04:00
Network
This commit is contained in:
parent
4cdb46afb8
commit
11e4dfb7c2
@ -131,7 +131,7 @@ class LibraryBooksController: CoreDataCollectionBaseController, UICollectionView
|
||||
book.articleCountDescription
|
||||
].flatMap({$0}).joined(separator: " ")
|
||||
cell.descriptionLabel.text = book.desc
|
||||
cell.hasPicLabel.isHidden = book.hasPic
|
||||
cell.hasPicLabel.isHidden = !book.hasPic
|
||||
|
||||
return cell
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C68" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="9Nq-QX-pIk">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12100" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="9Nq-QX-pIk">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12072"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -52,7 +53,7 @@
|
||||
</collectionViewFlowLayout>
|
||||
<cells>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="Aea-Om-Uku" customClass="LibraryCollectionCell" customModule="Kiwix" customModuleProvider="target">
|
||||
<rect key="frame" x="28" y="20" width="320" height="66"/>
|
||||
<rect key="frame" x="27.5" y="20" width="320" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
|
||||
@ -174,13 +175,22 @@
|
||||
<action selector="dismissButtonTapped:" destination="cDq-va-k4I" id="RgX-lM-cfN"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" image="LanguageFilter" id="0v8-uV-zCG">
|
||||
<connections>
|
||||
<segue destination="yzc-l3-ioG" kind="popoverPresentation" identifier="showLangFilter" popoverAnchorBarButtonItem="0v8-uV-zCG" id="hlL-SF-vTM">
|
||||
<popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
|
||||
</segue>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<rightBarButtonItems>
|
||||
<barButtonItem image="LanguageFilter" id="0v8-uV-zCG">
|
||||
<connections>
|
||||
<segue destination="yzc-l3-ioG" kind="popoverPresentation" identifier="showLangFilter" popoverAnchorBarButtonItem="0v8-uV-zCG" id="hlL-SF-vTM">
|
||||
<popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
|
||||
</segue>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Downloading" id="dtT-qi-KWI">
|
||||
<connections>
|
||||
<segue destination="slW-nF-UiF" kind="popoverPresentation" popoverAnchorBarButtonItem="dtT-qi-KWI" id="NDd-oj-RlO">
|
||||
<popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
|
||||
</segue>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</rightBarButtonItems>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="collectionView" destination="UY0-46-c3y" id="lFH-jM-jh7"/>
|
||||
@ -246,6 +256,35 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1112.8" y="-740.7796101949026"/>
|
||||
</scene>
|
||||
<!--Table View Controller-->
|
||||
<scene sceneID="6ID-KU-NtG">
|
||||
<objects>
|
||||
<tableViewController id="hlk-pR-5X0" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="4As-10-hJy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="GKO-mU-RLA">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="GKO-mU-RLA" id="DhQ-qe-TwG">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="hlk-pR-5X0" id="t2d-pl-3Xc"/>
|
||||
<outlet property="delegate" destination="hlk-pR-5X0" id="uOn-D5-gXu"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" id="Ear-9F-grv"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nGp-ZZ-iRU" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1622" y="-2118"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="009-Nq-Jlu">
|
||||
<objects>
|
||||
@ -274,7 +313,7 @@
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="dyh-6J-ZGf" detailTextLabel="d0p-hJ-zVy" style="IBUITableViewCellStyleValue1" id="PuW-jR-mYi">
|
||||
<rect key="frame" x="0.0" y="56" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="PuW-jR-mYi" id="hQJ-5P-hy0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
@ -311,7 +350,7 @@
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<segmentedControl key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="ODt-wT-Y6T">
|
||||
<rect key="frame" x="126" y="8" width="123" height="29"/>
|
||||
<rect key="frame" x="126" y="7.5" width="123" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<segments>
|
||||
<segment title="English"/>
|
||||
@ -330,6 +369,24 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1623" y="-1397"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="INT-af-HVz">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="slW-nF-UiF" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="AEw-P0-Tdq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="hlk-pR-5X0" kind="relationship" relationship="rootViewController" id="v7D-WH-08D"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="z5c-N8-xas" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="721" y="-2118"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Cross" width="16" height="16"/>
|
||||
|
@ -54,7 +54,6 @@
|
||||
974C49681DA4266200E276E1 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 974C49671DA4266200E276E1 /* CloudKit.framework */; };
|
||||
975227CD1D0227E8001D1DDE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227CA1D0227E8001D1DDE /* Main.storyboard */; };
|
||||
975227D01D022814001D1DDE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227CF1D022814001D1DDE /* LaunchScreen.storyboard */; };
|
||||
9757C74C1E106958008A9469 /* BackgroundDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9757C74B1E106958008A9469 /* BackgroundDownload.swift */; };
|
||||
97599AA21E26D3B000BA15EF /* BookmarkBooksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97599AA11E26D3B000BA15EF /* BookmarkBooksController.swift */; };
|
||||
97599AE21E28193D00BA15EF /* BookmarkCollectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97599AE11E28193D00BA15EF /* BookmarkCollectionController.swift */; };
|
||||
975B90FE1CEB909100D13906 /* iOSExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975B90FD1CEB909100D13906 /* iOSExtensions.swift */; };
|
||||
@ -213,7 +212,6 @@
|
||||
974C49671DA4266200E276E1 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||
975227CA1D0227E8001D1DDE /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = "Kiwix-iOS/Storyboard/Main.storyboard"; sourceTree = SOURCE_ROOT; };
|
||||
975227CF1D022814001D1DDE /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = "Kiwix-iOS/Storyboard/LaunchScreen.storyboard"; sourceTree = SOURCE_ROOT; };
|
||||
9757C74B1E106958008A9469 /* BackgroundDownload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundDownload.swift; sourceTree = "<group>"; };
|
||||
97599AA11E26D3B000BA15EF /* BookmarkBooksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkBooksController.swift; sourceTree = "<group>"; };
|
||||
97599AE11E28193D00BA15EF /* BookmarkCollectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkCollectionController.swift; sourceTree = "<group>"; };
|
||||
975B90FD1CEB909100D13906 /* iOSExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = iOSExtensions.swift; path = "Kiwix-iOS/iOSExtensions.swift"; sourceTree = SOURCE_ROOT; };
|
||||
@ -271,7 +269,6 @@
|
||||
97D6811C1D6F70AC00E5FA99 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = "<group>"; };
|
||||
97D6811E1D6F70AC00E5FA99 /* ScanLocalBook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanLocalBook.swift; sourceTree = "<group>"; };
|
||||
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateWidgetDataSourceOperation.swift; sourceTree = "<group>"; };
|
||||
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionDownloadTaskOperation.swift; sourceTree = "<group>"; };
|
||||
97D6812B1D6F70DE00E5FA99 /* 1.5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = 1.5.xcdatamodel; sourceTree = "<group>"; };
|
||||
97D6812C1D6F70DE00E5FA99 /* 1.7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = 1.7.xcdatamodel; sourceTree = "<group>"; };
|
||||
97D6812D1D6F70DE00E5FA99 /* Kiwix.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Kiwix.xcdatamodel; sourceTree = "<group>"; };
|
||||
@ -757,7 +754,6 @@
|
||||
children = (
|
||||
97D6811C1D6F70AC00E5FA99 /* Queue.swift */,
|
||||
9764CBD21D8083AA00072D6A /* ArticleOperation.swift */,
|
||||
9757C74B1E106958008A9469 /* BackgroundDownload.swift */,
|
||||
970A2A211DD562CB0078BB7C /* BookOperations.swift */,
|
||||
973A5C981DEBC54800C7804C /* CloudKit.swift */,
|
||||
973208281DD223DB00EDD3DC /* RefreshLibrary.swift */,
|
||||
@ -765,7 +761,6 @@
|
||||
972F81561DDBFC79008D7289 /* Search.swift */,
|
||||
97D4D64E1D874E6E00C1B065 /* SearchOperation.swift */,
|
||||
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */,
|
||||
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */,
|
||||
976C1DD31E300695005EDEC4 /* UIProcedure.swift */,
|
||||
);
|
||||
name = Operation;
|
||||
@ -1059,7 +1054,6 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9757C74C1E106958008A9469 /* BackgroundDownload.swift in Sources */,
|
||||
973A5C951DEA6DD000C7804C /* URLResponseCache.swift in Sources */,
|
||||
973207A51DD1984700EDD3DC /* SearchScopeAndHistoryController.swift in Sources */,
|
||||
973208231DD19C7600EDD3DC /* DownloadProgress.swift in Sources */,
|
||||
|
@ -11,14 +11,15 @@ import CoreData
|
||||
|
||||
|
||||
class DownloadTask: NSManagedObject {
|
||||
|
||||
class func fetch(book: Book, context: NSManagedObjectContext) -> DownloadTask? {
|
||||
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "DownloadTask")
|
||||
|
||||
class func fetch(bookID: String, context: NSManagedObjectContext) -> DownloadTask? {
|
||||
let fetchRequest = DownloadTask.fetchRequest() as! NSFetchRequest<DownloadTask>
|
||||
guard let book = Book.fetch(bookID, context: context) else {return nil}
|
||||
fetchRequest.predicate = NSPredicate(format: "book = %@", book)
|
||||
let downloadTask = DownloadTask.fetch(fetchRequest, type: DownloadTask.self, context: context)?.first ?? insert(DownloadTask.self, context: context)
|
||||
|
||||
downloadTask?.creationTime = Date()
|
||||
downloadTask?.book = book
|
||||
guard let downloadTask = try? context.fetch(fetchRequest).first ?? DownloadTask(context: context) else {return nil}
|
||||
downloadTask.creationTime = Date()
|
||||
downloadTask.book = book
|
||||
return downloadTask
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,82 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class Network: NSObject {
|
||||
class Network: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {
|
||||
static let shared = Network()
|
||||
private override init() {}
|
||||
var progresses = [String: Int64]()
|
||||
let managedObjectContext = AppDelegate.persistentContainer.viewContext
|
||||
var timer: Timer?
|
||||
|
||||
lazy var wifiSession: URLSession = {
|
||||
let configuration = URLSessionConfiguration.background(withIdentifier: "org.kiwix.wifi")
|
||||
configuration.allowsCellularAccess = false
|
||||
configuration.isDiscretionary = false
|
||||
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
|
||||
}()
|
||||
lazy var cellularSession: URLSession = {
|
||||
let configuration = URLSessionConfiguration.background(withIdentifier: "org.kiwix.cellular")
|
||||
configuration.allowsCellularAccess = true
|
||||
configuration.isDiscretionary = false
|
||||
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
|
||||
}()
|
||||
|
||||
// MARK: - actions
|
||||
|
||||
func start(book: Book) {
|
||||
guard let url = book.url else {return}
|
||||
let task = (book.fileSize > 100000000 ? wifiSession: cellularSession).downloadTask(with: url)
|
||||
task.taskDescription = book.id
|
||||
task.resume()
|
||||
|
||||
let downloadTask = DownloadTask.fetch(bookID: book.id, context: managedObjectContext)
|
||||
downloadTask?.state = .queued
|
||||
|
||||
progresses[book.id] = 0
|
||||
if progresses.count == 1 { startTimer() }
|
||||
}
|
||||
|
||||
func startTimer() {
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in
|
||||
for (bookID, bytesWritten) in self.progresses {
|
||||
guard let book = Book.fetch(bookID, context: self.managedObjectContext) else {continue}
|
||||
book.downloadTask?.totalBytesWritten = bytesWritten
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - URLSessionTaskDelegate
|
||||
|
||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - URLSessionDownloadDelegate
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
|
||||
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
guard let bookID = downloadTask.taskDescription else {return}
|
||||
progresses[bookID] = bytesWritten
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
||||
managedObjectContext.perform {
|
||||
guard let bookID = downloadTask.taskDescription,
|
||||
let book = Book.fetch(bookID, context: self.managedObjectContext),
|
||||
let downloadTask = DownloadTask.fetch(bookID: bookID, context: self.managedObjectContext) else {return}
|
||||
//book.state = .local
|
||||
self.managedObjectContext.delete(downloadTask)
|
||||
print("finish downloading")
|
||||
|
||||
self.progresses[bookID] = nil
|
||||
if self.progresses.count == 0 { self.timer?.invalidate() }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
//
|
||||
// BookDownload.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 12/25/16.
|
||||
// Copyright © 2016 Chris Li. All rights reserved.
|
||||
//
|
||||
|
||||
import ProcedureKit
|
||||
|
||||
class BackgroundDownloadProcedure: Procedure {
|
||||
let task: URLSessionDownloadTask
|
||||
let resumeDataProcessing: (Data?) -> Void
|
||||
private let stateLock = NSLock()
|
||||
private var produceResumeData = false
|
||||
|
||||
init(task: URLSessionDownloadTask, resumeData: @escaping (Data?) -> Void) {
|
||||
self.task = task
|
||||
self.resumeDataProcessing = resumeData
|
||||
super.init()
|
||||
|
||||
add(observer: NetworkObserver())
|
||||
addDidCancelBlockObserver { procedure, errors in
|
||||
procedure.stateLock.withCriticalScope {
|
||||
if procedure.produceResumeData {
|
||||
procedure.task.cancel(byProducingResumeData: self.resumeDataProcessing)
|
||||
} else {
|
||||
procedure.task.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
stateLock.withCriticalScope {
|
||||
guard !isCancelled, task.state == .suspended else { return }
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
func pause() {
|
||||
produceResumeData = true
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
class BookDownloadProcedure: BackgroundDownloadProcedure {
|
||||
|
||||
init(task: URLSessionDownloadTask) {
|
||||
super.init(task: task) { data in
|
||||
print("cancelled, resume data length = \(data?.count)")
|
||||
}
|
||||
addDidFinishBlockObserver { (procedure, errors) in
|
||||
print("Download has finished")
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(session: URLSession, bookID: String, url: URL) {
|
||||
let task = session.downloadTask(with: url)
|
||||
task.taskDescription = bookID
|
||||
self.init(task: task)
|
||||
}
|
||||
|
||||
convenience init(session: URLSession, bookID: String, resumeData: Data) {
|
||||
let task = session.downloadTask(withResumeData: resumeData)
|
||||
task.taskDescription = bookID
|
||||
self.init(task: task)
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
let context = AppDelegate.persistentContainer.viewContext
|
||||
context.performAndWait {
|
||||
guard let bookID = self.task.taskDescription,
|
||||
let book = Book.fetch(bookID, context: context),
|
||||
let downloadTask = DownloadTask.fetch(book: book, context: context) else {return}
|
||||
downloadTask.state = .queued
|
||||
}
|
||||
super.execute()
|
||||
}
|
||||
}
|
@ -91,6 +91,7 @@ extension AlertProcedure {
|
||||
let alert = AlertProcedure(presentAlertFrom: context, withPreferredStyle: .actionSheet, waitForDismissal: true)
|
||||
alert.title = book.title
|
||||
alert.add(actionWithTitle: Localized.Library.download, style: .default) { _ in
|
||||
Network.shared.start(book: book)
|
||||
alert.finish()
|
||||
}
|
||||
alert.add(actionWithTitle: Localized.Library.copyURL, style: .default) { _ in
|
||||
|
@ -1,88 +0,0 @@
|
||||
//
|
||||
// URLSessionDownloadTaskOperation.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 7/11/16.
|
||||
// Copyright © 2016 Chris Li. All rights reserved.
|
||||
//
|
||||
|
||||
import ProcedureKit
|
||||
|
||||
class URLSessionDownloadTaskOperation: Procedure {
|
||||
|
||||
enum KeyPath: String {
|
||||
case State = "state"
|
||||
}
|
||||
|
||||
let task: URLSessionTask
|
||||
|
||||
fileprivate(set) var produceResumeData = false
|
||||
fileprivate var removedObserved = false
|
||||
fileprivate let lock = NSLock()
|
||||
|
||||
init(downloadTask: URLSessionDownloadTask) {
|
||||
self.task = downloadTask
|
||||
super.init()
|
||||
|
||||
add(observer: NetworkObserver())
|
||||
addObserver(DidCancelObserver { _ in
|
||||
if self.produceResumeData {
|
||||
downloadTask.cancelByProducingResumeData({ _ in })
|
||||
} else {
|
||||
downloadTask.cancel()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func cancel(produceResumeData: Bool) {
|
||||
self.produceResumeData = produceResumeData
|
||||
cancel()
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
guard task.state == .suspended || task.state == .running else {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
task.addObserver(self, forKeyPath: "state", options: NSKeyValueObservingOptions(), context: &URLSessionTaskOperationKVOContext)
|
||||
if task.state == .suspended {
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
override func
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [String : AnyObject]?, context: UnsafeMutableRawPointer) {
|
||||
guard context == &URLSessionTaskOperationKVOContext else { return }
|
||||
|
||||
lock.withCriticalScope {
|
||||
if object === task && keyPath == KeyPath.State.rawValue && !removedObserved {
|
||||
|
||||
if case .completed = task.state {
|
||||
finish(task.error)
|
||||
}
|
||||
|
||||
switch task.state {
|
||||
case .completed, .canceling:
|
||||
task.removeObserver(self, forKeyPath: KeyPath.State.rawValue)
|
||||
removedObserved = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable variable_name
|
||||
private var URLSessionTaskOperationKVOContext = 0
|
||||
// swiftlint:enable variable_name
|
||||
|
||||
extension NSLock {
|
||||
func withCriticalScope<T>(_ block: () -> T) -> T {
|
||||
lock()
|
||||
let value = block()
|
||||
unlock()
|
||||
return value
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user