Skip to content

Instantly share code, notes, and snippets.

Avatar
🏂

Ian Keen IanKeen

🏂
View GitHub Profile
@IanKeen
IanKeen / ReadSpacing.swift
Created October 24, 2022 20:10
SwiftUI: Read the default spacing for use elsewhere
View ReadSpacing.swift
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
View Abstraction.swift
// 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
View Invalidating.swift
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
View .swift
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 March 28, 2023 18:52
PropertyWrapper: Storage to extend support for more types using `@AppStorage`
View Storage.swift
@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
View Publisher+WithPrevious.swift
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
View SizeClass.swift
//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
}
}
View AnyEquatable.swift
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
View RetryWhen.swift
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 July 9, 2022 17:31
Simple type safe wrapper around UserDefaults
View UserDefaultsKey.swift
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)