Add missing documentation

This commit is contained in:
Davide De Rosa 2019-05-24 14:58:25 +02:00
parent 72ce14b676
commit 21eee24e7c
9 changed files with 216 additions and 11 deletions

View File

@ -36,30 +36,69 @@
import Foundation
/// Receives events from a `GenericSocket`.
public protocol GenericSocketDelegate: class {
/**
The socket timed out.
**/
func socketDidTimeout(_ socket: GenericSocket)
/**
The socket became active.
**/
func socketDidBecomeActive(_ socket: GenericSocket)
/**
The socket shut down.
- Parameter failure: `true` if the shutdown was caused by a failure.
**/
func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool)
/**
The socket has a better path.
**/
func socketHasBetterPath(_ socket: GenericSocket)
}
/// An opaque socket implementation.
public protocol GenericSocket {
/// The address of the remote endpoint.
var remoteAddress: String? { get }
/// `true` if the socket has a better path.
var hasBetterPath: Bool { get }
/// `true` if the socket was shut down.
var isShutdown: Bool { get }
/// The optional delegate for events.
var delegate: GenericSocketDelegate? { get set }
/**
Observes socket events.
- Parameter queue: The queue to observe events in.
- Parameter activeTimeout: The timeout in milliseconds for socket activity.
**/
func observe(queue: DispatchQueue, activeTimeout: Int)
/**
Stops observing socket events.
**/
func unobserve()
/**
Shuts down the socket
**/
func shutdown()
/**
Returns an upgraded socket if available (e.g. when a better path exists).
- Returns: An upgraded socket if any.
**/
func upgraded() -> GenericSocket?
}

View File

@ -41,9 +41,12 @@ import SwiftyBeaver
private let log = SwiftyBeaver.self
extension NSNotification.Name {
static let __InterfaceObserverDidDetectWifiChange = NSNotification.Name("__InterfaceObserverDidDetectWifiChange")
/// A change in Wi-Fi state occurred.
public static let InterfaceObserverDidDetectWifiChange = NSNotification.Name("InterfaceObserverDidDetectWifiChange")
}
/// Observes changes in the current Wi-Fi network.
public class InterfaceObserver: NSObject {
private var queue: DispatchQueue?
@ -51,6 +54,11 @@ public class InterfaceObserver: NSObject {
private var lastWifiName: String?
/**
Starts observing Wi-Fi updates.
- Parameter queue: The `DispatchQueue` to deliver notifications to.
**/
public func start(queue: DispatchQueue) {
self.queue = queue
@ -64,6 +72,9 @@ public class InterfaceObserver: NSObject {
self.timer = timer
}
/**
Stops observing Wi-Fi updates.
**/
public func stop() {
timer?.cancel()
timer = nil
@ -77,7 +88,7 @@ public class InterfaceObserver: NSObject {
log.debug("SSID is now '\(current.maskedDescription)'")
if let last = lastWifiName, (current != last) {
queue?.async {
NotificationCenter.default.post(name: .__InterfaceObserverDidDetectWifiChange, object: nil)
NotificationCenter.default.post(name: .InterfaceObserverDidDetectWifiChange, object: nil)
}
}
} else {
@ -87,6 +98,11 @@ public class InterfaceObserver: NSObject {
lastWifiName = currentWifiName
}
/**
Returns the current Wi-Fi SSID if any.
- Returns: The current Wi-Fi SSID if any.
**/
public func currentWifiNetworkName() -> String? {
#if os(iOS)
guard let interfaceNames = CNCopySupportedInterfaces() as? [CFString] else {

View File

@ -36,29 +36,49 @@
import Foundation
/// Error raised by `Keychain` methods.
public enum KeychainError: Error {
/// Unable to add.
case add
/// Item not found.
case notFound
case typeMismatch
// /// Unexpected item type returned.
// case typeMismatch
}
/// Wrapper for easy keychain access and modification.
public class Keychain {
private let service: String?
private let accessGroup: String?
/// :nodoc:
public init() {
service = Bundle.main.bundleIdentifier
accessGroup = nil
}
/**
Creates a keychain in an App Group.
- Parameter group: The App Group.
- Precondition: Proper App Group entitlements.
**/
public init(group: String) {
service = nil
accessGroup = group
}
/**
Creates a keychain in an App Group and a Team ID prefix.
- Parameter team: The Team ID prefix.
- Parameter group: The App Group.
- Precondition: Proper App Group entitlements.
**/
public init(team: String, group: String) {
service = nil
accessGroup = "\(team).\(group)"
@ -66,6 +86,14 @@ public class Keychain {
// MARK: Password
/**
Sets a password.
- Parameter password: The password to set.
- Parameter username: The username to set the password for.
- Parameter label: An optional label.
- Throws: `KeychainError.add` if unable to add the password to the keychain.
**/
public func set(password: String, for username: String, label: String? = nil) throws {
do {
let currentPassword = try self.password(for: username)
@ -94,6 +122,12 @@ public class Keychain {
}
}
/**
Removes a password.
- Parameter username: The username to remove the password for.
- Returns: `true` if the password was successfully removed.
**/
@discardableResult public func removePassword(for username: String) -> Bool {
var query = [String: Any]()
setScope(query: &query)
@ -104,6 +138,13 @@ public class Keychain {
return (status == errSecSuccess)
}
/**
Gets a password.
- Parameter username: The username to get the password for.
- Returns: The password for the input username.
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
**/
public func password(for username: String) throws -> String {
var query = [String: Any]()
setScope(query: &query)
@ -126,6 +167,13 @@ public class Keychain {
return password
}
/**
Gets a password reference.
- Parameter username: The username to get the password for.
- Returns: The password reference for the input username.
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
**/
public func passwordReference(for username: String) throws -> Data {
var query = [String: Any]()
setScope(query: &query)
@ -145,6 +193,14 @@ public class Keychain {
return data
}
/**
Gets a password associated with a password reference.
- Parameter username: The username to get the password for.
- Parameter reference: The password reference.
- Returns: The password for the input username and reference.
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
**/
public static func password(for username: String, reference: Data) throws -> String {
var query = [String: Any]()
query[kSecClass as String] = kSecClassGenericPassword
@ -170,6 +226,14 @@ public class Keychain {
// https://forums.developer.apple.com/thread/13748
/**
Adds a public key.
- Parameter identifier: The unique identifier.
- Parameter data: The public key data.
- Returns: The `SecKey` object representing the public key.
- Throws: `KeychainError.add` if unable to add the public key to the keychain.
**/
public func add(publicKeyWithIdentifier identifier: String, data: Data) throws -> SecKey {
var query = [String: Any]()
query[kSecClass as String] = kSecClassKey
@ -188,6 +252,13 @@ public class Keychain {
return try publicKey(withIdentifier: identifier)
}
/**
Gets a public key.
- Parameter identifier: The unique identifier.
- Returns: The `SecKey` object representing the public key.
- Throws: `KeychainError.notFound` if unable to find the public key in the keychain.
**/
public func publicKey(withIdentifier identifier: String) throws -> SecKey {
var query = [String: Any]()
query[kSecClass as String] = kSecClassKey
@ -211,6 +282,12 @@ public class Keychain {
return result as! SecKey
}
/**
Removes a public key.
- Parameter identifier: The unique identifier.
- Returns: `true` if the public key was successfully removed.
**/
@discardableResult public func remove(publicKeyWithIdentifier identifier: String) -> Bool {
var query = [String: Any]()
query[kSecClass as String] = kSecClassKey

View File

@ -25,6 +25,13 @@
import Foundation
/// Entity able to produce a `LinkInterface`.
public protocol LinkProducer {
/**
Returns a `LinkInterface`.
- Parameter mtu: The MTU value.
**/
func link(withMTU mtu: Int) -> LinkInterface
}

View File

@ -37,22 +37,35 @@
import Foundation
import SwiftyBeaver
/// Implements a `SwiftyBeaver.BaseDestination` logging to a memory buffer.
public class MemoryDestination: BaseDestination, CustomStringConvertible {
private var buffer: [String] = []
/// Max number of retained lines.
public var maxLines: Int?
/// :nodoc:
public override init() {
super.init()
asynchronously = false
}
public func start(with existing: [String]) {
/**
Starts logging. Optionally prepend an array of lines.
- Parameter existing: The optional lines to prepend (none by default).
**/
public func start(with existing: [String] = []) {
execute(synchronously: true) {
self.buffer = existing
}
}
/**
Flushes the log content to an URL.
- Parameter url: The URL to write the log content to.
**/
public func flush(to url: URL) {
execute(synchronously: true) {
let content = self.buffer.joined(separator: "\n")
@ -63,6 +76,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
// MARK: BaseDestination
// XXX: executed in SwiftyBeaver queue. DO NOT invoke execute* here (sync in sync would crash otherwise)
/// :nodoc:
public override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int, context: Any?) -> String? {
guard let message = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) else {
return nil
@ -78,6 +92,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
// MARK: CustomStringConvertible
/// :nodoc:
public var description: String {
return executeSynchronously {
return self.buffer.joined(separator: "\n")

View File

@ -40,11 +40,14 @@ import SwiftyBeaver
private let log = SwiftyBeaver.self
/// TCP implementation of a `GenericSocket` via NetworkExtension.
public class NETCPSocket: NSObject, GenericSocket {
private static var linkContext = 0
/// :nodoc:
public let impl: NWTCPConnection
/// :nodoc:
public init(impl: NWTCPConnection) {
self.impl = impl
isActive = false
@ -57,18 +60,23 @@ public class NETCPSocket: NSObject, GenericSocket {
private var isActive: Bool
/// :nodoc:
public private(set) var isShutdown: Bool
/// :nodoc:
public var remoteAddress: String? {
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
}
/// :nodoc:
public var hasBetterPath: Bool {
return impl.hasBetterPath
}
/// :nodoc:
public weak var delegate: GenericSocketDelegate?
/// :nodoc:
public func observe(queue: DispatchQueue, activeTimeout: Int) {
isActive = false
@ -86,16 +94,19 @@ public class NETCPSocket: NSObject, GenericSocket {
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
}
/// :nodoc:
public func unobserve() {
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), context: &NETCPSocket.linkContext)
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), context: &NETCPSocket.linkContext)
}
/// :nodoc:
public func shutdown() {
impl.writeClose()
impl.cancel()
}
/// :nodoc:
public func upgraded() -> GenericSocket? {
guard impl.hasBetterPath else {
return nil
@ -105,6 +116,7 @@ public class NETCPSocket: NSObject, GenericSocket {
// MARK: Connection KVO (any queue)
/// :nodoc:
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard (context == &NETCPSocket.linkContext) else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)

View File

@ -37,11 +37,13 @@
import Foundation
import NetworkExtension
/// `TunnelInterface` implementation via NetworkExtension.
public class NETunnelInterface: TunnelInterface {
private weak var impl: NEPacketTunnelFlow?
private let protocolNumber: NSNumber
/// :nodoc:
public init(impl: NEPacketTunnelFlow, isIPv6: Bool) {
self.impl = impl
protocolNumber = (isIPv6 ? AF_INET6 : AF_INET) as NSNumber
@ -49,12 +51,14 @@ public class NETunnelInterface: TunnelInterface {
// MARK: TunnelInterface
/// :nodoc:
public var isPersistent: Bool {
return false
}
// MARK: IOInterface
/// :nodoc:
public func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
loopReadPackets(queue, handler)
}
@ -70,11 +74,13 @@ public class NETunnelInterface: TunnelInterface {
}
}
/// :nodoc:
public func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
impl?.writePackets([packet], withProtocols: [protocolNumber])
completionHandler?(nil)
}
/// :nodoc:
public func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
let protocols = [NSNumber](repeating: protocolNumber, count: packets.count)
impl?.writePackets(packets, withProtocols: protocols)

View File

@ -40,11 +40,14 @@ import SwiftyBeaver
private let log = SwiftyBeaver.self
/// UDP implementation of a `GenericSocket` via NetworkExtension.
public class NEUDPSocket: NSObject, GenericSocket {
private static var linkContext = 0
/// :nodoc:
public let impl: NWUDPSession
/// :nodoc:
public init(impl: NWUDPSession) {
self.impl = impl
@ -58,18 +61,23 @@ public class NEUDPSocket: NSObject, GenericSocket {
private var isActive: Bool
/// :nodoc:
public private(set) var isShutdown: Bool
/// :nodoc:
public var remoteAddress: String? {
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
}
/// :nodoc:
public var hasBetterPath: Bool {
return impl.hasBetterPath
}
/// :nodoc:
public weak var delegate: GenericSocketDelegate?
/// :nodoc:
public func observe(queue: DispatchQueue, activeTimeout: Int) {
isActive = false
@ -87,15 +95,18 @@ public class NEUDPSocket: NSObject, GenericSocket {
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
}
/// :nodoc:
public func unobserve() {
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state), context: &NEUDPSocket.linkContext)
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), context: &NEUDPSocket.linkContext)
}
/// :nodoc:
public func shutdown() {
impl.cancel()
}
/// :nodoc:
public func upgraded() -> GenericSocket? {
guard impl.hasBetterPath else {
return nil
@ -105,6 +116,7 @@ public class NEUDPSocket: NSObject, GenericSocket {
// MARK: Connection KVO (any queue)
/// :nodoc:
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard (context == &NEUDPSocket.linkContext) else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)

View File

@ -36,9 +36,18 @@
import Foundation
/// Convenient methods for DNS resolution.
public class DNSResolver {
private static let queue = DispatchQueue(label: "DNSResolver")
/**
Resolves a hostname asynchronously.
- Parameter hostname: The hostname to resolve.
- Parameter timeout: The timeout in milliseconds.
- Parameter queue: The queue to execute the `completionHandler` in.
- Parameter completionHandler: The completion handler with the resolved addresses and an optional error.
*/
public static func resolve(_ hostname: String, timeout: Int, queue: DispatchQueue, completionHandler: @escaping ([String]?, Error?) -> Void) {
var pendingHandler: (([String]?, Error?) -> Void)? = completionHandler
let host = CFHostCreateWithName(nil, hostname as CFString).takeRetainedValue()
@ -94,6 +103,12 @@ public class DNSResolver {
completionHandler(ipAddresses, nil)
}
/**
Returns a `String` representation from a numeric IPv4 address.
- Parameter ipv4: The IPv4 address as a 32-bit number.
- Returns: The string representation of `ipv4`.
*/
public static func string(fromIPv4 ipv4: UInt32) -> String {
var addr = in_addr(s_addr: CFSwapInt32HostToBig(ipv4))
var buf = Data(count: Int(INET_ADDRSTRLEN))
@ -110,6 +125,12 @@ public class DNSResolver {
return String(cString: result)
}
/**
Returns a numeric representation from an IPv4 address.
- Parameter string: The IPv4 address as a string.
- Returns: The numeric representation of `string`.
*/
public static func ipv4(fromString string: String) -> UInt32? {
var addr = in_addr()
let result = string.withCString {