Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active March 29, 2022 13:06
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save IanKeen/d3a22473a8f946bffce213a16e02dc2f to your computer and use it in GitHub Desktop.
Save IanKeen/d3a22473a8f946bffce213a16e02dc2f to your computer and use it in GitHub Desktop.
Custom Decoder that can be used to create Decodable instances that are populated with random values
import Foundation
extension Decodable {
public static func randomInstance() throws -> Self {
let decoder = RandomDecoder()
return try Self(from: decoder)
}
}
private class RandomDecoder: Decoder {
let codingPath: [CodingKey] = []
let userInfo: [CodingUserInfoKey: Any] = [:]
init() { }
func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
return .init(KeyedContainer<Key>())
}
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
return UnkeyedContainer()
}
func singleValueContainer() throws -> SingleValueDecodingContainer {
return SingleValueContainer()
}
struct KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {
let allKeys: [Key] = []
let codingPath: [CodingKey] = []
init() { }
func contains(_ key: Key) -> Bool {
return true
}
func decodeNil(forKey key: Key) throws -> Bool {
return Bool.random()
}
func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
return try T.randomInstance()
}
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {
return .init(KeyedContainer<NestedKey>())
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
return UnkeyedContainer()
}
func superDecoder() throws -> Decoder {
return RandomDecoder()
}
func superDecoder(forKey key: Key) throws -> Decoder {
return RandomDecoder()
}
}
struct UnkeyedContainer: UnkeyedDecodingContainer {
let codingPath: [CodingKey] = []
let count: Int? = (0...5).randomElement()
var isAtEnd: Bool { return currentIndex == (count ?? 0) }
private(set) var currentIndex = 0
init() { }
mutating func decodeNil() throws -> Bool {
return Bool.random()
}
mutating func decode<T: Decodable>(_ type: T.Type) throws -> T {
defer { currentIndex += 1 }
return try T.randomInstance()
}
mutating func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
return .init(KeyedContainer<NestedKey>())
}
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
return UnkeyedContainer()
}
mutating func superDecoder() throws -> Decoder {
return RandomDecoder()
}
}
struct SingleValueContainer: SingleValueDecodingContainer {
let codingPath: [CodingKey] = []
init() { }
func decodeNil() -> Bool { return Bool.random() }
func decode(_ type: Bool.Type) throws -> Bool { return Bool.random() }
func decode(_ type: String.Type) throws -> String { return String.random(length: Int.random(in: 0...50)) }
func decode(_ type: Double.Type) throws -> Double { return Double.random(in: 0...500) }
func decode(_ type: Float.Type) throws -> Float { return Float.random(in: 0...500) }
func decode(_ type: Int.Type) throws -> Int { return Int.random(in: 0...500) }
func decode(_ type: Int8.Type) throws -> Int8 { return Int8.random(in: 0...100) }
func decode(_ type: Int16.Type) throws -> Int16 { return Int16.random(in: 0...500) }
func decode(_ type: Int32.Type) throws -> Int32 { return Int32.random(in: 0...500) }
func decode(_ type: Int64.Type) throws -> Int64 { return Int64.random(in: 0...500) }
func decode(_ type: UInt.Type) throws -> UInt { return UInt.random(in: 0...500) }
func decode(_ type: UInt8.Type) throws -> UInt8 { return UInt8.random(in: 0...100) }
func decode(_ type: UInt16.Type) throws -> UInt16 { return UInt16.random(in: 0...500) }
func decode(_ type: UInt32.Type) throws -> UInt32 { return UInt32.random(in: 0...500) }
func decode(_ type: UInt64.Type) throws -> UInt64 { return UInt64.random(in: 0...500) }
func decode<T: Decodable>(_ type: T.Type) throws -> T {
return try T.randomInstance()
}
}
}
extension String {
private static let source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
static func random<G: RandomNumberGenerator>(length: Int, using generator: inout G) -> String {
let range = 0...length
return range
.compactMap { _ in String.source.randomElement() }
.map(String.init)
.joined()
}
static func random(length: Int) -> String {
var g = SystemRandomNumberGenerator()
return String.random(length: length, using: &g)
}
}
struct User: Decodable {
let string: String
let optional: String?
let integer: Int
let boolean: Bool
let array: [Int]
let nested: Pet
}
struct Pet: Decodable {
let name: String
}
let u = try User.randomInstance()
/* Example Output
User(string: "maeDILfJkgnoxubFJQZpb", optional: nil, integer: 94, boolean: true, array: [34], nested: __lldb_expr_78.Pet(name: "EwZolDJsseaLaahYCvdjy"))
User(string: "DzVphWEYBWHSAhuRrwvYN", optional: Optional("sKmTPmlbdBIgExiFnRmGl"), integer: 397, boolean: false, array: [], nested: __lldb_expr_78.Pet(name: "aMqWeAaPKMszPrLClqBNP"))
User(string: "GNOoLQLwCdGASCpcIijhh", optional: nil, integer: 237, boolean: true, array: [179, 76], nested: __lldb_expr_78.Pet(name: "HzHPqpscGwxxqrRdteaYi"))
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment