├── .gitignore ├── Package.swift ├── README.md ├── Sources ├── CartesianProduct.swift ├── SwiftUtility+CharacterSet.swift ├── SwiftUtility+Collection.swift ├── SwiftUtility+CoreError.swift ├── SwiftUtility+Diagnose.swift ├── SwiftUtility+Dispatch.swift ├── SwiftUtility+Math.swift ├── SwiftUtility+Nostalgia.swift ├── SwiftUtility+Optional.swift ├── SwiftUtility+OptionalComparison.swift ├── SwiftUtility+Precedence.swift ├── SwiftUtility+Reflection.swift ├── SwiftUtility+Splatting.swift ├── SwiftUtility+TimeTest.swift ├── SwiftUtility+Unimplemented.swift └── SwiftUtility+With.swift └── Various ├── Notes.swift ├── SwiftUtility+Interesting.swift └── SwiftUtility+Operatorbase.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary files. 2 | *~ 3 | 4 | # Xcode user data. 5 | xcuserdata 6 | 7 | # Finder metadata 8 | .DS_Store 9 | 10 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "SwiftUtility" 5 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Utility 2 | 3 | Useful stuff. BSD. Use at your own risk. 4 | 5 | A lot of these are "notes" and have not been tested 6 | 7 | # Cartesian Product 8 | * What it says on the label 9 | 10 | # CharacterSet 11 | * Don't know if I'll keep this long term, but character set members 12 | 13 | # Collection 14 | * Useful stuff, not fully updated to Swift 4 See "TODO"s 15 | 16 | # Core Error 17 | * Basic utility error 18 | * Contextualizing 19 | * ContextError 20 | * Common Error Handler type 21 | * `attempt` (replaces `try?` and `try!`) and Boolean variation `testAttempt` 22 | 23 | # Diagnosis 24 | * Postfix printing 25 | * Also debug-only version, which requires compilation flags 26 | * Also `dump`-enabled high octane versions 27 | * Diagnostic printing 28 | * `here`: The current file and line number 29 | * `diagnose(object)`: shows the object in detail with pass-through 30 | * Infix printing 31 | * offers low-precedence forward-looking print or dump functionality 32 | 33 | # Dispatch 34 | * General timing and invocation utilities 35 | 36 | # Math 37 | * Exponentiation 38 | * Double unification 39 | 40 | # Nostalgia 41 | * Prefix, Postfix increment/decrement 42 | 43 | # Optional 44 | * Controlled landings for forced unwraps 45 | * `do` instead of optional.map(action) 46 | * Conditional map/flatmap application 47 | * Zipping optionals / flatmap2 for optionals 48 | 49 | # Optional Comparison 50 | * What it says on the box. Follows IEEE754 wrt NaN 51 | 52 | # Precedence 53 | * Custom items including very low and very high 54 | 55 | # Reflection 56 | * Self-reflecting reference types 57 | 58 | # Splatting 59 | * 2-, 3-, 4- item 60 | 61 | # TimeTest 62 | * Avoid using in playgrounds 63 | 64 | # Unimplemented 65 | * Clean line/source info 66 | 67 | # With 68 | * Flexible initialization and immutable assignment 69 | 70 | # Moved from Sources 71 | Most operators have been moved out of the main sources into `Various` 72 | * Casting 73 | * Force cast to the type demanded by static context (Groffcast) 74 | * Unsafe bitcast constrained to equal memory footprint (Ashcast) 75 | * Dynamic type dispatch (Dynogroff) 76 | * Dynamic casting without bridging (Groffchoo) 77 | * Assignment 78 | * In-place value assignment (EridiusAssignment) 79 | * Conditional in-place assignment (Ashignment) 80 | * Failable and direct chaining (ChainChainChainOfFools operator) 81 | * Interesting things that caught my eye 82 | * Notes on stuff that I'm looking at at the moment 83 | -------------------------------------------------------------------------------- /Sources/CartesianProduct.swift: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/erica/0aad0d8fc0b7a982e542c4b28ce53fc5 2 | 3 | /// Creates a sequence of pairs built of the Cartesian product of two 4 | /// underlying sequences. 5 | /// - Parameter seq1: The first sequence or collection 6 | /// - Parameter seq2: The second sequence or collection 7 | /// - Returns: A sequence of tuple pairs, where the elements of each pair are 8 | /// corresponding elements of `Seq1` and `Seq2`. 9 | /// - Warning: `seq2` must be a multipass (not single-pass) sequence 10 | public func product 11 | (_ seq1: Seq1, _ seq2: Seq2) -> CartesianSequence 12 | { 13 | return CartesianSequence(seq1, seq2) 14 | } 15 | 16 | /// A sequence of pairs built out of the Cartesian product of 17 | /// two underlying sequences. 18 | public struct CartesianSequence : Sequence 19 | { 20 | /// A type whose instances produce the ordered elements of this sequence 21 | public typealias Iterator = CartesianIterator 22 | 23 | /// Returns an iterator over the elements of this sequence. 24 | public func makeIterator() -> CartesianIterator { 25 | return Iterator(_seq1, _seq2) 26 | } 27 | 28 | /// Creates an instance that makes pairs of elements from the 29 | /// Cartesian product of `seq1` and `seq2`. 30 | public init(_ seq1: Seq1, _ seq2: Seq2) { 31 | (_seq1, _seq2) = (seq1, seq2) 32 | } 33 | 34 | internal let _seq1: Seq1 35 | internal let _seq2: Seq2 36 | } 37 | 38 | /// An iterator for `CartesianSequence`. 39 | public struct CartesianIterator: IteratorProtocol 40 | { 41 | /// The type of element returned by `next()`. 42 | public typealias Element = (Seq1.Iterator.Element, Seq2.Iterator.Element) 43 | 44 | /// Creates an instance around a pair of underlying iterators. 45 | internal init(_ seq1: Seq1, _ seq2: Seq2) { 46 | let _sequence = seq1.lazy.flatMap ({ 47 | item1 in seq2.lazy.map ({ 48 | item2 in (item1, item2) 49 | }) 50 | }) 51 | _iterator = _sequence.makeIterator() 52 | } 53 | 54 | /// Advances to the next element and returns it 55 | public mutating func next() -> 56 | (Seq1.Iterator.Element, Seq2.Iterator.Element)? { 57 | return _iterator.next() 58 | } 59 | 60 | internal var _iterator: FlattenIterator< 61 | LazyMapIterator>> 63 | } 64 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+CharacterSet.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension CharacterSet { 4 | /// Returns member count 5 | public var count: Int { 6 | var count = 0 7 | for i in 0 ..< 8192 * 16 { 8 | if 9 | let scalar = UnicodeScalar(i), 10 | contains(scalar) { 11 | count = count + 1 12 | } 13 | } 14 | return count 15 | } 16 | 17 | /// Returns set of members, presented as strings 18 | public var members: Set { 19 | var set: Set = [] 20 | for i in 0 ..< 8192 * 16 { 21 | if 22 | let scalar = UnicodeScalar(i), 23 | contains(scalar) { 24 | set.insert(String(scalar)) 25 | } 26 | } 27 | return set 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Collection.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // -------------------------------------------------- 4 | // MARK: Collections 5 | // -------------------------------------------------- 6 | 7 | /// Return the final index in a collection 8 | /// that satisfies a given predicate 9 | func finalIndex( 10 | of collection: T, 11 | where predicate: 12 | (T.Iterator.Element) -> Bool) -> T.Index? 13 | // where T.Index == T.Indices.Iterator.Element 14 | { 15 | for idx in collection.indices.reversed() { 16 | if predicate(collection[idx]) { 17 | return idx 18 | } 19 | } 20 | return nil 21 | } 22 | 23 | extension Collection 24 | // where Index == Indices.Iterator.Element 25 | { 26 | /// Return the final index in a collection 27 | /// that satisfies a given predicate 28 | func finalIndex(where predicate: 29 | (Self.Iterator.Element) -> Bool) -> Self.Index? 30 | { 31 | for idx in indices.reversed() { 32 | if predicate(self[idx]) { 33 | return idx 34 | } 35 | } 36 | return nil 37 | } 38 | } 39 | 40 | 41 | // -------------------------------------------------- 42 | // MARK: Fallback 43 | // -------------------------------------------------- 44 | extension Dictionary { 45 | /// Returns non-optional coalescing to fallback value 46 | subscript(key: Key, fallback fallback: Value) -> Value { 47 | return self[key] ?? fallback 48 | } 49 | } 50 | 51 | // -------------------------------------------------- 52 | // MARK: Wrapping 53 | // -------------------------------------------------- 54 | 55 | extension Array { 56 | public subscript(wrap index: Int) -> Element? { 57 | guard !isEmpty else { return nil } 58 | // Support negative indices via modulo and 59 | // adding `count` 60 | return self[(index % count + count) % count] 61 | } 62 | } 63 | 64 | // -------------------------------------------------- 65 | // MARK: Safety 66 | // -------------------------------------------------- 67 | 68 | extension Collection // where 69 | // Indices.Iterator.Element: Equatable, 70 | // Index == Self.Indices.Iterator.Element 71 | { 72 | /// Returns optional value for safely indexed value 73 | /// and nil if index is out of bounds 74 | public subscript(safe index: Self.Index) -> Self.Iterator.Element? { 75 | guard indices.contains(index) else { return nil } 76 | return self[index] 77 | } 78 | 79 | // FIXME: Broken needs fixing 80 | // /// Returns objects safely collected at existing desired indices. 81 | // public func objects(collectedAt desiredIndices: Self.Index...) -> [Self.Iterator.Element] { 82 | // return desiredIndices.flatMap({ self[safe: $0] }) 83 | // } 84 | 85 | // FIXME: Broken needs fixing 86 | // /// Returns objects safely found at existing desired indices. 87 | // public func objects(safelyAt desiredIndices: Self.Index...) -> [Self.Iterator.Element?] { 88 | // return desiredIndices.map({ self[safe: $0] }) 89 | // } 90 | } 91 | 92 | 93 | // -------------------------------------------------- 94 | // MARK: Indexing 95 | // -------------------------------------------------- 96 | 97 | 98 | // FIXME: Broken needs fixing 99 | //extension Collection { 100 | // /// Returns objects found at existing desired indices. 101 | // /// No safety guarantees 102 | // public func objects(at desiredIndices: Self.Index...) -> [Self.Iterator.Element] { 103 | // return desiredIndices.map({ self[$0] }) 104 | // } 105 | //} 106 | 107 | // FIXME: Broken needs fixing 108 | //extension Collection { 109 | // /// Returns objects found at desired subscript indices. 110 | // /// No safety guarantees 111 | // subscript(_ idx1: Self.Index, _ idx2: Self.Index, _ rest: Self.Index...) -> [Self.Iterator.Element] { 112 | // return [self[idx1], self[idx2]] + rest.lazy.map({ self[$0] }) 113 | // } 114 | //} 115 | 116 | // Also from Kevin B 117 | /* 118 | extension Collection { 119 | subscript(first: Index, second: Index, rest: Index...) -> [Iterator.Element] { 120 | var results: [Iterator.Element] = [] 121 | results.reserveCapacity(rest.count + 2) 122 | results.append(self[first]) 123 | results.append(self[second]) 124 | results.append(rest.lazy.map({ self[$0] })) 125 | return results 126 | } 127 | } 128 | */ 129 | 130 | // From Kevin B. 131 | extension Collection where Index == Int { 132 | subscript(indices: IndexSet) -> [Iterator.Element] { 133 | return indices.map({ self[$0] }) 134 | } 135 | } 136 | 137 | // FIXME: Broken needs fixing 138 | //extension Dictionary { 139 | // /// Returns multiply-scripted dictionary as array of optionals 140 | // subscript(_ idx1: Key, _ idx2: Key, _ rest: Key...) -> [Value?] { 141 | // return [self[idx1], self[idx2]] + rest.lazy.map({ self[$0] }) 142 | // } 143 | //} 144 | 145 | extension Collection { 146 | /// Returns a sequence of pairs (*idx*, *x*), where *idx* represents a 147 | /// consecutive collection index, and *x* represents an element of 148 | /// the sequence. 149 | func indexed() -> Zip2Sequence { 150 | return zip(indices, self) 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+CoreError.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Erica Sadun, http://ericasadun.com 4 | Basic Errors 5 | 6 | */ 7 | 8 | import Foundation 9 | 10 | /// A basic utility error type. It stores a reason for 11 | /// a failed operation and the context under which the error 12 | /// has occurred 13 | public struct CoreError: Error { 14 | 15 | let (reason, context): (String, String) 16 | 17 | public init( 18 | _ reason: String, 19 | _ context: String = "", 20 | fileName: String = #file, 21 | lineNumber: Int = #line 22 | ) 23 | { 24 | /// Establishes a context if one is not supplied by the caller 25 | let derivedContext = context.isEmpty 26 | ? (fileName as NSString).lastPathComponent + ":\(lineNumber)" 27 | : context 28 | 29 | // Initialize with supplied reason and derived or supplied context 30 | (self.reason, self.context) = (reason, derivedContext) 31 | } 32 | } 33 | 34 | /// On adopting Contextualizable, constructs can build errors 35 | /// specific to their calling conditions 36 | public protocol Contextualizable {} 37 | 38 | // Introduces a default implementation to support context-dependent 39 | // error building 40 | public extension Contextualizable { 41 | 42 | /// Creates a context error at the point of failure, picking 43 | /// up the file, function, and line of the error event 44 | public func BuildContextError( 45 | _ items: Any..., 46 | fileName: String = #file, 47 | function: String = #function, 48 | line: Int = #line 49 | ) -> CoreError 50 | { 51 | /// Caller supplies one or more instances 52 | /// as "reasons", which need not be strings. Their default 53 | /// representation will be added to the 'reasons' list. 54 | let reasons = items.map({ "\($0)" }).joined(separator: ", ") 55 | 56 | /// Trimmed file name, derived from calling context 57 | let coreFileName = (fileName as NSString).lastPathComponent 58 | 59 | /// Calling context composed of function, type, file name, line 60 | let context = "\(function):\(type(of: self)):\(coreFileName):\(line) " 61 | 62 | // Produce and return a core error 63 | return CoreError(reasons, context) 64 | } 65 | } 66 | 67 | /// A lighter weight alternative to Contextualizable that also 68 | /// picks up file and line context for error handling 69 | /// 70 | /// - parameter items: Caller supplies one or more instances 71 | /// as "reasons", which need not be strings. Their default 72 | /// representation will be added to the 'reasons' list. 73 | public func ContextError( 74 | _ items: Any..., 75 | fileName: String = #file, 76 | lineNumber: Int = #line 77 | ) -> CoreError 78 | { 79 | /// Munges supplied items into a single compound "reason" 80 | /// describing the reasons the error took place 81 | let reasons = items.map({ "\($0)" }).joined(separator: ", ") 82 | 83 | /// Trimmed file name, derived from calling context 84 | let coreFileName = (fileName as NSString).lastPathComponent 85 | 86 | /// Establish the context 87 | let context = "\(coreFileName):\(lineNumber) " 88 | 89 | // Produce and return a core error 90 | return CoreError(reasons, context) 91 | } 92 | 93 | /// consists of file path, line number, error tuple 94 | public typealias CommonErrorHandlerType = (String, Int, Error) -> Void 95 | 96 | /// Default error handler prints context and error 97 | public let defaultCommonErrorHandler: CommonErrorHandlerType = { 98 | filePath, lineNumber, error in 99 | let trimmedFileName: String = (filePath as NSString).lastPathComponent 100 | print("Error \(trimmedFileName):\(lineNumber) \(error)") 101 | } 102 | 103 | /// Replacement for `try?` that introduces an error handler 104 | /// The default error handler prints an error before returning nil 105 | /// 106 | /// - Parameter file: source file, derived from `__FILE__` context literal 107 | /// - Parameter line: source line, derived from `__LINE__` context literal 108 | /// - Parameter crashOnError: defaults to false. When set to true 109 | /// will raise a fatal error, emulating try! instead of try? 110 | /// - Parameter errorHandler: processes the error, returns nil 111 | /// 112 | /// ```swift 113 | /// // Void example, will fail 114 | /// attempt { 115 | /// let mgr = NSFileManager.defaultManager() 116 | /// try mgr.createDirectoryAtPath( 117 | /// "/Users/notarealuser", 118 | /// withIntermediateDirectories: true, 119 | /// attributes: nil) 120 | /// } 121 | /// 122 | /// // Return example, will fail 123 | /// let x = attempt { 124 | /// _ -> [NSURL] in 125 | /// let url = NSURL(fileURLWithPath: "/Not/Real") 126 | /// return try NSFileManager 127 | /// .defaultManager() 128 | /// .contentsOfDirectoryAtURL(url, includingPropertiesForKeys: nil, options: []) 129 | /// } 130 | /// 131 | /// /// Return example, will succeed 132 | /// let y = attempt { 133 | /// _ -> [NSURL] in 134 | /// let url = NSBundle.mainBundle().bundleURL 135 | /// return try NSFileManager 136 | /// .defaultManager() 137 | /// .contentsOfDirectoryAtURL(url, includingPropertiesForKeys: nil, options: []) 138 | /// } 139 | /// ``` 140 | /// 141 | public func attempt( 142 | file fileName: String = #file, 143 | line lineNumber: Int = #line, 144 | crashOnError: Bool = false, 145 | errorHandler: CommonErrorHandlerType = defaultCommonErrorHandler, 146 | closure: () throws -> T) -> T? { 147 | 148 | do { 149 | // Return executes only if closure succeeds, returning T 150 | return try closure() 151 | 152 | } catch { 153 | // Emulate try! by crashing 154 | if crashOnError { 155 | print("Fatal error \(fileName):\(lineNumber): \(error)") 156 | fatalError() 157 | } 158 | 159 | // Execute error handler and return nil 160 | errorHandler(fileName, lineNumber, error) 161 | return nil 162 | } 163 | } 164 | 165 | /// Alternative to attempt that ignores any results and returns 166 | /// a Boolean value indicating success 167 | public func testAttempt( 168 | file fileName: String = #file, 169 | line lineNumber: Int = #line, 170 | crashOnError: Bool = false, 171 | closure: () throws -> T 172 | ) -> Bool 173 | { 174 | /// Throw away result but check for non-nil. Thanks nuclearace 175 | return attempt( 176 | file: fileName, 177 | line: lineNumber, 178 | crashOnError: crashOnError, 179 | closure: closure 180 | ) == nil ? false : true 181 | } 182 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Diagnose.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /* 4 | There is no problem so big so strong so bad that it cannot be solved with a surfeit of print statements 5 | */ 6 | 7 | // ----------------------------------------------------------- 8 | // Debug-only Printing 9 | // ----------------------------------------------------------- 10 | 11 | func debugOnlyPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") { 12 | #if debug 13 | print(items.map({ "\($0)" }).joined(separator: separator), terminator: terminator) 14 | #endif 15 | } 16 | 17 | 18 | //----------------------------------------------------------- 19 | // MARK: Pass-through Postfix Printing 20 | //----------------------------------------------------------- 21 | 22 | postfix operator *? 23 | 24 | /// Postfix printing for quick playground tests 25 | public postfix func *?(object: T) -> T { 26 | print(object); return object 27 | } 28 | 29 | postfix operator **? 30 | 31 | /// Debug-only postfix printing 32 | public postfix func **? (object: T) -> T { 33 | debugOnlyPrint(object) 34 | return object 35 | } 36 | 37 | postfix operator *?! 38 | 39 | /// Postfix printing for quick playground tests 40 | public postfix func *?!(object: T) -> T { 41 | return dump(object) 42 | } 43 | 44 | postfix operator **?! 45 | 46 | /// Debug-only postfix printing 47 | public postfix func **?! (object: T) -> T { 48 | #if debug 49 | dump(object) 50 | #endif 51 | return object 52 | } 53 | 54 | //----------------------------------------------------------- 55 | // MARK: Diagnostic Output 56 | //----------------------------------------------------------- 57 | 58 | /// Shows the current source file and line 59 | /// Follow with ?* or ?*! to print/dump the item being looked at 60 | /// ``` 61 | /// guard 62 | /// here() ?* !histogram.isEmpty, 63 | /// let image = here() ?* BarChart(numbers: histogram) 64 | /// ``` 65 | /// alternatively, stick at the end of lines to see where 66 | /// evaluation stops 67 | /// ``` 68 | /// if 69 | /// let json = try JSONSerialization 70 | /// .jsonObject(with: data, options: []) as? NSDictionary, here("JSON"), 71 | /// let resultsList = json["results"] as? NSArray, here("resultsList") 72 | /// ``` 73 | 74 | func here(_ note: String = "", file: String = #file, 75 | line: Int = #line, cr: Bool = false) -> Bool 76 | { 77 | let filename = (file as NSString).lastPathComponent 78 | print( 79 | "[\(filename):\(line)\(note.isEmpty ? "" : " " + note)] ", 80 | terminator: cr ? "\n" : "") 81 | return true 82 | } 83 | 84 | /// Diagnoses state in compound conditions. 85 | /// Always returns true, so it does not interfere with 86 | /// the condition progression. 87 | /// 88 | /// ``` 89 | /// if 90 | /// let json = try JSONSerialization 91 | /// .jsonObject(with: data, options: []) as? NSDictionary, 92 | /// diagnose(json), 93 | /// let resultsList = json["results"] as? NSArray, 94 | /// diagnose(resultsList), 95 | /// ``` 96 | func diagnose(_ item: Any?) -> Bool 97 | { 98 | if let item = item { print(item) } 99 | else { print("nil") } 100 | return true 101 | } 102 | 103 | 104 | //----------------------------------------------------------- 105 | // MARK: Diagnostic Infix Printing 106 | //----------------------------------------------------------- 107 | 108 | /// An operator alternative to diagnose, which can be a pain to type. 109 | /// These use a low precedence infix operator to allow the entire 110 | /// condition to be evaluated and printed before assignment. 111 | /// Place the argument and operator inline before the condition. 112 | /// ``` 113 | /// guard let x = here() ?* complex condition statement, ... 114 | /// ``` 115 | /// Using here() as the first argument prints the location 116 | /// in code before peforming the dump or print 117 | 118 | 119 | infix operator ?* : VeryLowPrecedence 120 | infix operator ?*! : VeryLowPrecedence 121 | 122 | /// Quick print the following item. Infix operator 123 | /// so this requires some value (typically `here()`) 124 | /// to follow onto. If you don't want to print a location, 125 | /// you can put any dummy value before the ?*. 126 | func ?*(_: Any, item: T) -> T { 127 | print(item); return item 128 | } 129 | 130 | /// Fully dump the following item. Infix operator 131 | /// so this requires some value (typically `here()`) 132 | /// to follow onto. If you don't want to print a location, 133 | /// you can put any dummy value before the ?*. 134 | func ?*! (_: Any, item: T) -> T { 135 | dump(item); return item 136 | } 137 | 138 | 139 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Dispatch.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Executes closure on global queue (not main) after a delay in seconds 4 | public func perform(after delay: Double, _ action: @escaping () -> Void) { 5 | DispatchQueue 6 | .global(qos: .default) 7 | .asyncAfter(deadline: .now() + delay, execute: action) 8 | } 9 | 10 | /// Executes closure on global queue (not main) after a delay 11 | public func perform(after delay: DispatchTimeInterval, _ action: @escaping () -> Void) { 12 | DispatchQueue 13 | .global(qos: .default) 14 | .asyncAfter(deadline: .now() + delay, execute: action) 15 | } 16 | 17 | /// Executes closure on main queue after a delay in seconds 18 | public func performOnMain(after delay: Double, _ action: @escaping () -> Void) { 19 | DispatchQueue 20 | .main 21 | .asyncAfter(deadline: .now() + delay, execute: action) 22 | } 23 | 24 | /// Executes closure on main queue after a delay 25 | public func performOnMain(after delay: DispatchTimeInterval, _ action: @escaping () -> Void) { 26 | DispatchQueue 27 | .main 28 | .asyncAfter(deadline: .now() + delay, execute: action) 29 | } 30 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Math.swift: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // MARK: - EXPONENTIATION 3 | // -------------------------------------------------- 4 | 5 | import Foundation 6 | 7 | /// Exponentiation operator 8 | infix operator ** 9 | 10 | extension SignedInteger { 11 | /// Returns base ^^ exp 12 | /// - parameter base: the base value 13 | /// - parameter exp: the exponentiation value 14 | static func **(base: Self, exp: Int) -> Self { 15 | return repeatElement(base, count: exp).reduce(1 as! Self, *) 16 | } 17 | } 18 | 19 | extension FloatingPoint { 20 | /// Returns base ^^ exp 21 | /// - parameter base: the base value 22 | /// - parameter exp: the exponentiation value 23 | static func **(base: Self, exp: Int) -> Self { 24 | return repeatElement(base, count: exp).reduce(1, *) 25 | } 26 | } 27 | 28 | extension Double { 29 | /// Returns base ^^ exp 30 | /// - parameter base: the base value 31 | /// - parameter exp: the exponentiation value 32 | static func **(base: Double, exp: Double) -> Double { 33 | return pow(base, exp) 34 | } 35 | 36 | /// Returns base ^^ exp 37 | /// - parameter base: the base value 38 | /// - parameter exp: the exponentiation value 39 | static func **(base: Int, exp: Double) -> Double { 40 | return pow(Double(base), exp) 41 | } 42 | } 43 | 44 | // -------------------------------------------------- 45 | // MARK: - Double Conversion 46 | // -------------------------------------------------- 47 | 48 | extension BinaryFloatingPoint { 49 | public var doubleValue: Double { 50 | guard !(self is Double) else { return self as! Double } 51 | guard !(self is Float) else { return Double(self as! Float) } 52 | guard !(self is Float80) else { return Double(self as! Float80) } 53 | guard !(self is CGFloat) else { return Double(self as! CGFloat) } 54 | fatalError("Unsupported floating point type") 55 | } 56 | 57 | public var intValue: Int { return lrint(self.doubleValue) } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Nostalgia.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ericasadun.com 4 | Sometimes letting go doesn't mean saying goodbye 5 | 6 | See: https://bugs.swift.org/browse/SR-1692 7 | 8 | Related: 9 | ``` 10 | public func _sequence( 11 | first: T, next: (T) -> T?, 12 | while test: (T) -> Bool) -> UnfoldSequence 13 | { 14 | return sequence(first: first, next: next).prefix(while: test) 15 | } 16 | ``` 17 | */ 18 | 19 | prefix operator ++ 20 | prefix operator -- 21 | postfix operator ++ 22 | postfix operator -- 23 | 24 | @discardableResult 25 | prefix public func ++(x: inout Int) -> Int { x = x + 1; return x } 26 | @discardableResult 27 | prefix public func ++(x: inout UInt) -> UInt { x = x + 1; return x } 28 | @discardableResult 29 | prefix public func --(x: inout Int) -> Int { x = x - 1; return x } 30 | @discardableResult 31 | prefix public func --(x: inout UInt) -> UInt { x = x - 1; return x } 32 | 33 | 34 | @discardableResult 35 | postfix public func ++(x: inout Int) -> Int { defer { x = x + 1 }; return x } 36 | @discardableResult 37 | postfix public func ++(x: inout UInt) -> UInt { defer { x = x + 1 }; return x } 38 | @discardableResult 39 | postfix public func --(x: inout Int) -> Int { defer { x = x - 1 }; return x } 40 | @discardableResult 41 | postfix public func --(x: inout UInt) -> UInt { defer { x = x - 1 }; return x } 42 | 43 | @discardableResult 44 | prefix public func ++(x: inout Int16) -> Int16 { x = x + 1; return x } 45 | @discardableResult 46 | prefix public func ++(x: inout UInt16) -> UInt16 { x = x + 1; return x } 47 | @discardableResult 48 | prefix public func --(x: inout Int16) -> Int16 { x = x - 1; return x } 49 | @discardableResult 50 | prefix public func --(x: inout UInt16) -> UInt16 { x = x - 1; return x } 51 | 52 | 53 | @discardableResult 54 | postfix public func ++(x: inout Int16) -> Int16 { defer { x = x + 1 }; return x } 55 | @discardableResult 56 | postfix public func ++(x: inout UInt16) -> UInt16 { defer { x = x + 1 }; return x } 57 | @discardableResult 58 | postfix public func --(x: inout Int16) -> Int16 { defer { x = x - 1 }; return x } 59 | @discardableResult 60 | postfix public func --(x: inout UInt16) -> UInt16 { defer { x = x - 1 }; return x } 61 | 62 | @discardableResult 63 | prefix public func ++(x: inout Int32) -> Int32 { x = x + 1; return x } 64 | @discardableResult 65 | prefix public func ++(x: inout UInt32) -> UInt32 { x = x + 1; return x } 66 | @discardableResult 67 | prefix public func --(x: inout Int32) -> Int32 { x = x - 1; return x } 68 | @discardableResult 69 | prefix public func --(x: inout UInt32) -> UInt32 { x = x - 1; return x } 70 | 71 | 72 | @discardableResult 73 | postfix public func ++(x: inout Int32) -> Int32 { defer { x = x + 1 }; return x } 74 | @discardableResult 75 | postfix public func ++(x: inout UInt32) -> UInt32 { defer { x = x + 1 }; return x } 76 | @discardableResult 77 | postfix public func --(x: inout Int32) -> Int32 { defer { x = x - 1 }; return x } 78 | @discardableResult 79 | postfix public func --(x: inout UInt32) -> UInt32 { defer { x = x - 1 }; return x } 80 | 81 | @discardableResult 82 | prefix public func ++(x: inout Int64) -> Int64 { x = x + 1; return x } 83 | @discardableResult 84 | prefix public func ++(x: inout UInt64) -> UInt64 { x = x + 1; return x } 85 | @discardableResult 86 | prefix public func --(x: inout Int64) -> Int64 { x = x - 1; return x } 87 | @discardableResult 88 | prefix public func --(x: inout UInt64) -> UInt64 { x = x - 1; return x } 89 | 90 | 91 | @discardableResult 92 | postfix public func ++(x: inout Int64) -> Int64 { defer { x = x + 1 }; return x } 93 | @discardableResult 94 | postfix public func ++(x: inout UInt64) -> UInt64 { defer { x = x + 1 }; return x } 95 | @discardableResult 96 | postfix public func --(x: inout Int64) -> Int64 { defer { x = x - 1 }; return x } 97 | @discardableResult 98 | postfix public func --(x: inout UInt64) -> UInt64 { defer { x = x - 1 }; return x } 99 | 100 | @discardableResult 101 | prefix public func ++(x: inout Int8) -> Int8 { x = x + 1; return x } 102 | @discardableResult 103 | prefix public func ++(x: inout UInt8) -> UInt8 { x = x + 1; return x } 104 | @discardableResult 105 | prefix public func --(x: inout Int8) -> Int8 { x = x - 1; return x } 106 | @discardableResult 107 | prefix public func --(x: inout UInt8) -> UInt8 { x = x - 1; return x } 108 | 109 | 110 | @discardableResult 111 | postfix public func ++(x: inout Int8) -> Int8 { defer { x = x + 1 }; return x } 112 | @discardableResult 113 | postfix public func ++(x: inout UInt8) -> UInt8 { defer { x = x + 1 }; return x } 114 | @discardableResult 115 | postfix public func --(x: inout Int8) -> Int8 { defer { x = x - 1 }; return x } 116 | @discardableResult 117 | postfix public func --(x: inout UInt8) -> UInt8 { defer { x = x - 1 }; return x } 118 | 119 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Optional.swift: -------------------------------------------------------------------------------- 1 | infix operator !! : VeryLowPrecedence 2 | 3 | // -------------------------------------------------- 4 | // MARK: Controlled Landing 5 | // -------------------------------------------------- 6 | 7 | /// Forced unwrap with expressive fatal error courtesy of Ben Cohen 8 | /// 9 | /// ``` 10 | /// let array: [Int] = [] 11 | /// 12 | /// let lastItem = array.last !! "Expected non-empty array" 13 | /// ``` 14 | /// 15 | func !! (optionalValue: Wrapped?, fatalMessage: String) -> Wrapped { 16 | if let result = optionalValue { return result } 17 | fatalError(fatalMessage) 18 | } 19 | 20 | 21 | // -------------------------------------------------- 22 | // MARK: More readable `optional.map` 23 | // -------------------------------------------------- 24 | 25 | /// A more expressive way to perform optional.map(action) 26 | extension Optional { 27 | public func `do`(perform action: (Wrapped) -> Void) { 28 | self.map(action) 29 | } 30 | } 31 | 32 | // -------------------------------------------------- 33 | // MARK: Map/Flatmap Conditional Application 34 | // -------------------------------------------------- 35 | 36 | infix operator .? : VeryHighPrecedence 37 | 38 | /// Contextually performs an in-line operator `flatMap`. 39 | /// Use for conditionally applying a functional pass-through 40 | /// transformation closure 41 | /// ``` 42 | /// optionalInteger.?(String.init) 43 | /// ``` 44 | /// - Parameter transform: A closure that accepts 45 | /// an optional value as its argument and returns 46 | /// an optional value. 47 | /// - Parameter lhs: the operand 48 | /// - Note: There is no way to assign this at 49 | /// slightly higher precedence than the 50 | /// `map` version of `.?` Check your output types 51 | public func .?(lhs: T?, transform: (T) throws -> U?) rethrows -> U? { 52 | return try lhs.flatMap(transform) 53 | } 54 | 55 | /// Contextually performs an in-line operator `map`. 56 | /// Use for conditionally applying a functional pass-through 57 | /// transformation closure 58 | /// ``` 59 | /// optionalInteger.?(String.init) 60 | /// ``` 61 | /// - Parameter lhs: the operand 62 | /// - Parameter transform: A closure that accepts 63 | /// an optional value as its argument and returns 64 | /// an non-optional value. 65 | /// - Note: There is no way to assign this at 66 | /// slightly lower precedence than the 67 | /// `flatMap` version of `.?`. Check your output 68 | /// types 69 | public func .?(lhs: T?, _ transform: (T) throws -> U) rethrows -> U? { 70 | return try lhs.map(transform) 71 | } 72 | 73 | //----------------------------------------------------------------------------- 74 | // MARK: Zipping optionals 75 | //----------------------------------------------------------------------------- 76 | 77 | /// Returns a tuple of parameters if all are `.Some`, `nil` otherwise 78 | public func zip(_ first: T?, _ second: U?) -> (T, U)? { 79 | return first.flatMap({ firstItem in 80 | second.flatMap({ secondItem in 81 | return (firstItem, secondItem) 82 | }) 83 | }) 84 | } 85 | 86 | /// Evaluates the given closure when two `Optional` instances are not `nil`, 87 | /// passing the unwrapped values as parameters. (Thanks, Mike Ash) 88 | public func flatMap2(_ first: T?, _ second: U?, _ transform: (T, U) throws -> V?) rethrows -> V? { 89 | return try zip(first, second).flatMap ({ try transform($0.0, $0.1) }) 90 | } 91 | 92 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+OptionalComparison.swift: -------------------------------------------------------------------------------- 1 | // MARK: Introduction 2 | // 3 | // Better `Optional` comparisons 4 | // 5 | // This implementation provides the following styles of optional comparison: 6 | // 7 | // Wrapped Optional Comparisons: 8 | // 9 | // * Compare p? and q?, and return p! op q!, otherwise nil 10 | // * Compare p and q?, and return p op q!, otherwise nil 11 | // * Compare p? and q, and return p! op q, otherwise nil 12 | // 13 | // Unwrapped Optional Comparions: 14 | // 15 | // * Compare p? and q?, and return p! op q!, otherwise false 16 | // * Compare p and q?, and return p op q!, otherwise false 17 | // * Compare p? and q, and return p! op q, otherwise false 18 | // 19 | // These implementations follow the precedent of IEEE754 wrt NaN: 20 | // Any comparison involving .none is invalid, producing nil 21 | // (wrapped result) or false (truth value result) 22 | // 23 | // See also: http://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values 24 | // 25 | // Thanks, Mike Ash, Tim Vermeulen, Greg Titus, Brent Royal-Gordon 26 | // 27 | // Brent RG offers his take here: https://gist.github.com/brentdax/60460ad4578d5d8d52a9d736240cfea6 28 | 29 | // MARK: Utility 30 | 31 | /// Evaluates the given closure when two `Optional` instances are not `nil`, 32 | /// passing the unwrapped values as parameters. 33 | fileprivate func _flatMap2( 34 | _ first: T?, _ second: U?, 35 | _ transform: (T, U) throws -> V?) rethrows -> V? 36 | { 37 | guard let first = first, let second = second else { return nil } 38 | return try transform(first, second) 39 | } 40 | 41 | // MARK: Wrapped Optional Comparisons 42 | 43 | /// Weak precedence ensures that comparison happens late 44 | precedencegroup OptionalComparisonPrecedence { 45 | higherThan: NilCoalescingPrecedence 46 | } 47 | 48 | /// Failable less-than comparison 49 | infix operator ?: OptionalComparisonPrecedence 59 | 60 | /// Failable greater-than-or-equal comparison 61 | infix operator >=?: OptionalComparisonPrecedence 62 | 63 | // MARK: Wrapped Optional Comparisons 64 | 65 | // These operators compare two optional truth values p? and q? 66 | // returning `p op q` for non-nil values, otherwise nil 67 | 68 | /// Returns lhs! < rhs!, otherwise nil 69 | public func (lhs: T?, rhs: T?) -> Bool? { return _flatMap2(lhs, rhs, <) } 70 | 71 | /// Returns lhs! <= rhs!, otherwise nil 72 | public func <=? (lhs: T?, rhs: T?) -> Bool? { return _flatMap2(lhs, rhs, <=) } 73 | 74 | /// Returns lhs! == rhs!, otherwise nil 75 | public func ==? (lhs: T?, rhs: T?) -> Bool? { return _flatMap2(lhs, rhs, ==) } 76 | 77 | /// Returns lhs! > rhs!, otherwise nil 78 | public func >? (lhs: T?, rhs: T?) -> Bool? { return _flatMap2(lhs, rhs, >) } 79 | 80 | /// Returns lhs! >= rhs!, otherwise nil 81 | public func >=? (lhs: T?, rhs: T?) -> Bool? { return _flatMap2(lhs, rhs, >=) } 82 | 83 | // MARK: Wrapped Optional-Nonoptional Comparison 84 | 85 | // These operators compare an optional truth value p? and 86 | // a non-optional truth value q, returning `p op q` for non-nil 87 | // p?, otherwise nil 88 | 89 | /// Returns lhs! < rhs, otherwise nil 90 | public func (lhs: T?, rhs: T) -> Bool? { return lhs.map({ $0 < rhs }) } 91 | 92 | /// Returns lhs! <= rhs, otherwise nil 93 | public func <=? (lhs: T?, rhs: T) -> Bool? { return lhs.map({ $0 <= rhs }) } 94 | 95 | /// Returns lhs! == rhs, otherwise nil 96 | public func ==? (lhs: T?, rhs: T) -> Bool? { return lhs.map({ $0 == rhs }) } 97 | 98 | /// Returns lhs! > rhs, otherwise nil 99 | public func >? (lhs: T?, rhs: T) -> Bool? { return lhs.map({ $0 > rhs }) } 100 | 101 | /// Returns lhs! >= rhs, otherwise nil 102 | public func >=? (lhs: T?, rhs: T) -> Bool? { return lhs.map({ $0 >= rhs }) } 103 | 104 | // These operators compare a non-optional truth value p and 105 | // an optional truth value q?, returning `p op q` for non-nil 106 | // q?, otherwise nil 107 | 108 | /// Returns lhs < rhs!, otherwise nil 109 | public func (lhs: T, rhs: T?) -> Bool? { return rhs.map({ lhs < $0 }) } 110 | 111 | /// Returns lhs <= rhs!, otherwise nil 112 | public func <=? (lhs: T, rhs: T?) -> Bool? { return rhs.map({ lhs <= $0 }) } 113 | 114 | /// Returns lhs == rhs!, otherwise nil 115 | public func ==? (lhs: T, rhs: T?) -> Bool? { return rhs.map({ lhs == $0 }) } 116 | 117 | /// Returns lhs > rhs!, otherwise nil 118 | public func >? (lhs: T, rhs: T?) -> Bool? { return rhs.map({ lhs > $0 }) } 119 | 120 | /// Returns lhs >= rhs!, otherwise nil 121 | public func >=? (lhs: T, rhs: T?) -> Bool? { return rhs.map({ lhs >= $0 }) } 122 | 123 | // MARK: Unwrapped Optional Comparisons 124 | 125 | // These operators compare two optional truth values p? and q? 126 | // returning `p op q` for non-nil values, otherwise false 127 | 128 | /// Returns lhs! < rhs!, otherwise false 129 | public func < (lhs: T?, rhs: T?) -> Bool { return lhs (lhs: T?, rhs: T?) -> Bool { return lhs <=? rhs ?? false } 133 | 134 | /// Returns lhs! == rhs!, otherwise false 135 | public func == (lhs: T?, rhs: T?) -> Bool { return lhs ==? rhs ?? false } 136 | 137 | /// Returns lhs! > rhs!, otherwise false 138 | public func > (lhs: T?, rhs: T?) -> Bool { return lhs >? rhs ?? false } 139 | 140 | /// Returns lhs! >= rhs!, otherwise false 141 | public func >= (lhs: T?, rhs: T?) -> Bool { return lhs >=? rhs ?? false } 142 | 143 | // MARK: Unwrapped Optional-Nonoptional Comparisons 144 | 145 | // These operators compare an optional truth value p? and 146 | // a non-optional truth value q, returning `p op q` for non-nil 147 | // p?, otherwise false 148 | 149 | /// Returns lhs! < rhs, otherwise false 150 | public func < (lhs: T?, rhs: T) -> Bool { return lhs (lhs: T?, rhs: T) -> Bool { return lhs <=? rhs ?? false } 154 | 155 | /// Returns lhs! == rhs, otherwise false 156 | public func == (lhs: T?, rhs: T) -> Bool { return lhs ==? rhs ?? false } 157 | 158 | /// Returns lhs! > rhs, otherwise false 159 | public func > (lhs: T?, rhs: T) -> Bool { return lhs >? rhs ?? false } 160 | 161 | /// Returns lhs! >= rhs, otherwise false 162 | public func >= (lhs: T?, rhs: T) -> Bool { return lhs >=? rhs ?? false } 163 | 164 | // These operators compare a non-optional truth value p and 165 | // an optional truth value q?, returning `p op q` for non-nil 166 | // q?, otherwise false 167 | 168 | /// Returns lhs < rhs!, otherwise false 169 | public func < (lhs: T, rhs: T?) -> Bool { return lhs (lhs: T, rhs: T?) -> Bool { return lhs <=? rhs ?? false } 173 | 174 | /// Returns lhs == rhs!, otherwise false 175 | public func == (lhs: T, rhs: T?) -> Bool { return lhs ==? rhs ?? false } 176 | 177 | /// Returns lhs > rhs!, otherwise false 178 | public func > (lhs: T, rhs: T?) -> Bool { return lhs >? rhs ?? false } 179 | 180 | /// Returns lhs >= rhs!, otherwise false 181 | public func >= (lhs: T, rhs: T?) -> Bool { return lhs >=? rhs ?? false } 182 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Precedence.swift: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MARK: Precedence 3 | //----------------------------------------------------------------------------- 4 | 5 | 6 | // BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence > RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence > ComparisonPrecedence > LogicalConjunctionPrecedence > LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence > FunctionArrowPrecedence > [nothing] 7 | // DefaultPrecedence > TernaryPrecedence 8 | 9 | /// Low precedence group 10 | precedencegroup LowPrecedence { higherThan: VeryLowPrecedence } 11 | 12 | /// Very low precedence group 13 | precedencegroup VeryLowPrecedence { lowerThan: FunctionArrowPrecedence } 14 | 15 | /// Very high precedence 16 | precedencegroup VeryHighPrecedence { higherThan: HighPrecedence} 17 | 18 | /// High precedence 19 | precedencegroup HighPrecedence { higherThan: BitwiseShiftPrecedence } 20 | 21 | /// Left associative precedence 22 | precedencegroup LeftAssociativePrecedence { associativity: left } 23 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Reflection.swift: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MARK: Default class reflection 3 | // 4 | // Thanks, Mike Ash 5 | //----------------------------------------------------------------------------- 6 | 7 | /// Provides a better default representation for reference types than the class name 8 | public protocol DefaultReflectable: CustomStringConvertible {} 9 | 10 | /// A default implementation to enable class members to display their values 11 | extension DefaultReflectable { 12 | 13 | /// Constructs a better representation using reflection 14 | internal func DefaultDescription(instance: T) -> String { 15 | 16 | let mirror = Mirror(reflecting: instance) 17 | let chunks = mirror.children.map({ 18 | (child) -> String in 19 | guard let label = child.label else { return "\(child.value)" } 20 | return child.value is String ? 21 | "\(label): \"\(child.value)\"" : "\(label): \(child.value)" 22 | }) 23 | 24 | if chunks.isEmpty { return "\(instance)" } 25 | let chunksString = chunks.joined(separator: ", ") 26 | return "\(mirror.subjectType)(\(chunksString))" 27 | } 28 | 29 | /// Conforms to CustomStringConvertible 30 | public var description: String { return DefaultDescription(instance: self) } 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Splatting.swift: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MARK: Splatting 3 | //----------------------------------------------------------------------------- 4 | 5 | /// Returns a tuple-parameterized version of the passed function 6 | /// Via Brent R-G 7 | func splatted(_ function: @escaping (T, U) -> Z) -> ((T, U)) -> Z { 8 | return { tuple in function(tuple.0, tuple.1) } 9 | } 10 | 11 | /// Returns a tuple-parameterized version of the passed function 12 | /// Via Brent R-G 13 | func splatted(_ function: @escaping (T, U, V) -> Z) -> ((T, U, V)) -> Z { 14 | return { tuple in function(tuple.0, tuple.1, tuple.2) } 15 | } 16 | 17 | /// Returns a tuple-parameterized version of the passed function 18 | /// Via Brent R-G 19 | func splatted(_ function: @escaping (T, U, V, W) -> Z) -> ((T, U, V, W)) -> Z { 20 | return { tuple in function(tuple.0, tuple.1, tuple.2, tuple.3) } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+TimeTest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | //----------------------------------------------------------------------------- 4 | // MARK: Time Test 5 | //----------------------------------------------------------------------------- 6 | 7 | /// Prints the elapsed time to execute a block under whatever optimization 8 | /// conditions are currently in use by the compiler 9 | public func timetest(_ note: String, block: () -> Void) { 10 | print("Starting Test:", note) 11 | let now = ProcessInfo().systemUptime 12 | block() 13 | let timeInterval = ProcessInfo().systemUptime - now 14 | print("Ending Test:", note); print("Elapsed time: \(timeInterval)") 15 | } 16 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+Unimplemented.swift: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // MARK: Unimplemented functionality 3 | // -------------------------------------------------- 4 | 5 | /// Handles unimplemented functionality with site-specific information, courtesy of Sourosh Khanlou 6 | func unimplemented(_ function: String = #function, _ file: String = #file) -> Never { 7 | fatalError("\(function) in \(file) has not been implemented") 8 | } 9 | 10 | -------------------------------------------------------------------------------- /Sources/SwiftUtility+With.swift: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MARK: Immutable Assignment 3 | //----------------------------------------------------------------------------- 4 | 5 | /// Returns `item` after calling `update` to inspect and possibly 6 | /// modify it. 7 | /// 8 | /// If `T` is a value type, `update` uses an independent copy 9 | /// of `item`. If `T` is a reference type, `update` uses the 10 | /// same instance passed in, but it can substitute a different 11 | /// instance by setting its parameter to a new value. 12 | @discardableResult 13 | public func with(_ item: T, update: (inout T) throws -> Void) rethrows -> T { 14 | var this = item 15 | try update(&this) 16 | return this 17 | } 18 | 19 | /// Returns a modified reference type 20 | @discardableResult 21 | public func with(_ this: T, update: (T) throws -> Void) rethrows -> T { 22 | try update(this) 23 | return this 24 | } 25 | -------------------------------------------------------------------------------- /Various/Notes.swift: -------------------------------------------------------------------------------- 1 | // Ongoing work 2 | -------------------------------------------------------------------------------- /Various/SwiftUtility+Interesting.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A collection of interesting things to keep on hand 4 | 5 | */ 6 | 7 | 8 | /// Courtesy of Tim Vermeulen 9 | /// "I have an object that should logically only be identifiable by its pointer address, 10 | /// and I want to use it as the key in a dictionary" 11 | protocol AutoHashable: class, Hashable {} 12 | 13 | extension AutoHashable { 14 | static func == (left: Self, right: Self) -> Bool { 15 | return left === right 16 | } 17 | 18 | var hashValue: Int { 19 | return ObjectIdentifier(self).hashValue 20 | } 21 | } 22 | 23 | import Dispatch 24 | 25 | /// I can't remember where I found this or who it is from 26 | /// But I jotted it down anyway 27 | func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void, 28 | on queue: DispatchQueue) { 29 | withoutActuallyEscaping(f) { escapableF in 30 | withoutActuallyEscaping(g) { escapableG in 31 | queue.async(execute: escapableF) 32 | queue.async(execute: escapableG) 33 | queue.sync(flags: .barrier) {} 34 | } 35 | } 36 | } 37 | 38 | infix operator |> : VeryLowPrecedence 39 | 40 | /// Pipe-forward, courtesy of Ben Cohen 41 | /// 42 | /// ``` 43 | /// let words = ["five","four","three","two","one","blastoff!"] 44 | /// ((0...5).reversed() |> { zip($0, words) }) 45 | /// .forEach { print($0.0,$0.1, separator: ": ") } 46 | /// ``` 47 | func |> (lhs: T, rhs: (T)->U) -> U { 48 | return rhs(lhs) 49 | } 50 | 51 | //----------------------------------------------------------------------------- 52 | // MARK: Clamp to Range 53 | //----------------------------------------------------------------------------- 54 | 55 | /// Returns nearest possible value that falls within the closed range 56 | public func clamp(_ value: T, to range: ClosedRange) -> T { 57 | return max(min(value, range.upperBound), range.lowerBound) 58 | } 59 | 60 | 61 | //----------------------------------------------------------------------------- 62 | // MARK: Adding Optionality 63 | //----------------------------------------------------------------------------- 64 | 65 | postfix operator +? 66 | // postfix operator +? : LeftAssociativePrecedence // will not compile 67 | 68 | /// Adds optionality by wrapping the 69 | /// rhs value in an Optional enumeration 70 | /// 71 | /// - Parameter lhs: a value 72 | /// - Returns: The value wrapped as an Optional 73 | /// ``` 74 | /// let x = 42+? // x is Int? 75 | /// ``` 76 | public postfix func +?(lhs: T) -> T? { return Optional(lhs) } 77 | 78 | //----------------------------------------------------------------------------- 79 | // MARK: HIGH PRECEDENCE COALESCING 80 | //----------------------------------------------------------------------------- 81 | 82 | infix operator .?? : HighPrecedence 83 | 84 | /// A high-precedence coalescing nil, 85 | /// ensuring that coalescing occurs to 86 | /// produce a value before applying value 87 | /// to other operations 88 | /// - Parameter lhs: an optional value 89 | /// - Parameter rhs: a non-optional fallback value 90 | /// - Returns: The coalesced result of `lhs ?? rhs` 91 | /// performed at high precedence 92 | /// ``` 93 | /// let x = Optional(42) 94 | /// let y = 5 + x ?? 2 // won't compile 95 | /// let y = 5 + x .?? 2 // will compile 96 | /// ``` 97 | public func .??(lhs: T?, rhs: T) -> T { 98 | return lhs ?? rhs 99 | } 100 | 101 | 102 | //----------------------------------------------------------------------------- 103 | // MARK: CONDITIONAL FORCE UNWRAP 104 | //----------------------------------------------------------------------------- 105 | 106 | public struct MappingUnwrappingError: Error{} 107 | 108 | infix operator .?! : VeryHighPrecedence 109 | 110 | /// Performs an in-line operator-based `flatMap` with 111 | /// forced unwrap. 112 | /// - Parameter lhs: the operand 113 | /// - Parameter transform: A closure that accepts 114 | /// an optional value as its argument and returns 115 | /// an optional value. 116 | /// - throws: MappingUnwrappingError 117 | public func .?!(lhs: T?, transform: (T) throws -> U?) throws -> U { 118 | guard let result = try lhs.flatMap(transform) 119 | else { throw MappingUnwrappingError() } 120 | return result 121 | } 122 | 123 | /// Performs an in-line operator-based map with forced unwrap 124 | /// - Parameter lhs: the operand 125 | /// - Parameter transform: A closure that accepts 126 | /// an optional value as its argument and returns 127 | /// an non-optional value. 128 | /// - throws: MappingUnwrappingError 129 | public func .?!(lhs: T?, transform: (T) throws -> U) throws -> U { 130 | guard let result = try lhs.map(transform) 131 | else { throw MappingUnwrappingError() } 132 | return result 133 | } 134 | 135 | 136 | -------------------------------------------------------------------------------- /Various/SwiftUtility+Operatorbase.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // -------------------------------------------------- 4 | // MARK: - CASTING 5 | // -------------------------------------------------- 6 | 7 | prefix operator * 8 | 9 | /// Performs a groffcast by force casting to 10 | /// the type demanded by static context 11 | /// 12 | /// ``` 13 | /// var value: Any = 2 14 | /// 1 + *value // 3 15 | /// value = "world" 16 | /// "hello " + *value // "hello world" 17 | /// ``` 18 | /// 19 | public prefix func *(item: Any) -> T { return item as! T } 20 | 21 | infix operator --> : HighPrecedence 22 | 23 | /// Perform unsafe bitcast iff the type sizes equate 24 | /// 25 | /// ``` 26 | /// let int = 42 27 | /// let double = 42 --> Double.self 28 | /// print(double) // Optional(2.0750757125332355e-322) 29 | /// ``` 30 | /// - Note: The ashcast: a better cast. Thanks Mike Ash for the assist 31 | public func -->(item: T, target: U.Type) -> U? { 32 | guard MemoryLayout.size == MemoryLayout.size else { return nil } 33 | return unsafeBitCast(item, to: target) 34 | } 35 | 36 | infix operator => 37 | 38 | /// Dynamically dispatch a method using dynogroffing 39 | /// 40 | /// ``` 41 | /// var x: Any = 2 42 | /// (x=>String.hasPrefix)("wor") 43 | /// ``` 44 | /// 45 | public func =>(item: Any, method: (T) -> (U) -> V) -> (U) -> V { 46 | return method(item as! T) 47 | } 48 | 49 | infix operator ==> 50 | 51 | /// Dynamically cast without bridging (The Groffchoo) 52 | /// 53 | /// ``` 54 | /// var x: Any = NSString(string: "Hello") 55 | /// let y = x ==> NSString.self // "Hello" 56 | /// let z = x ==> String.self // nil 57 | /// ``` 58 | /// 59 | public func ==>(item: Any, destinationType: T.Type) -> T? { 60 | guard 61 | let casted = item as? T, 62 | type(of: item) is T.Type 63 | else { return nil } 64 | return casted 65 | } 66 | 67 | // -------------------------------------------------- 68 | // MARK: - VALUE ASSIGNMENT 69 | // -------------------------------------------------- 70 | 71 | infix operator <- 72 | 73 | /// Replace the value of `a` with `b` and return the old value. 74 | /// The EridiusAssignment courtesy of Kevin Ballard 75 | /// ``` 76 | /// var b = "First" 77 | /// let y = a <- "Second" 78 | /// print(a, b) // "Second", "First"/// ``` 79 | /// 80 | public func <- (originalValue: inout T, newValue: T) -> T { 81 | var holder = newValue; swap(&originalValue, &holder); return holder 82 | } 83 | 84 | infix operator =? : AssignmentPrecedence 85 | 86 | /// Conditionally assign optional value to target via unwrapping 87 | /// Thanks, Mike Ash 88 | /// ``` 89 | /// var x: Int? = 8 90 | /// var y = 0 91 | /// y =? x // y is now 8 92 | /// x = nil 93 | /// y =? x // y is still 8 94 | /// 95 | public func =?(target: inout T, newValue: T?) { 96 | if let unwrapped = newValue { target = unwrapped } 97 | } 98 | 99 | // -------------------------------------------------- 100 | // MARK: - CHAINING 101 | // -------------------------------------------------- 102 | // 103 | // e.g. let y = (3 + 2) >>> String.init // y is "5" 104 | 105 | precedencegroup ChainingPrecedence { 106 | associativity: left 107 | } 108 | infix operator >>>: ChainingPrecedence 109 | 110 | /// Failable chaining 111 | public func >>>(value: T?, transform: (T) -> U?) -> U? { 112 | if let value = value { return transform(value) } 113 | return nil 114 | } 115 | 116 | /// Direct chaining 117 | public func >>>(value: T, transform: (T) -> U) -> U { 118 | return transform(value) 119 | } 120 | --------------------------------------------------------------------------------