Add missing documentation
This commit is contained in:
parent
72ce14b676
commit
21eee24e7c
|
@ -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?
|
||||
}
|
||||
|
|
|
@ -41,16 +41,24 @@ 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?
|
||||
|
||||
private var timer: DispatchSourceTimer?
|
||||
|
||||
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
|
||||
|
||||
|
@ -63,7 +71,10 @@ 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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue