199 lines
8.5 KiB
Swift
199 lines
8.5 KiB
Swift
//
|
|
// ResumeDataHelper.swift
|
|
// Tiercel
|
|
//
|
|
// Created by Daniels on 2019/1/7.
|
|
// Copyright © 2019 Daniels. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
|
|
internal enum ResumeDataHelper {
|
|
|
|
static let infoVersionKey = "NSURLSessionResumeInfoVersion"
|
|
static let currentRequestKey = "NSURLSessionResumeCurrentRequest"
|
|
static let originalRequestKey = "NSURLSessionResumeOriginalRequest"
|
|
static let resumeByteRangeKey = "NSURLSessionResumeByteRange"
|
|
static let infoTempFileNameKey = "NSURLSessionResumeInfoTempFileName"
|
|
static let infoLocalPathKey = "NSURLSessionResumeInfoLocalPath"
|
|
static let bytesReceivedKey = "NSURLSessionResumeBytesReceived"
|
|
static let archiveRootObjectKey = "NSKeyedArchiveRootObjectKey"
|
|
|
|
internal static func handleResumeData(_ data: Data) -> Data? {
|
|
if #available(iOS 11.3, *) {
|
|
return data
|
|
} else if #available(iOS 11.0, *) {
|
|
// 修复 11.0 - 11.2 bug
|
|
return deleteResumeByteRange(data)
|
|
} else if #available(iOS 10.2, *) {
|
|
return data
|
|
} else if #available(iOS 10.0, *) {
|
|
// 修复 10.0 - 10.1 bug
|
|
return correctResumeData(data)
|
|
} else {
|
|
return data
|
|
}
|
|
}
|
|
|
|
|
|
/// 修复 11.0 - 11.2 resumeData bug
|
|
///
|
|
/// - Parameter data:
|
|
/// - Returns:
|
|
private static func deleteResumeByteRange(_ data: Data) -> Data? {
|
|
guard let resumeDictionary = getResumeDictionary(data) else { return nil }
|
|
resumeDictionary.removeObject(forKey: resumeByteRangeKey)
|
|
return try? PropertyListSerialization.data(fromPropertyList: resumeDictionary,
|
|
format: PropertyListSerialization.PropertyListFormat.xml,
|
|
options: PropertyListSerialization.WriteOptions())
|
|
}
|
|
|
|
|
|
/// 修复 10.0 - 10.1 resumeData bug
|
|
///
|
|
/// - Parameter data:
|
|
/// - Returns:
|
|
private static func correctResumeData(_ data: Data) -> Data? {
|
|
guard let resumeDictionary = getResumeDictionary(data) else { return nil }
|
|
|
|
if let currentRequest = resumeDictionary[currentRequestKey] as? Data {
|
|
resumeDictionary[currentRequestKey] = correct(with: currentRequest)
|
|
}
|
|
if let originalRequest = resumeDictionary[originalRequestKey] as? Data {
|
|
resumeDictionary[originalRequestKey] = correct(with: originalRequest)
|
|
}
|
|
|
|
return try? PropertyListSerialization.data(fromPropertyList: resumeDictionary,
|
|
format: PropertyListSerialization.PropertyListFormat.xml,
|
|
options: PropertyListSerialization.WriteOptions())
|
|
}
|
|
|
|
|
|
/// 把resumeData解析成字典
|
|
///
|
|
/// - Parameter data:
|
|
/// - Returns:
|
|
internal static func getResumeDictionary(_ data: Data) -> NSMutableDictionary? {
|
|
// In beta versions, resumeData is NSKeyedArchive encoded instead of plist
|
|
var object: NSDictionary?
|
|
if #available(OSX 10.11, iOS 9.0, *) {
|
|
let keyedUnarchiver = NSKeyedUnarchiver(forReadingWith: data)
|
|
|
|
do {
|
|
object = try keyedUnarchiver.decodeTopLevelObject(of: NSDictionary.self, forKey: archiveRootObjectKey)
|
|
if object == nil {
|
|
object = try keyedUnarchiver.decodeTopLevelObject(of: NSDictionary.self, forKey: NSKeyedArchiveRootObjectKey)
|
|
}
|
|
} catch {}
|
|
keyedUnarchiver.finishDecoding()
|
|
}
|
|
|
|
if object == nil {
|
|
do {
|
|
object = try PropertyListSerialization.propertyList(from: data,
|
|
options: .mutableContainersAndLeaves,
|
|
format: nil) as? NSDictionary
|
|
} catch {}
|
|
}
|
|
|
|
if let resumeDictionary = object as? NSMutableDictionary {
|
|
return resumeDictionary
|
|
}
|
|
|
|
guard let resumeDictionary = object else { return nil }
|
|
return NSMutableDictionary(dictionary: resumeDictionary)
|
|
|
|
}
|
|
|
|
internal static func getTmpFileName(_ data: Data) -> String? {
|
|
guard let resumeDictionary = ResumeDataHelper.getResumeDictionary(data),
|
|
let version = resumeDictionary[infoVersionKey] as? Int
|
|
else { return nil }
|
|
if version > 1 {
|
|
return resumeDictionary[infoTempFileNameKey] as? String
|
|
} else {
|
|
guard let path = resumeDictionary[infoLocalPathKey] as? String else { return nil }
|
|
let url = URL(fileURLWithPath: path)
|
|
return url.lastPathComponent
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// 修复resumeData中的当前请求数据和原始请求数据
|
|
///
|
|
/// - Parameter data:
|
|
/// - Returns:
|
|
private static func correct(with data: Data) -> Data? {
|
|
if NSKeyedUnarchiver.unarchiveObject(with: data) != nil {
|
|
return data
|
|
}
|
|
guard let resumeDictionary = try? PropertyListSerialization.propertyList(from: data,
|
|
options: .mutableContainersAndLeaves,
|
|
format: nil) as? NSMutableDictionary
|
|
else { return nil }
|
|
// Rectify weird __nsurlrequest_proto_props objects to $number pattern
|
|
var k = 0
|
|
while ((resumeDictionary["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "$\(k)") != nil {
|
|
k += 1
|
|
}
|
|
var i = 0
|
|
while ((resumeDictionary["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_prop_obj_\(i)") != nil {
|
|
let arr = resumeDictionary["$objects"] as? NSMutableArray
|
|
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_prop_obj_\(i)"] {
|
|
dic.setObject(obj, forKey: "$\(i + k)" as NSString)
|
|
dic.removeObject(forKey: "__nsurlrequest_proto_prop_obj_\(i)")
|
|
arr?[1] = dic
|
|
resumeDictionary["$objects"] = arr
|
|
}
|
|
i += 1
|
|
}
|
|
if ((resumeDictionary["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_props") != nil {
|
|
let arr = resumeDictionary["$objects"] as? NSMutableArray
|
|
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_props"] {
|
|
dic.setObject(obj, forKey: "$\(i + k)" as NSString)
|
|
dic.removeObject(forKey: "__nsurlrequest_proto_props")
|
|
arr?[1] = dic
|
|
resumeDictionary["$objects"] = arr
|
|
}
|
|
}
|
|
|
|
if let obj = (resumeDictionary["$top"] as? NSMutableDictionary)?.object(forKey: archiveRootObjectKey) as AnyObject? {
|
|
(resumeDictionary["$top"] as? NSMutableDictionary)?.setObject(obj, forKey: NSKeyedArchiveRootObjectKey as NSString)
|
|
(resumeDictionary["$top"] as? NSMutableDictionary)?.removeObject(forKey: archiveRootObjectKey)
|
|
}
|
|
// Reencode archived object
|
|
return try? PropertyListSerialization.data(fromPropertyList: resumeDictionary,
|
|
format: PropertyListSerialization.PropertyListFormat.binary,
|
|
options: PropertyListSerialization.WriteOptions())
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|