Created
May 24, 2020 19:48
-
-
Save zacharyvoase/c988762c9c33382875a1b8bff51f73c0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BindingTranslation.swift | |
// Swoleness | |
// | |
// Created by Zack Voase on 5/23/20. | |
// Copyright © 2020 Zack Voase. All rights reserved. | |
// | |
import Foundation | |
import SwiftUI | |
/** A completely-defined two-way mapping */ | |
struct Bijection<T, U> { | |
let forward: (T) -> U; | |
let reverse: (U) -> T; | |
func inverse() -> Bijection<U, T> { | |
return Bijection<U, T>(forward: self.reverse, reverse: self.forward) | |
} | |
func compose<V>(next: Bijection<U, V>) -> Bijection<T, V> { | |
return Bijection<T, V>( | |
forward: {next.forward(self.forward($0))}, | |
reverse: {self.reverse(next.reverse($0))}) | |
} | |
} | |
/** | |
A mapping where every T can be mapped to a U, but not every U can be mapped to T. | |
*/ | |
struct Mapping<T, U> { | |
let forward: (T) -> U; | |
let reverse: (U) -> T?; | |
/** Turn this Mapping into a Bijection by providing a default T for nil U reverse-inputs. */ | |
func withDefaultFor(reverse reverseDefault: T) -> Bijection<T, U> { | |
return Bijection( | |
forward: self.forward, | |
reverse: { self.reverse($0) ?? reverseDefault }) | |
} | |
/** Turn this Mapping into a Bijection by providing a default U for nil T forward-inputs. */ | |
func mapNilTo(_ forwardNilValue: U) -> Bijection<T?, U> { | |
return Bijection( | |
forward: { | |
guard let t = $0 else { return forwardNilValue } | |
return self.forward(t) }, | |
reverse: self.reverse) | |
} | |
/** Turn this mapping into a Bijection by forcibly unwrapping any nils returned by the reverse function. CAREFUL. */ | |
func forceReverse() -> Bijection<T, U> { | |
return Bijection(forward: self.forward, reverse: { self.reverse($0)! }) | |
} | |
} | |
/** A mapping where some T can be mapped to U, and some U can be mapped to T. */ | |
struct PartialMapping<T, U> { | |
let forward: (T) -> U?; | |
let reverse: (U) -> T?; | |
/** Turn this PartialMapping into a Mapping by providing a default for when the forward function returns nil. */ | |
func withDefaultFor(forward forwardDefault: U) -> Mapping<T, U> { | |
return Mapping( | |
forward: { self.forward($0) ?? forwardDefault }, | |
reverse: self.reverse) | |
} | |
/** Turn this PartialMapping into a Bijection by providing defaults for nil output from both the forward and reverse functions. */ | |
func withDefaultsFor(forward forwardDefault: U, reverse reverseDefault: T) -> Bijection<T, U> { | |
return Bijection( | |
forward: { self.forward($0) ?? forwardDefault }, | |
reverse: { self.reverse($0) ?? reverseDefault }) | |
} | |
/** Turn this PartialMapping into a Bijection by forcibly unwrapping the optionals returned by its forward and reverse functions */ | |
func force() -> Bijection<T, U> { | |
return Bijection( | |
forward: { self.forward($0)! }, | |
reverse: { self.reverse($0)! }) | |
} | |
} | |
extension Binding { | |
func map<To>(_ bijection: Bijection<Value, To>) -> Binding<To> { | |
return Binding<To>( | |
get: { bijection.forward(self.wrappedValue) }, | |
set: { self.wrappedValue = bijection.reverse($0) }) | |
} | |
} | |
func intFormatter(withFormatter formatter: NumberFormatter) -> Mapping<Int, String> { | |
return Mapping( | |
forward: { formatter.string(for: $0)! }, | |
reverse: { | |
guard let number = formatter.number(from: $0) else { | |
return nil | |
} | |
return Int(exactly: number) | |
}) | |
} | |
func intToDoubleExactly() -> PartialMapping<Int, Double> { | |
return PartialMapping<Int, Double>( | |
forward: { Double(exactly: $0) }, | |
reverse: { Int(exactly: $0) }) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment