Skip to content

Instantly share code, notes, and snippets.

View JanGorman's full-sized avatar
:shipit:

Jan Gorman JanGorman

:shipit:
View GitHub Profile
@propertyWrapper
struct Expirable<Value: ExpressibleByNilLiteral> {
let duration: TimeInterval
private var expirationDate = Date()
private var innerValue: Value = nil
private var hasExpired: Bool {
expirationDate < Date()
}
@JanGorman
JanGorman / isBlank.swift
Created May 21, 2019 05:30
Swift5+ String extension to check if a string is blank
extension String {
var isBlank: Bool {
return allSatisfy { $0.isWhitespace }
}
}
// Combine app + shared defaults into one
extension UserDefaults {
static var shared: UserDefaults {
let combined = UserDefaults.standard
combined.addSuite(named: "com.schnaub.app")
return combined
}
}
// Unit test instance that clears all values before the test is run
func with<T>(_ x: T, _ f: (inout T) -> Void) -> T {
var value = x
f(&value)
return value
}
@JanGorman
JanGorman / non-empty-optionals.swift
Created January 22, 2019 16:41
Avoids unwrapping/checking emptiness
extension Optional where Wrapped: Collection {
var nonEmpty: Wrapped? {
return self?.isEmpty == true ? nil : self
}
}
struct BillingInfo {
var name: String
var vatNumber: String?
}
@JanGorman
JanGorman / predicate.swift
Created January 21, 2019 09:13
Predicate, an application of KeyPaths
struct Predicate<Element> {
private let condition: (Element) -> Bool
func evaluate(for element: Element) -> Bool {
return condition(element)
}
}
func <= <Element, T: Comparable>(_ attribute: KeyPath<Element, T>, _ constant: T) -> Predicate<Element> {
return Predicate(condition: { $0[keyPath: attribute] <= constant })
extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
return self?.isEmpty ?? true
}
}
if textField.text.isNilOrEmpty {
// No unwraps
}
// Float comparison
infix operator ==~ : ComparisonPrecedence
func ==~<T> (lhs: T, rhs: T) -> Bool where T: FloatingPoint {
return lhs == rhs || lhs.nextDown == rhs || lhs.nextUp == rhs
}
// 0 padded numbers
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = 20
formatter.string(for: 30) // 00000000000000000030
extension String {
// [i]
subscript(i: Int) -> Character {
let index = self.index(self.startIndex, offsetBy: i)
return self[index]
}
// [i..<j]
subscript(r: Range<Int>) -> String {
let i = self.index(self.startIndex, offsetBy: r.lowerBound)