Skip to content

Instantly share code, notes, and snippets.

View rnapier's full-sized avatar

Rob Napier rnapier

View GitHub Profile
rnapier / TestVO.swift
Created July 31, 2023 20:04
Demonstration of VoiceOver bug
View TestVO.swift
// Demonstration of FB12811151
// On some devices (so far we haven't found any newer than an 2020 SE2), VoiceOver will allow you to select
// the link by swiping down, and then will immediately re-select the entire text. The user generally cannot
// follow the link because it snaps back too quickly.
// On one iPhone 8, this did not reproduce until the device went to sleep, then it reliably reproduced.
import SwiftUI
let text = try! AttributedString(markdown: "Here is a link to [ArchiveOrg]( It should be selectable with the rotor without jumping back to the whole text.")
View ValueStream.swift
import Foundation
public class Disposable {
private var isDisposed = false
private let _dispose: () -> Void
public func dispose() {
if !isDisposed {
isDisposed = true
rnapier / GridView.swift
Last active September 27, 2020 20:32
GridView that makes me cry
View GridView.swift
// This GridView makes me cry. It is recreating an HTML-style bordered table, sized to
// its data, with a header. It requires a GeometryReader and Preferences, which might
// be unavoidable, but it also requires a *horrible* DispatchQueue.main.async in updateMaxValue.
// This means it doesn't work in Previews, and completely breaks the idea of "declarative" UI.
import SwiftUI
struct WidthPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
View LengthLimitedTextField.swift
struct LengthLimitedTextField: UIViewRepresentable {
var title: String
@Binding var text: String
var maxLength: Int
var onCommit: () -> Void
init(_ title: String, text: Binding<String>, maxLength: Int = 255, onCommit: @escaping () -> Void) {
self.title = title
self._text = text
self.maxLength = maxLength
View decodeJSON.swift
// Based on
extension Bundle {
// This is synchronous, which is bad if called on the main queue.
func decodeJSON<T: Decodable>(_ type: T.Type = T.self, from filename: String) throws -> T {
guard let path = self.url(forResource: filename, withExtension: nil) else {
throw NSError(domain: NSCocoaErrorDomain,
code: CocoaError.fileNoSuchFile.rawValue,
userInfo: [NSFilePathErrorKey: filename])
View StyledText.swift
// TextStyle.swift
// Created by Rob Napier on 12/20/19.
// Copyright © 2019 Rob Napier. All rights reserved.
import SwiftUI
public struct TextStyle {
rnapier / assertion.swift
Last active August 21, 2020 14:00
AssertionFailure as a type
View assertion.swift
/// Boring setup. See below for the good parts
public class Logger {
public static let root = Logger(subsystem: .none, parent: nil)
public let subsystem: Subsystem
public let parent: Logger?
public init(subsystem: Subsystem, parent: Logger? = .root) {
self.subsystem = subsystem
View JSONStringConvertible.swift
import Foundation
public protocol JSONStringConvertible: class {
var jsonString: String { get }
extension Logger {
// Converts an arbitrary object into some that is JSON-safe
static func makeJSONObject(_ object: Any) -> Any {
if let jsonObj = object as? JSONStringConvertible {
rnapier / TypeCoder.swift
Last active June 13, 2019 15:04
Decode based on a type field
View TypeCoder.swift
import Foundation
/// Simple example of decoding different values based on a "type" field
let json = Data("""
"type": "user",
"name": "Alice"
View APIClient.swift
import Foundation
// Provides a nice bare-init for ID types
protocol IDType: Codable, Hashable {
associatedtype Value
var value: Value { get }
init(value: Value)
extension IDType {