Skip to content

Instantly share code, notes, and snippets.

View IanKeen's full-sized avatar
🏂

Ian Keen IanKeen

🏂
View GitHub Profile
@IanKeen
IanKeen / Invalidating.swift
Created July 31, 2022 01:36
PropertyWrapper: @invalidating backport
struct Invalidation {
static let display = Invalidation { $0.setNeedsDisplay() }
static let layout = Invalidation { $0.setNeedsLayout() }
let action: (UIView) -> Void
}
@propertyWrapper
struct Invalidating<Value> {
private let invalidations: [Invalidation]
@IanKeen
IanKeen / .swift
Created July 26, 2022 22:33
SwiftUI: @StateObject.init(wrappedValue:) gotcha
struct Parent: View {
@State private var foo = "foo" {
didSet { print("Parent", foo) }
}
var body: some View {
VStack {
Inner(value: foo)
Button("Parent Double") {
@IanKeen
IanKeen / Storage.swift
Last active July 29, 2025 16:07
PropertyWrapper: Storage to extend support for more types using `@AppStorage`
@propertyWrapper
struct Storage<T: AppStorageConvertible>: RawRepresentable {
var rawValue: String { wrappedValue.storedValue }
var wrappedValue: T
init?(rawValue: String) {
guard let value = T.init(rawValue) else { return nil }
self.wrappedValue = value
}
init(wrappedValue: T) {
@IanKeen
IanKeen / Publisher+WithPrevious.swift
Created July 12, 2022 22:26
Combine: WithPrevious
extension Publisher {
public typealias Pair<T> = (previous: T?, current: T)
public func withPrevious() -> AnyPublisher<Pair<Output>, Failure> {
return scan(nil) { previous, current -> Pair<Output>? in
return Pair(previous: previous?.current, current: current)
}
.compactMap { $0 }
.eraseToAnyPublisher()
}
@IanKeen
IanKeen / SizeClass.swift
Created July 2, 2022 22:46
PropertyWrapper: SwiftUI SizeClass helper
//v1: just find out if sizeclass is regular
@propertyWrapper
struct SizeClass: DynamicProperty {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.verticalSizeClass) var verticalSizeClass
var wrappedValue: Bool {
horizontalSizeClass == .regular && verticalSizeClass == .regular
}
}
public struct AnyEquatable: Equatable {
public let base: Any
private let isEqual: (_ other: Any) -> Bool
public init<T: Equatable>(_ value: T) {
self.base = value
self.isEqual = { other in
guard let other = other as? T else { return false }
return other == value
}
@IanKeen
IanKeen / RetryWhen.swift
Last active June 1, 2022 17:41
Combine: RetryWhen
import Combine
import Foundation
public extension Publisher {
func retryWhen(max: Int = .max, delay: DispatchQueue.SchedulerTimeType.Stride = 0, _ predicate: @escaping Publishers.RetryWhen<Self>.Predicate) -> Publishers.RetryWhen<Self> {
.init(upstream: self, max: max, delay: delay, predicate: predicate)
}
}
extension Publishers {
@IanKeen
IanKeen / UserDefaultsKey.swift
Last active December 24, 2024 23:12
Simple type safe wrapper around UserDefaults
struct UserDefaultsKey<T: Codable> {
var name: String
var `default`: T
}
extension UserDefaults {
func get<T>(_ key: UserDefaultsKey<T>) -> T {
guard
let data = data(forKey: key.name),
let box = try? JSONDecoder().decode(Box<T>.self, from: data)
@IanKeen
IanKeen / AnyCodable.swift
Last active August 8, 2023 22:02
AnyCodable
public struct AnyCodable: Codable {
public var base: Any
public init(_ base: Any) {
self.base = base
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
guard !container.decodeNil() else {
@IanKeen
IanKeen / Namespace.swift
Created December 18, 2021 03:46
Namespace and Implicit member chains
// 1. Create a generic namespace we can ue to 'hang' values off
public struct Namespace<Base> { }
// 2. Add a namespace to a type(s) with the desired name
extension Color {
public static var theme: Namespace<Self> { .init() }
}
extension Font {
public static var theme: Namespace<Self> { .init() }
}