Skip to content

Instantly share code, notes, and snippets.

View IanKeen's full-sized avatar
🏂

Ian Keen IanKeen

🏂
View GitHub Profile
@IanKeen
IanKeen / AnalyticsReducer.swift
Created January 16, 2024 04:27
TCA: Example of creating an Analytics component to get before/after state
public protocol AnalyticsReducer {
associatedtype State
associatedtype Action
func analytics(before: State, after: State, action: Action) -> Effect<Action>
}
public struct _AnalyticsReducer<Base: Reducer, Analytics: AnalyticsReducer>: Reducer where Analytics.State == Base.State, Analytics.Action == Base.Action {
@usableFromInline
let base: Base
@IanKeen
IanKeen / ReadSpacing.swift
Created October 24, 2022 20:10
SwiftUI: Read the default spacing for use elsewhere
struct ReadSpacing<Content: View>: View {
@State private var spacing: CGFloat = 0
@Binding private var outsideSpacing: CGFloat?
private let content: (CGFloat) -> Content
init(@ViewBuilder content: @escaping (CGFloat) -> Content) {
self._outsideSpacing = .constant(nil)
self.content = content
}
init(into spacing: Binding<CGFloat>, @ViewBuilder content: @escaping () -> Content) {
@IanKeen
IanKeen / Abstraction.swift
Created August 16, 2022 17:41
TCA Scoping Abstraction
// MARK: - TCAView
public protocol TCAView: View where Body == WithViewStore<ScopedState, ScopedAction, Content> {
associatedtype ViewState
associatedtype ViewAction
associatedtype ScopedState
associatedtype ScopedAction
associatedtype Content
@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 April 8, 2024 12:24
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 {