Skip to content

Instantly share code, notes, and snippets.

View IanKeen's full-sized avatar
🏂

Ian Keen IanKeen

🏂
View GitHub Profile
@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 / 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 / ShareReplay.swift
Created June 3, 2021 19:38
Combine: ShareReplay - Shares the underlying resource and replays the last emitted value (if there was one) to new subscribers
extension Publisher {
func shareReplay() -> AnyPublisher<Output, Failure> {
let subject = CurrentValueSubject<Output?, Failure>(nil)
return map { $0 }
.multicast(subject: subject)
.autoconnect()
.compactMap { $0 }
.eraseToAnyPublisher()
}
@IanKeen
IanKeen / Banner.swift
Last active March 1, 2024 21:49
Persistent banner in SwiftUI : Supports top/bottom edge, swipe to dismiss + auto dismissal after time
public enum BannerEdge {
case top, bottom
}
public enum BannerAutoDismiss {
case after(TimeInterval)
case never
}
extension View {
public func banner<C: View>(
@IanKeen
IanKeen / AnyCodingKey.swift
Created March 1, 2019 18:40
AnyCodingKey: Helpful for a range of Codable tricks
struct AnyCodingKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(intValue: Int) {
self.intValue = intValue
self.stringValue = "\(intValue)"
}
init?(stringValue: String) {
self.intValue = nil
@IanKeen
IanKeen / DictionaryDecoder.swift
Created March 1, 2019 18:43
DictionaryDecoder
import Foundation
class DictionaryDecoder {
init() { }
func decode<T: Decodable>(_ type: T.Type, from data: [String: Any]) throws -> T {
let decoder = _Decoder(codingPath: [], source: data)
return try T(from: decoder)
}
}
@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 / View+Discover.swift
Last active February 13, 2024 08:00
SwiftUI: discover underlying components to fill in gaps in SwiftUI api
import SwiftUI
extension View {
public func discover<T: UIView>(
tag: Int = .random(in: (.min)...(.max)),
where predicate: @escaping (T) -> Bool = { _ in true },
_ closure: @escaping (T) -> Void
) -> some View {
self.overlay(
DiscoveryView(tag: tag)
@IanKeen
IanKeen / Example_Complex.swift
Last active January 23, 2024 07:53
PropertyWrapper: @transaction binding for SwiftUI to make changes to data supporting commit/rollback
struct User: Equatable {
var firstName: String
var lastName: String
}
@main
struct MyApp: App {
@State var value = User(firstName: "", lastName: "")
@State var showEdit = false
@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