From 1414804537ec6ae26369aac17b97e09faf0ccf7a Mon Sep 17 00:00:00 2001 From: Pavel Kozlov <pavelcauselov@gmail.com> Date: Mon, 22 Jan 2018 10:38:12 +0300 Subject: [PATCH 1/5] Download Progress Handler --- .../Rebekka/FileDownloadOperation.swift | 33 +++++--- .../Rebekka/ReadStreamOperation.swift | 7 +- rebekka-source/Rebekka/Session.swift | 83 ++++++++++--------- 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/rebekka-source/Rebekka/FileDownloadOperation.swift b/rebekka-source/Rebekka/FileDownloadOperation.swift index 225806b..d30c260 100644 --- a/rebekka-source/Rebekka/FileDownloadOperation.swift +++ b/rebekka-source/Rebekka/FileDownloadOperation.swift @@ -11,15 +11,16 @@ import Foundation /** Operation for downloading a file from FTP server. */ internal class FileDownloadOperation: ReadStreamOperation { - private var fileHandle: NSFileHandle? - var fileURL: NSURL? + private var fileHandle: FileHandle? + var fileURL: URL? + var progressHandler: DownloadProgressHandler? override func start() { - let filePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent(NSUUID().UUIDString) - self.fileURL = NSURL(fileURLWithPath: filePath) + let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(path ?? UUID().uuidString) + self.fileURL = URL(fileURLWithPath: filePath) do { - try NSData().writeToURL(self.fileURL!, options: NSDataWritingOptions.DataWritingAtomic) - self.fileHandle = try NSFileHandle(forWritingToURL: self.fileURL!) + try Data().write(to: self.fileURL!, options: NSData.WritingOptions.atomic) + self.fileHandle = try FileHandle(forWritingTo: self.fileURL!) self.startOperationWithStream(self.readStream) } catch let error as NSError { self.error = error @@ -27,32 +28,37 @@ internal class FileDownloadOperation: ReadStreamOperation { } } - override func streamEventEnd(aStream: NSStream) -> (Bool, NSError?) { + override func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) { self.fileHandle?.closeFile() return (true, nil) } - override func streamEventError(aStream: NSStream) { + override func streamEventError(_ aStream: Stream) { super.streamEventError(aStream) self.fileHandle?.closeFile() if self.fileURL != nil { do { - try NSFileManager.defaultManager().removeItemAtURL(self.fileURL!) + try FileManager.default.removeItem(at: self.fileURL!) } catch _ { } } self.fileURL = nil } - override func streamEventHasBytes(aStream: NSStream) -> (Bool, NSError?) { - if let inputStream = aStream as? NSInputStream { + override func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) { + let totalBytesSize = aStream.property(forKey: Stream.PropertyKey(rawValue: kCFStreamPropertyFTPResourceSize as String)) as! Int + var downloadedBytes: Int = 0 + + if let inputStream = aStream as? InputStream { var parsetBytes: Int = 0 repeat { parsetBytes = inputStream.read(self.temporaryBuffer, maxLength: 1024) + downloadedBytes += parsetBytes + progressHandler?(Float(downloadedBytes) / Float(totalBytesSize)) if parsetBytes > 0 { autoreleasepool { - let data = NSData(bytes: self.temporaryBuffer, length: parsetBytes) - self.fileHandle!.writeData(data) + let data = Data(bytes: UnsafePointer<UInt8>(self.temporaryBuffer), count: parsetBytes) + self.fileHandle!.write(data) } } } while (parsetBytes > 0) @@ -60,3 +66,4 @@ internal class FileDownloadOperation: ReadStreamOperation { return (true, nil) } } + diff --git a/rebekka-source/Rebekka/ReadStreamOperation.swift b/rebekka-source/Rebekka/ReadStreamOperation.swift index 4514232..e39db17 100644 --- a/rebekka-source/Rebekka/ReadStreamOperation.swift +++ b/rebekka-source/Rebekka/ReadStreamOperation.swift @@ -12,11 +12,11 @@ import Foundation internal class ReadStreamOperation: StreamOperation { internal lazy var temporaryBuffer: UnsafeMutablePointer<UInt8> = { - return UnsafeMutablePointer<UInt8>.alloc(1024) + return UnsafeMutablePointer<UInt8>.allocate(capacity: 1024) }() - lazy var readStream: NSInputStream = { - let cfStream = CFReadStreamCreateWithFTPURL(nil, self.fullURL()) + lazy var readStream: InputStream = { + let cfStream = CFReadStreamCreateWithFTPURL(nil, self.fullURL() as CFURL) CFReadStreamSetDispatchQueue(cfStream.takeUnretainedValue(), self.queue) return cfStream.takeRetainedValue() }() @@ -25,3 +25,4 @@ internal class ReadStreamOperation: StreamOperation { self.startOperationWithStream(self.readStream) } } + diff --git a/rebekka-source/Rebekka/Session.swift b/rebekka-source/Rebekka/Session.swift index 0793cf2..e0f0917 100644 --- a/rebekka-source/Rebekka/Session.swift +++ b/rebekka-source/Rebekka/Session.swift @@ -9,41 +9,42 @@ import Foundation public typealias ResourceResultCompletionHandler = ([ResourceItem]?, NSError?) -> Void -public typealias FileURLResultCompletionHandler = (NSURL?, NSError?) -> Void +public typealias FileURLResultCompletionHandler = (URL?, NSError?) -> Void public typealias BooleanResultCompletionHandler = (Bool, NSError?) -> Void +public typealias DownloadProgressHandler = (Float) -> Void /** The FTP session. */ -public class Session { +open class Session { /** The serial private operation queue. */ - private let operationQueue: NSOperationQueue + fileprivate let operationQueue: OperationQueue /** The queue for completion handlers. */ - private let completionHandlerQueue: NSOperationQueue + fileprivate let completionHandlerQueue: OperationQueue /** The serial queue for streams in operations. */ - private let streamQueue: dispatch_queue_t + fileprivate let streamQueue: DispatchQueue /** The configuration of the session. */ - private let configuration: SessionConfiguration + fileprivate let configuration: SessionConfiguration public init(configuration: SessionConfiguration, - completionHandlerQueue: NSOperationQueue = NSOperationQueue.mainQueue()) { - self.operationQueue = NSOperationQueue() - self.operationQueue.maxConcurrentOperationCount = 1 - self.operationQueue.name = "net.ftp.rebekka.operations.queue" - self.streamQueue = dispatch_queue_create("net.ftp.rebekka.cfstream.queue", nil) - self.completionHandlerQueue = completionHandlerQueue - self.configuration = configuration + completionHandlerQueue: OperationQueue = OperationQueue.main) { + self.operationQueue = OperationQueue() + self.operationQueue.maxConcurrentOperationCount = 1 + self.operationQueue.name = "net.ftp.rebekka.operations.queue" + self.streamQueue = DispatchQueue(label: "net.ftp.rebekka.cfstream.queue", attributes: []) + self.completionHandlerQueue = completionHandlerQueue + self.configuration = configuration } /** Returns content of directory at path. */ - public func list(path: String, completionHandler: ResourceResultCompletionHandler) { + open func list(_ path: String, completionHandler: @escaping ResourceResultCompletionHandler) { let operation = ResourceListOperation(configuration: configuration, queue: self.streamQueue) operation.completionBlock = { [weak operation] in if let strongOperation = operation { - self.completionHandlerQueue.addOperationWithBlock { + self.completionHandlerQueue.addOperation { completionHandler(strongOperation.resources, strongOperation.error) } } @@ -56,12 +57,12 @@ public class Session { } /** Creates new directory at path. */ - public func createDirectory(path: String, completionHandler: BooleanResultCompletionHandler) { + open func createDirectory(_ path: String, completionHandler: @escaping BooleanResultCompletionHandler) { let operation = DirectoryCreationOperation(configuration: configuration, queue: self.streamQueue) operation.completionBlock = { [weak operation] in if let strongOperation = operation { - self.completionHandlerQueue.addOperationWithBlock { + self.completionHandlerQueue.addOperation { completionHandler(strongOperation.error == nil, strongOperation.error) } } @@ -73,15 +74,16 @@ public class Session { self.operationQueue.addOperation(operation) } - /** - Downloads file at path from FTP server. - File is stored in /tmp directory. Caller is responsible for deleting this file. */ - public func download(path: String, completionHandler: FileURLResultCompletionHandler) { + /** + Downloads file at path from FTP server. + File is stored in /tmp directory. Caller is responsible for deleting this file. */ + open func download(_ path: String, progressHandler: @escaping DownloadProgressHandler, completionHandler: @escaping FileURLResultCompletionHandler) { let operation = FileDownloadOperation(configuration: configuration, queue: self.streamQueue) + operation.progressHandler = progressHandler operation.completionBlock = { [weak operation] in if let strongOperation = operation { - self.completionHandlerQueue.addOperationWithBlock { + self.completionHandlerQueue.addOperation { completionHandler(strongOperation.fileURL, strongOperation.error) } } @@ -91,12 +93,12 @@ public class Session { } /** Uploads file from fileURL at path. */ - public func upload(fileURL: NSURL, path: String, completionHandler: BooleanResultCompletionHandler) { + open func upload(_ fileURL: URL, path: String, completionHandler: @escaping BooleanResultCompletionHandler) { let operation = FileUploadOperation(configuration: configuration, queue: self.streamQueue) operation.completionBlock = { [weak operation] in if let strongOperation = operation { - self.completionHandlerQueue.addOperationWithBlock { + self.completionHandlerQueue.addOperation { completionHandler(strongOperation.error == nil, strongOperation.error) } } @@ -112,21 +114,21 @@ public let kFTPAnonymousUser = "anonymous" /** The session configuration. */ public struct SessionConfiguration { /** - The host of FTP server. Defaults to `localhost`. - Can be like this: - ftp://192.168.0.1 - 127.0.0.1:21 - localhost - ftp.mozilla.org - ftp://ftp.mozilla.org:21 - */ + The host of FTP server. Defaults to `localhost`. + Can be like this: + ftp://192.168.0.1 + 127.0.0.1:21 + localhost + ftp.mozilla.org + ftp://ftp.mozilla.org:21 + */ public var host: String = "localhost" /* Whether connection should be passive or not. Defaults to `true`. */ public var passive = true /** The encoding of resource names. */ - public var encoding = NSUTF8StringEncoding + public var encoding = String.Encoding.utf8 /** The username for authorization. Defaults to `anonymous` */ public var username = kFTPAnonymousUser @@ -136,12 +138,12 @@ public struct SessionConfiguration { public init() { } - internal func URL() -> NSURL { + internal func URL() -> Foundation.URL { var stringURL = host if !stringURL.hasPrefix("ftp://") { stringURL = "ftp://\(host)/" } - let url = NSURL(string: stringURL) + let url = Foundation.URL(string: stringURL) return url! } } @@ -150,24 +152,24 @@ public struct SessionConfiguration { private class SessionConfigurationStorage { /** The URL to plist file. */ - private let storageURL: NSURL! + fileprivate let storageURL: URL! init() { - storageURL = NSURL(fileURLWithPath: "") + storageURL = URL(fileURLWithPath: "") } /** Returns an array of all stored servers. */ - private func allServers() { + fileprivate func allServers() { } /** Stores server. */ - private func storeServer() { + fileprivate func storeServer() { } /** Deletes server. */ - private func deleteServer() { + fileprivate func deleteServer() { } @@ -177,3 +179,4 @@ private class SessionConfigurationStorage { private class CredentialsStorage { } + From 1fc6736a0fe88d73a11e010355740541abdaa2ec Mon Sep 17 00:00:00 2001 From: Pavel Kozlov <pavelcauselov@gmail.com> Date: Mon, 22 Jan 2018 10:41:05 +0300 Subject: [PATCH 2/5] Download Progress Handler 2 --- rebekka-demo/Demo.xcodeproj/project.pbxproj | 2 + rebekka-demo/Demo/AppDelegate.swift | 53 ++++++++------- .../Rebekka.xcodeproj/project.pbxproj | 2 + .../Rebekka/DirectoryCreationOperation.swift | 1 + .../Rebekka/FileUploadOperation.swift | 21 +++--- rebekka-source/Rebekka/Operation.swift | 35 +++++----- .../Rebekka/ResourceListOperation.swift | 59 ++++++++-------- rebekka-source/Rebekka/StreamOperation.swift | 67 ++++++++++--------- .../Rebekka/WriteStreamOperation.swift | 7 +- 9 files changed, 131 insertions(+), 116 deletions(-) diff --git a/rebekka-demo/Demo.xcodeproj/project.pbxproj b/rebekka-demo/Demo.xcodeproj/project.pbxproj index e9a7e7e..3fa9f96 100644 --- a/rebekka-demo/Demo.xcodeproj/project.pbxproj +++ b/rebekka-demo/Demo.xcodeproj/project.pbxproj @@ -347,6 +347,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.fry.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -358,6 +359,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.fry.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/rebekka-demo/Demo/AppDelegate.swift b/rebekka-demo/Demo/AppDelegate.swift index ba0009f..e211c3d 100644 --- a/rebekka-demo/Demo/AppDelegate.swift +++ b/rebekka-demo/Demo/AppDelegate.swift @@ -18,45 +18,50 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - // Override point for customization after application launch. - - var configuration = SessionConfiguration() - configuration.host = "ftp://speedtest.tele2.net" - self.session = Session(configuration: configuration) - - testList() - //testDownload() - //testUpload() - //testCreate() - return true + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + + var configuration = SessionConfiguration() + configuration.host = "ftp://speedtest.tele2.net" + self.session = Session(configuration: configuration) + + testList() + // testDownload() + //testUpload() + //testCreate() + return true } func testList() { - self.session.list("/") { + self.session.list("/") { [unowned self] (resources, error) -> Void in - print("List directory with result:\n\(resources), error: \(error)\n\n") + print("List directory with result:\n\(String(describing: resources)), error: \(String(describing: error))\n\n") + } } func testUpload() { - if let URL = NSBundle.mainBundle().URLForResource("TestUpload", withExtension: "png") { - let path = "/upload/\(NSUUID().UUIDString).png" + if let URL = Bundle.main.url(forResource: "TestUpload", withExtension: "png") { + let path = "/upload/\(UUID().uuidString).png" self.session.upload(URL, path: path) { (result, error) -> Void in - print("Upload file with result:\n\(result), error: \(error)\n\n") + print("Upload file with result:\n\(result), error: \(String(describing: error))\n\n") } } } - func testDownload() { - self.session.download("/1MB.zip") { + func testDownload(_ path: String) { + self.session.download(path, progressHandler: { progress in + DispatchQueue.main.async { + print("progress", progress) + } + }) { (fileURL, error) -> Void in - print("Download file with result:\n\(fileURL), error: \(error)\n\n") + print("Download file with result:\n\(String(describing: fileURL)), error: \(String(describing: error))\n\n") if let fileURL = fileURL { do { - try NSFileManager.defaultManager().removeItemAtURL(fileURL) + // try FileManager.default.removeItem(at: fileURL) } catch let error as NSError { print("Error: \(error)") } @@ -66,10 +71,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func testCreate() { - let name = NSUUID().UUIDString + let name = UUID().uuidString self.session.createDirectory("/upload/\(name)") { (result, error) -> Void in - print("Create directory with result:\n\(result), error: \(error)") + print("Create directory with result:\n\(result), error: \(String(describing: error))") } } diff --git a/rebekka-source/Rebekka.xcodeproj/project.pbxproj b/rebekka-source/Rebekka.xcodeproj/project.pbxproj index d80b1b0..fb33872 100644 --- a/rebekka-source/Rebekka.xcodeproj/project.pbxproj +++ b/rebekka-source/Rebekka.xcodeproj/project.pbxproj @@ -348,6 +348,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -388,6 +389,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; diff --git a/rebekka-source/Rebekka/DirectoryCreationOperation.swift b/rebekka-source/Rebekka/DirectoryCreationOperation.swift index 2cf88d5..01e0a63 100644 --- a/rebekka-source/Rebekka/DirectoryCreationOperation.swift +++ b/rebekka-source/Rebekka/DirectoryCreationOperation.swift @@ -12,3 +12,4 @@ import Foundation internal class DirectoryCreationOperation: WriteStreamOperation { } + diff --git a/rebekka-source/Rebekka/FileUploadOperation.swift b/rebekka-source/Rebekka/FileUploadOperation.swift index ce60ff3..a3e15db 100644 --- a/rebekka-source/Rebekka/FileUploadOperation.swift +++ b/rebekka-source/Rebekka/FileUploadOperation.swift @@ -10,12 +10,12 @@ import Foundation /** Operation for file uploading. */ internal class FileUploadOperation: WriteStreamOperation { - private var fileHandle: NSFileHandle? - var fileURL: NSURL! + fileprivate var fileHandle: FileHandle? + var fileURL: URL! override func start() { do { - self.fileHandle = try NSFileHandle(forReadingFromURL: fileURL) + self.fileHandle = try FileHandle(forReadingFrom: fileURL) self.startOperationWithStream(self.writeStream) } catch let error as NSError { self.error = error @@ -24,23 +24,23 @@ internal class FileUploadOperation: WriteStreamOperation { } } - override func streamEventEnd(aStream: NSStream) -> (Bool, NSError?) { + override func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) { self.fileHandle?.closeFile() return (true, nil) } - override func streamEventError(aStream: NSStream) { + override func streamEventError(_ aStream: Stream) { super.streamEventError(aStream) self.fileHandle?.closeFile() } - override func streamEventHasSpace(aStream: NSStream) -> (Bool, NSError?) { - if let writeStream = aStream as? NSOutputStream { + override func streamEventHasSpace(_ aStream: Stream) -> (Bool, NSError?) { + if let writeStream = aStream as? OutputStream { let offsetInFile = self.fileHandle!.offsetInFile - let data = self.fileHandle!.readDataOfLength(1024) - let writtenBytes = writeStream.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length) + let data = self.fileHandle!.readData(ofLength: 1024) + let writtenBytes = writeStream.write((data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count), maxLength: data.count) if writtenBytes > 0 { - self.fileHandle?.seekToFileOffset(offsetInFile + UInt64(writtenBytes)) + self.fileHandle?.seek(toFileOffset: offsetInFile + UInt64(writtenBytes)) } else if writtenBytes == -1 { self.finishOperation() } @@ -49,3 +49,4 @@ internal class FileUploadOperation: WriteStreamOperation { } } + diff --git a/rebekka-source/Rebekka/Operation.swift b/rebekka-source/Rebekka/Operation.swift index 8000815..c2ad122 100644 --- a/rebekka-source/Rebekka/Operation.swift +++ b/rebekka-source/Rebekka/Operation.swift @@ -9,39 +9,40 @@ import Foundation internal enum OperationState { - case None - case Ready - case Executing - case Finished + case none + case ready + case executing + case finished } /** The base class for FTP operations used in framework. */ -internal class Operation: NSOperation { - +internal class Operation: Foundation.Operation { + var error: NSError? internal let configuration: SessionConfiguration - internal var state = OperationState.Ready { + internal var state = OperationState.ready { willSet { - self.willChangeValueForKey("isReady") - self.willChangeValueForKey("isExecuting") - self.willChangeValueForKey("isFinished") + self.willChangeValue(forKey: "isReady") + self.willChangeValue(forKey: "isExecuting") + self.willChangeValue(forKey: "isFinished") } didSet { - self.didChangeValueForKey("isReady") - self.didChangeValueForKey("isExecuting") - self.didChangeValueForKey("isFinished") + self.didChangeValue(forKey: "isReady") + self.didChangeValue(forKey: "isExecuting") + self.didChangeValue(forKey: "isFinished") } } - override var asynchronous: Bool { get { return true } } + override var isAsynchronous: Bool { get { return true } } - override var ready: Bool { get { return self.state == .Ready } } - override var executing: Bool { get { return self.state == .Executing } } - override var finished: Bool { get { return self.state == .Finished } } + override var isReady: Bool { get { return self.state == .ready } } + override var isExecuting: Bool { get { return self.state == .executing } } + override var isFinished: Bool { get { return self.state == .finished } } init(configuration: SessionConfiguration) { self.configuration = configuration } } + diff --git a/rebekka-source/Rebekka/ResourceListOperation.swift b/rebekka-source/Rebekka/ResourceListOperation.swift index 8c34583..57f67fd 100644 --- a/rebekka-source/Rebekka/ResourceListOperation.swift +++ b/rebekka-source/Rebekka/ResourceListOperation.swift @@ -22,18 +22,18 @@ public enum ResourceType: String { case Whiteout = "Whiteout" // DT_WHT } -public class ResourceItem: CustomStringConvertible { - public var type: ResourceType = .Unknown - public var name: String = "" - public var link: String = "" - public var date: NSDate = NSDate() - public var size: Int = 0 - public var mode: Int = 0 - public var owner: String = "" - public var group: String = "" - public var path: String = "/" +open class ResourceItem: CustomStringConvertible { + open var type: ResourceType = .Unknown + open var name: String = "" + open var link: String = "" + open var date: Date = Date() + open var size: Int = 0 + open var mode: Int = 0 + open var owner: String = "" + open var group: String = "" + open var path: String = "/" - public var description: String { + open var description: String { get { return "\nResourceItem: \(name), \(type.rawValue)" } @@ -56,20 +56,20 @@ private let _resourceTypeMap: [Int:ResourceType] = [ /** Operation for resource listing. */ internal class ResourceListOperation: ReadStreamOperation { - private var inputData: NSMutableData? + fileprivate var inputData: NSMutableData? var resources: [ResourceItem]? - override func streamEventEnd(aStream: NSStream) -> (Bool, NSError?) { + override func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) { var offset = 0 - let bytes = UnsafePointer<UInt8>(self.inputData!.bytes) + let bytes = self.inputData!.bytes.bindMemory(to: UInt8.self, capacity: (self.inputData?.length)!) let totalBytes = CFIndex(self.inputData!.length) var parsedBytes = CFIndex(0) - let entity = UnsafeMutablePointer<Unmanaged<CFDictionary>?>.alloc(1) + let entity = UnsafeMutablePointer<Unmanaged<CFDictionary>?>.allocate(capacity: 1) var resources = [ResourceItem]() repeat { - parsedBytes = CFFTPCreateParsedResourceListing(nil, bytes.advancedBy(offset), totalBytes - offset, entity) + parsedBytes = CFFTPCreateParsedResourceListing(nil, bytes.advanced(by: offset), totalBytes - offset, entity) if parsedBytes > 0 { - let value = entity.memory?.takeUnretainedValue() + let value = entity.pointee?.takeUnretainedValue() if let fptResource = value { resources.append(self.mapFTPResources(fptResource)) } @@ -77,11 +77,11 @@ internal class ResourceListOperation: ReadStreamOperation { } } while parsedBytes > 0 self.resources = resources - entity.destroy() + entity.deinitialize() return (true, nil) } - private func mapFTPResources(ftpResources: NSDictionary) -> ResourceItem { + fileprivate func mapFTPResources(_ ftpResources: NSDictionary) -> ResourceItem { let item = ResourceItem() if let mode = ftpResources[kCFFTPResourceMode as String] as? Int { item.mode = mode @@ -90,14 +90,14 @@ internal class ResourceListOperation: ReadStreamOperation { // CFFTPCreateParsedResourceListing assumes that teh names are in MacRoman. // To fix it we create data from string and read it with correct encoding. // https://devforums.apple.com/message/155626#155626 - if configuration.encoding == NSMacOSRomanStringEncoding { + if configuration.encoding == String.Encoding.macOSRoman { item.name = name - } else if let nameData = name.dataUsingEncoding(NSMacOSRomanStringEncoding) { - if let encodedName = NSString(data: nameData, encoding: self.configuration.encoding) { + } else if let nameData = name.data(using: String.Encoding.macOSRoman) { + if let encodedName = NSString(data: nameData, encoding: self.configuration.encoding.rawValue) { item.name = encodedName as String } } - item.path = self.path!.stringByAppendingString(item.name) + item.path = self.path! + item.name } if let owner = ftpResources[kCFFTPResourceOwner as String] as? String { item.owner = owner @@ -116,26 +116,27 @@ internal class ResourceListOperation: ReadStreamOperation { item.type = resourceType } } - if let date = ftpResources[kCFFTPResourceModDate as String] as? NSDate { + if let date = ftpResources[kCFFTPResourceModDate as String] as? Date { item.date = date } return item } - override func streamEventHasBytes(aStream: NSStream) -> (Bool, NSError?) { - if let inputStream = aStream as? NSInputStream { - let buffer = UnsafeMutablePointer<UInt8>.alloc(1024) + override func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) { + if let inputStream = aStream as? InputStream { + let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1024) let result = inputStream.read(buffer, maxLength: 1024) if result > 0 { if self.inputData == nil { self.inputData = NSMutableData(bytes: buffer, length: result) } else { - self.inputData!.appendBytes(buffer, length: result) + self.inputData!.append(buffer, length: result) } } - buffer.destroy() + buffer.deinitialize() } return (true, nil) } } + diff --git a/rebekka-source/Rebekka/StreamOperation.swift b/rebekka-source/Rebekka/StreamOperation.swift index 28dcdd4..439408f 100644 --- a/rebekka-source/Rebekka/StreamOperation.swift +++ b/rebekka-source/Rebekka/StreamOperation.swift @@ -9,35 +9,35 @@ import Foundation /** The base class for stream operations. */ -internal class StreamOperation: Operation, NSStreamDelegate { +internal class StreamOperation: Operation, StreamDelegate { var path: String? - internal let queue: dispatch_queue_t + internal let queue: DispatchQueue - private var currentStream: NSStream? + fileprivate var currentStream: Stream? - init(configuration: SessionConfiguration, queue: dispatch_queue_t) { + init(configuration: SessionConfiguration, queue: DispatchQueue) { self.queue = queue super.init(configuration: configuration) } - private func configureStream(stream: NSStream) { - stream.setProperty(true, forKey: kCFStreamPropertyShouldCloseNativeSocket as String) - stream.setProperty(true, forKey: kCFStreamPropertyFTPFetchResourceInfo as String) - stream.setProperty(self.configuration.passive, forKey: kCFStreamPropertyFTPUsePassiveMode as String) - stream.setProperty(self.configuration.username, forKey: kCFStreamPropertyFTPUserName as String) - stream.setProperty(self.configuration.password, forKey: kCFStreamPropertyFTPPassword as String) + fileprivate func configureStream(_ stream: Stream) { + stream.setProperty(true, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertyShouldCloseNativeSocket as String as String)) + stream.setProperty(true, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertyFTPFetchResourceInfo as String as String)) + stream.setProperty(self.configuration.passive, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertyFTPUsePassiveMode as String as String)) + stream.setProperty(self.configuration.username, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertyFTPUserName as String as String)) + stream.setProperty(self.configuration.password, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertyFTPPassword as String as String)) stream.delegate = self } - func fullURL() -> NSURL { + func fullURL() -> URL { if self.path != nil { - return self.configuration.URL().URLByAppendingPathComponent(path!) + return self.configuration.URL().appendingPathComponent(path!) } - return self.configuration.URL() + return self.configuration.URL() as URL } - @objc func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { - if self.cancelled { + @objc func stream(_ aStream: Stream, handle eventCode: Stream.Event) { + if self.isCancelled { self.streamEventError(aStream) self.error = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil) self.finishOperation() @@ -45,53 +45,54 @@ internal class StreamOperation: Operation, NSStreamDelegate { } switch eventCode { - case NSStreamEvent.OpenCompleted: - self.streamEventOpenComleted(aStream) - case NSStreamEvent.HasBytesAvailable: - self.streamEventHasBytes(aStream) - case NSStreamEvent.HasSpaceAvailable: - self.streamEventHasSpace(aStream) - case NSStreamEvent.ErrorOccurred: + case Stream.Event.openCompleted: + let _ = self.streamEventOpenComleted(aStream) + case Stream.Event.hasBytesAvailable: + let _ = self.streamEventHasBytes(aStream) + case Stream.Event.hasSpaceAvailable: + let _ = self.streamEventHasSpace(aStream) + case Stream.Event.errorOccurred: self.streamEventError(aStream) self.finishOperation() - case NSStreamEvent.EndEncountered: - self.streamEventEnd(aStream) + case Stream.Event.endEncountered: + let _ = self.streamEventEnd(aStream) self.finishOperation() default: print("Unkonwn NSStreamEvent: \(eventCode)") } } - func startOperationWithStream(aStream: NSStream) { + func startOperationWithStream(_ aStream: Stream) { self.currentStream = aStream self.configureStream(self.currentStream!) self.currentStream!.open() - self.state = .Executing + self.state = .executing } func finishOperation() { self.currentStream?.close() self.currentStream = nil - self.state = .Finished + self.state = .finished } - func streamEventOpenComleted(aStream: NSStream) -> (Bool, NSError?) { + func streamEventOpenComleted(_ aStream: Stream) -> (Bool, NSError?) { return (true, nil) } - func streamEventEnd(aStream: NSStream) -> (Bool, NSError?) { + func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) { return (true, nil) } - func streamEventHasBytes(aStream: NSStream) -> (Bool, NSError?) { + func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) { return (true, nil) } - func streamEventHasSpace(aStream: NSStream) -> (Bool, NSError?) { + func streamEventHasSpace(_ aStream: Stream) -> (Bool, NSError?) { return (true, nil) } - func streamEventError(aStream: NSStream) { - self.error = aStream.streamError + func streamEventError(_ aStream: Stream) { + self.error = aStream.streamError as NSError? } } + diff --git a/rebekka-source/Rebekka/WriteStreamOperation.swift b/rebekka-source/Rebekka/WriteStreamOperation.swift index c7ad30d..2fd1813 100644 --- a/rebekka-source/Rebekka/WriteStreamOperation.swift +++ b/rebekka-source/Rebekka/WriteStreamOperation.swift @@ -11,11 +11,11 @@ import Foundation /** The base class for write stream operatons. */ internal class WriteStreamOperation: StreamOperation { - lazy var writeStream: NSOutputStream = { + lazy var writeStream: OutputStream = { let url = self.fullURL() - let cfStream = CFWriteStreamCreateWithFTPURL(nil, self.fullURL()) + let cfStream = CFWriteStreamCreateWithFTPURL(nil, self.fullURL() as CFURL) CFWriteStreamSetDispatchQueue(cfStream.takeUnretainedValue(), self.queue) - let stream: NSOutputStream = cfStream.takeRetainedValue() + let stream: OutputStream = cfStream.takeRetainedValue() return stream }() @@ -23,3 +23,4 @@ internal class WriteStreamOperation: StreamOperation { self.startOperationWithStream(self.writeStream) } } + From eb24433332a65de6faef57cd18158a943ab02698 Mon Sep 17 00:00:00 2001 From: Pavel Kozlov <pavelcauselov@gmail.com> Date: Mon, 22 Jan 2018 10:47:24 +0300 Subject: [PATCH 3/5] updatePodfile --- rebekka.podspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rebekka.podspec b/rebekka.podspec index 090df87..a51f983 100644 --- a/rebekka.podspec +++ b/rebekka.podspec @@ -8,17 +8,17 @@ Pod::Spec.new do |s| s.name = "rebekka" - s.version = "1.0.2" + s.version = "1.0.5" s.summary = "Rebekka - FTP/FTPS client in Swift." s.description = <<-DESC DESC - s.homepage = "https://github.com/Constantine-Fry/rebekka/" + s.homepage = "https://github.com/fvonk/rebekka" s.license = "BSD 2-Clause License" - s.author = { "Constantine Fry" } + s.author = { "Constantine Fry" => "test@gmail.com" } s.ios.deployment_target = "8.0" s.osx.deployment_target = "10.9" - s.source = { :git => "https://github.com/Constantine-Fry/rebekka", :tag => "1.0.2" } + s.source = { :git => "https://github.com/fvonk/rebekka", :tag => "1.0.5" } s.source_files = "Classes", "Classes/**/*.{h,m}" s.exclude_files = "Classes/Exclude" From 3161f5eaf5f79be9e10d5232952ba1ec91c8101e Mon Sep 17 00:00:00 2001 From: Pavel Kozlov <pavelcauselov@gmail.com> Date: Mon, 22 Jan 2018 10:54:44 +0300 Subject: [PATCH 4/5] descr --- rebekka.podspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/rebekka.podspec b/rebekka.podspec index a51f983..61a1e15 100644 --- a/rebekka.podspec +++ b/rebekka.podspec @@ -10,8 +10,6 @@ Pod::Spec.new do |s| s.name = "rebekka" s.version = "1.0.5" s.summary = "Rebekka - FTP/FTPS client in Swift." - s.description = <<-DESC - DESC s.homepage = "https://github.com/fvonk/rebekka" s.license = "BSD 2-Clause License" s.author = { "Constantine Fry" => "test@gmail.com" } From 6bd862cd50abe68f2f20663a818535a287cf236d Mon Sep 17 00:00:00 2001 From: Pavel Kozlov <pavelcauselov@gmail.com> Date: Mon, 22 Jan 2018 17:44:23 +0300 Subject: [PATCH 5/5] 65536 --- rebekka-source/Rebekka/FileDownloadOperation.swift | 2 +- rebekka-source/Rebekka/ReadStreamOperation.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebekka-source/Rebekka/FileDownloadOperation.swift b/rebekka-source/Rebekka/FileDownloadOperation.swift index d30c260..66f4b9d 100644 --- a/rebekka-source/Rebekka/FileDownloadOperation.swift +++ b/rebekka-source/Rebekka/FileDownloadOperation.swift @@ -52,7 +52,7 @@ internal class FileDownloadOperation: ReadStreamOperation { if let inputStream = aStream as? InputStream { var parsetBytes: Int = 0 repeat { - parsetBytes = inputStream.read(self.temporaryBuffer, maxLength: 1024) + parsetBytes = inputStream.read(self.temporaryBuffer, maxLength: 65536) downloadedBytes += parsetBytes progressHandler?(Float(downloadedBytes) / Float(totalBytesSize)) if parsetBytes > 0 { diff --git a/rebekka-source/Rebekka/ReadStreamOperation.swift b/rebekka-source/Rebekka/ReadStreamOperation.swift index e39db17..b998380 100644 --- a/rebekka-source/Rebekka/ReadStreamOperation.swift +++ b/rebekka-source/Rebekka/ReadStreamOperation.swift @@ -12,7 +12,7 @@ import Foundation internal class ReadStreamOperation: StreamOperation { internal lazy var temporaryBuffer: UnsafeMutablePointer<UInt8> = { - return UnsafeMutablePointer<UInt8>.allocate(capacity: 1024) + return UnsafeMutablePointer<UInt8>.allocate(capacity: 65536) }() lazy var readStream: InputStream = {