Skip to content

Instantly share code, notes, and snippets.

View IanKeen's full-sized avatar
🏂

Ian Keen IanKeen

🏂
View GitHub Profile
@IanKeen
IanKeen / Example.swift
Last active June 26, 2024 07:38
ObservableObjectContainer: Consolidate nested/child ObservableObjects into a single publisher, useful for 'parent' ObservableObjects
// Setup
class Child: ObservableObject {
@Published var value = ""
}
class Parent: ObservableObjectContainer {
let single = Child()
let array = [Child(), Child()]
func updateSingle() {
@IanKeen
IanKeen / .swift
Last active October 14, 2021 00:08
SwiftUI: quick and dirty nested ForEach debugger
import SwiftUI
struct Location: Equatable {
var file: String
var line: UInt
}
private struct ForEachKey: EnvironmentKey {
static var defaultValue: [Location] = []
}
private extension EnvironmentValues {
@IanKeen
IanKeen / TopLevelDecoder+Nested.swift
Created September 18, 2021 21:44
Decode an object nested under a key
extension TopLevelDecoder {
func decode<T: Decodable>(_ type: T.Type, from input: Input, under: String) throws -> T {
let decoder = try decode(_Decoder.self, from: input).decoder
let container = try decoder.container(keyedBy: AnyCodingKey.self)
return try container.decode(T.self, forKey: .init(under))
}
}
private struct _Decoder: Decodable {
var decoder: Decoder
@IanKeen
IanKeen / Require.swift
Last active June 26, 2024 07:38
SwiftUI: Conditional views from optional bindings with required constraints
public struct Require<Content: View>: View {
@StateObject private var registrar: ViewRegistrar
@ViewBuilder private var content: () -> Content
public init(_ value: Int, @ViewBuilder content: @escaping () -> Content) {
self._registrar = .init(wrappedValue: ViewRegistrar(limit: value))
self.content = content
}
public var body: some View {
@IanKeen
IanKeen / FocusState.swift
Last active June 26, 2024 07:34
SwiftUI: FocusedState shim for < iOS15
import Combine
import SwiftUI
extension View {
public func focused<T>(file: StaticString = #file, _ state: FocusState<T>, equals value: T) -> some View {
modifier(FocusedModifier(state: state, id: value, file: file))
}
}
@propertyWrapper
@IanKeen
IanKeen / EnvironmentValues.swift
Last active June 20, 2025 23:44
SwiftUI: Peek at/extract hidden environment values
import Foundation
import SwiftUI
extension EnvironmentValues {
public func value<T>(_: T.Type = T.self, forKey key: String) -> T? {
guard let value = first(where: { name($0, equals: key) }) else {
print("No EnvironmentValue with key '\(key)' found.")
return nil
}
@IanKeen
IanKeen / LoadingIndicator.swift
Last active June 26, 2024 07:33
SwiftUI: Drop-in Pull to Refresh mechanism
public struct LoadingIndicator: View {
@State private var startingOffset: CGFloat = 0
@State private var space: CGSize = .zero
@State private var size: CGSize = .zero
@State private var progress: Double = 0
@Binding private var isLoading: Bool {
didSet {
if isLoading { action() }
else { progress = 0 }
}
@IanKeen
IanKeen / View+Discover.swift
Last active July 22, 2024 04:23
SwiftUI: discover underlying components to fill in gaps in SwiftUI api
import SwiftUI
extension View {
public func discover<T: UIView>(
where predicate: @escaping (T) -> Bool = { _ in true },
_ closure: @escaping (T) -> Void
) -> some View {
overlay(
DiscoveryView(predicate: predicate, setup: closure)
.frame(width: 0, height: 0)
@IanKeen
IanKeen / LosslessKey.swift
Last active August 12, 2021 20:13
PropertyWrapper: Allow en/de-coding of String keys to non-String types
@propertyWrapper
struct LosslessKey<T: Hashable & LosslessStringConvertible, U> {
var wrappedValue: [T: U]
}
extension LosslessKey: Codable where T: Codable, U: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
var result: [T: U] = [:]
for (key, value) in try container.decode([String: U].self) {
guard let key = T.init(key) else {
@IanKeen
IanKeen / CustomEquatable.swift
Created July 27, 2021 21:26
PropertyWrapper: Custom/Selective Equatable & Hashable conformance using PWs to choose which properties are used
public protocol CustomEquatable: Equatable { }
private extension CustomEquatable {
var equatables: [AnyEquatable] {
return Mirror(reflecting: self)
.children
.compactMap { ($0.value as? EquatingProperty)?.anyEquatable }
}
}