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 = {