diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..9f55b2c --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.0 diff --git a/rebekka-demo/Demo.xcodeproj/project.pbxproj b/rebekka-demo/Demo.xcodeproj/project.pbxproj index e9a7e7e..f13a2b0 100644 --- a/rebekka-demo/Demo.xcodeproj/project.pbxproj +++ b/rebekka-demo/Demo.xcodeproj/project.pbxproj @@ -154,11 +154,12 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Constantine Fry"; TargetAttributes = { 47FFC2A51B09287300D3EDFE = { CreatedOnToolsVersion = 6.3.1; + LastSwiftMigration = 1000; }; }; }; @@ -265,13 +266,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -310,13 +321,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -335,6 +356,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -347,6 +369,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.fry.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -358,6 +382,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.fry.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/rebekka-demo/Demo/AppDelegate.swift b/rebekka-demo/Demo/AppDelegate.swift index ba0009f..1a44e05 100644 --- a/rebekka-demo/Demo/AppDelegate.swift +++ b/rebekka-demo/Demo/AppDelegate.swift @@ -18,8 +18,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. var configuration = SessionConfiguration() @@ -36,16 +36,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func testList() { self.session.list("/") { (resources, error) -> Void in - print("List directory with result:\n\(resources), error: \(error)\n\n") + print("List directory with result:\n\(resources!), error: \(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: \(error!)\n\n") } } } @@ -53,10 +53,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func testDownload() { self.session.download("/1MB.zip") { (fileURL, error) -> Void in - print("Download file with result:\n\(fileURL), error: \(error)\n\n") + print("Download file with result:\n\(fileURL!), error: \(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 +66,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: \(error!)") } } diff --git a/rebekka-source/Rebekka.xcodeproj/project.pbxproj b/rebekka-source/Rebekka.xcodeproj/project.pbxproj index d80b1b0..a2d756e 100644 --- a/rebekka-source/Rebekka.xcodeproj/project.pbxproj +++ b/rebekka-source/Rebekka.xcodeproj/project.pbxproj @@ -194,14 +194,16 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Constantine Fry"; TargetAttributes = { 47FFC2821B09279200D3EDFE = { CreatedOnToolsVersion = 6.3.1; + LastSwiftMigration = 0800; }; 47FFC2DB1B09297E00D3EDFE = { CreatedOnToolsVersion = 6.3.1; + LastSwiftMigration = 1000; }; }; }; @@ -313,13 +315,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -362,13 +374,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -388,6 +410,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -419,6 +442,7 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -440,6 +464,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -447,6 +472,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -463,6 +489,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -470,6 +498,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -481,6 +510,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.fry.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/rebekka-source/Rebekka/FileDownloadOperation.swift b/rebekka-source/Rebekka/FileDownloadOperation.swift index 225806b..cd1c16d 100644 --- a/rebekka-source/Rebekka/FileDownloadOperation.swift +++ b/rebekka-source/Rebekka/FileDownloadOperation.swift @@ -11,15 +11,15 @@ 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? override func start() { - let filePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent(NSUUID().UUIDString) - self.fileURL = NSURL(fileURLWithPath: filePath) + let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(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 +27,32 @@ 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?) { + if let inputStream = aStream as? InputStream { var parsetBytes: Int = 0 repeat { parsetBytes = inputStream.read(self.temporaryBuffer, maxLength: 1024) if parsetBytes > 0 { autoreleasepool { - let data = NSData(bytes: self.temporaryBuffer, length: parsetBytes) - self.fileHandle!.writeData(data) + let data = Data(bytes: UnsafePointer(self.temporaryBuffer), count: parsetBytes) + self.fileHandle!.write(data) } } } while (parsetBytes > 0) diff --git a/rebekka-source/Rebekka/FileUploadOperation.swift b/rebekka-source/Rebekka/FileUploadOperation.swift index ce60ff3..199e8d3 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(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() } diff --git a/rebekka-source/Rebekka/Operation.swift b/rebekka-source/Rebekka/Operation.swift index 8000815..79552a7 100644 --- a/rebekka-source/Rebekka/Operation.swift +++ b/rebekka-source/Rebekka/Operation.swift @@ -9,37 +9,37 @@ 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/ReadStreamOperation.swift b/rebekka-source/Rebekka/ReadStreamOperation.swift index 4514232..d2e2506 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 = { - return UnsafeMutablePointer.alloc(1024) + return UnsafeMutablePointer.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() }() diff --git a/rebekka-source/Rebekka/ResourceListOperation.swift b/rebekka-source/Rebekka/ResourceListOperation.swift index 8c34583..825cfc4 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(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?>.alloc(1) + let entity = UnsafeMutablePointer?>.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(count: 1) 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,24 +116,24 @@ 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.alloc(1024) + override func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) { + if let inputStream = aStream as? InputStream { + let buffer = UnsafeMutablePointer.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(count: 1024) } return (true, nil) } diff --git a/rebekka-source/Rebekka/Session.swift b/rebekka-source/Rebekka/Session.swift index 0793cf2..a142919 100644 --- a/rebekka-source/Rebekka/Session.swift +++ b/rebekka-source/Rebekka/Session.swift @@ -9,41 +9,41 @@ 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 /** 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() + completionHandlerQueue: OperationQueue = OperationQueue.main) { + self.operationQueue = OperationQueue() self.operationQueue.maxConcurrentOperationCount = 1 self.operationQueue.name = "net.ftp.rebekka.operations.queue" - self.streamQueue = dispatch_queue_create("net.ftp.rebekka.cfstream.queue", nil) + 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 +56,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) } } @@ -76,12 +76,12 @@ public class Session { /** 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) { + open func download(_ path: String, completionHandler: @escaping FileURLResultCompletionHandler) { let operation = FileDownloadOperation(configuration: configuration, queue: self.streamQueue) operation.completionBlock = { [weak operation] in if let strongOperation = operation { - self.completionHandlerQueue.addOperationWithBlock { + self.completionHandlerQueue.addOperation { completionHandler(strongOperation.fileURL, strongOperation.error) } } @@ -91,12 +91,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) } } @@ -126,7 +126,7 @@ public struct SessionConfiguration { 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 +136,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 +150,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() { } diff --git a/rebekka-source/Rebekka/StreamOperation.swift b/rebekka-source/Rebekka/StreamOperation.swift index 28dcdd4..4383d0c 100644 --- a/rebekka-source/Rebekka/StreamOperation.swift +++ b/rebekka-source/Rebekka/StreamOperation.swift @@ -9,89 +9,89 @@ import Foundation /** The base class for stream operations. */ -internal class StreamOperation: Operation, NSStreamDelegate { - var path: String? - internal let queue: dispatch_queue_t - - private var currentStream: NSStream? - - init(configuration: SessionConfiguration, queue: dispatch_queue_t) { - 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) - stream.delegate = self - } - - func fullURL() -> NSURL { - if self.path != nil { - return self.configuration.URL().URLByAppendingPathComponent(path!) - } - return self.configuration.URL() - } - - @objc func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { - if self.cancelled { - self.streamEventError(aStream) - self.error = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil) - self.finishOperation() - return - } - - switch eventCode { - case NSStreamEvent.OpenCompleted: - self.streamEventOpenComleted(aStream) - case NSStreamEvent.HasBytesAvailable: - self.streamEventHasBytes(aStream) - case NSStreamEvent.HasSpaceAvailable: - self.streamEventHasSpace(aStream) - case NSStreamEvent.ErrorOccurred: - self.streamEventError(aStream) - self.finishOperation() - case NSStreamEvent.EndEncountered: - self.streamEventEnd(aStream) - self.finishOperation() - default: - print("Unkonwn NSStreamEvent: \(eventCode)") - } - } - - func startOperationWithStream(aStream: NSStream) { - self.currentStream = aStream - self.configureStream(self.currentStream!) - self.currentStream!.open() - self.state = .Executing - } - - func finishOperation() { - self.currentStream?.close() - self.currentStream = nil - self.state = .Finished - } - - func streamEventOpenComleted(aStream: NSStream) -> (Bool, NSError?) { - return (true, nil) - } - - func streamEventEnd(aStream: NSStream) -> (Bool, NSError?) { - return (true, nil) - } - - func streamEventHasBytes(aStream: NSStream) -> (Bool, NSError?) { - return (true, nil) - } - - func streamEventHasSpace(aStream: NSStream) -> (Bool, NSError?) { - return (true, nil) - } - - func streamEventError(aStream: NSStream) { - self.error = aStream.streamError - } +internal class StreamOperation: Operation, StreamDelegate { + var path: String? + internal let queue: DispatchQueue + + fileprivate var currentStream: Stream? + + init(configuration: SessionConfiguration, queue: DispatchQueue) { + self.queue = queue + super.init(configuration: configuration) + } + + 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() -> URL { + if self.path != nil { + return self.configuration.URL().appendingPathComponent(path!) + } + return self.configuration.URL() as URL + } + + @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() + return + } + + switch eventCode { + 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 Stream.Event.endEncountered: + let _ = self.streamEventEnd(aStream) + self.finishOperation() + default: + print("Unkonwn NSStreamEvent: \(eventCode)") + } + } + + func startOperationWithStream(_ aStream: Stream) { + self.currentStream = aStream + self.configureStream(self.currentStream!) + self.currentStream!.open() + self.state = .executing + } + + func finishOperation() { + self.currentStream?.close() + self.currentStream = nil + self.state = .finished + } + + func streamEventOpenComleted(_ aStream: Stream) -> (Bool, NSError?) { + return (true, nil) + } + + func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) { + return (true, nil) + } + + func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) { + return (true, nil) + } + + func streamEventHasSpace(_ aStream: Stream) -> (Bool, NSError?) { + return (true, nil) + } + + 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..e57515d 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 }() diff --git a/rebekka.podspec b/rebekka.podspec index 090df87..7f8f8ea 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.3" s.summary = "Rebekka - FTP/FTPS client in Swift." - s.description = <<-DESC - DESC - s.homepage = "https://github.com/Constantine-Fry/rebekka/" + s.description = "A simple FTP/FTPS client in Swift, written by Constantine Fry." + + s.homepage = "https://github.com/128keaton/rebekka/" s.license = "BSD 2-Clause License" - s.author = { "Constantine Fry" } + s.author = "Constantine Fry" - 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.ios.deployment_target = "9.0" + s.osx.deployment_target = "10.10" + s.source = { :git => "https://github.com/128keaton/rebekka.git", :tag => "1.0.3" } s.source_files = "Classes", "Classes/**/*.{h,m}" s.exclude_files = "Classes/Exclude"