Skip to content

Instantly share code, notes, and snippets.


Ian Keen IanKeen

View GitHub Profile
IanKeen / Example.swift
Created Sep 15, 2020
WithLatestFrom for Combine
View Example.swift
let source = PassthroughSubject<Int, Error>()
let other = PassthroughSubject<String, Error>()
let subscription = source.withLatestFrom(other).assertNoFailure().sink(receiveValue: { value in
IanKeen / AppStorage.swift
Created Sep 10, 2020
PropertyWrapper: AppStorage backport
View AppStorage.swift
struct AppStorage<Value>: DynamicProperty {
let key: String
let defaultValue: Value
init(wrappedValue defaultValue: Value, _ key: String) {
self.key = key
self.defaultValue = defaultValue
self._wrappedValue = State(wrappedValue: UserDefaults.standard.object(forKey: key) as? Value ?? defaultValue)
IanKeen / Dictionary+KeyPath.swift
Created Sep 8, 2020
Extract nested values from a Dictionary using key paths
View Dictionary+KeyPath.swift
public struct DictionaryKeyPathError: Error, CustomStringConvertible {
public enum FragmentError: Error {
case valueMissing
case valueMismatch(expected: Any.Type, received: Any.Type, value: Any)
public let path: [Any]
public let error: FragmentError
public var description: String {
IanKeen / NeighborSequence.swift
Created Sep 2, 2020
Iterate a sequence with access to the previous/next elements if they exist
View NeighborSequence.swift
/// A collection that returns the current element as well as the previous and next elements when available
public struct NeighborSequence<Base: Sequence>: Sequence {
public typealias Element = (previous: Base.Element?, current: Base.Element, next: Base.Element?)
private let base: Base
fileprivate init(_ base: Base) {
self.base = base
IanKeen / Result+ObjectiveC.swift
Created Sep 2, 2020
Result extension to work nicely w/ older Objc
View Result+ObjectiveC.swift
extension Result where Failure == Error {
public enum ResultError: Error {
case invalidState
/// Convenience initalizer for creating a `Result` from an
/// Objective-C based optional set of parameters.
/// For example a common pattern in Objective-C is for the callback block
/// to be structured using all optional parameters, such as: `(Data?, Error?) -> Void`
IanKeen / Array+KeyPath.swift
Created Aug 7, 2020
Quick and dirty way to get a keypath from Decodable error
View Array+KeyPath.swift
extension Array where Element: CodingKey {
var keyPath: String {
return map { $0.intValue == nil ? $0.stringValue : "[\($0.intValue!)]" }
.joined(separator: ".")
.replacingOccurrences(of: ".[", with: "[")
IanKeen / Example_Complex.swift
Last active Nov 18, 2020
PropertyWrapper: @transaction binding for SwiftUI to make changes to data supporting commit/rollback
View Example_Complex.swift
struct User: Equatable {
var firstName: String
var lastName: String
struct MyApp: App {
@State var value = User(firstName: "", lastName: "")
@State var showEdit = false
IanKeen / Usage.swift
Created Jul 24, 2020
SwiftUI: Show a sheet based on an optionals unwrapped value
View Usage.swift
struct MyApp: App {
enum Sheet { case first, second }
@State var sheet: Sheet? = nil
var body: some Scene {
WindowGroup {
VStack {
Button("First") { sheet = .first }
IanKeen / Input.swift
Created Jul 17, 2020
PropertyWrapper: @input provides a write-only interface externally, but a subject available internally for modelling reactive streams in Combine
View Input.swift
import Combine
public struct Input<T> {
public struct SendProxy {
private let send: (T) -> Void
fileprivate init(_ send: @escaping (T) -> Void) {
self.send = send
IanKeen / Publisher+Result.swift
Created Jul 14, 2020
Extension to convert a failable Publisher into a non-failable Result based publisher (as well as extensions to break out values/errors) - Same idea as RxSwifts Materialize
View Publisher+Result.swift
extension Publisher {
func asResult() -> AnyPublisher<Result<Output, Failure>, Never> {
return map(Result.success)
.catch { Just(Result.failure($0)) }
extension Publisher {
func values<S, F: Error>() -> AnyPublisher<S, Never> where Output == Result<S, F>, Failure == Never {
You can’t perform that action at this time.