Skip to content

Instantly share code, notes, and snippets.

@zacharyvoase
Created May 24, 2020 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zacharyvoase/c988762c9c33382875a1b8bff51f73c0 to your computer and use it in GitHub Desktop.
Save zacharyvoase/c988762c9c33382875a1b8bff51f73c0 to your computer and use it in GitHub Desktop.
//
// 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