Skip to content

gonzula/Awesome-swift-types

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Awesome-swift-types

A list of awesome value types in Swift, for a more type safe code

For motivation check and some explanation on why this might be useful, check: Bringing runtime errors to compile time (in Swift with Types)

This repo contains some cool types. Some of them are a specialization from the generic type ValidatedString.

Some usage examples of ValidatedString:

Let's assume your model contains an User with a username and a SSN:

struct User { let username: String let ssn: String }

That's not type safe, so let's do better with ValidatedString<Validator>

With very little code you create very safe data types with a lot of cool features

// First, let's create a username validator typealias Username = ValidatedString<UsernameValidator> enum UsernameValidator: StringValidator { /// Accepts any username with length between 3 and 10 inclusive static func validate(_ string: String) -> String? { let username = string.trimmingCharacters(in: .whitespacesAndNewlines) guard (3...10).contains(username.count) else {return nil} return username } } // and a SSN validator typealias SSN = ValidatedString<SSNValidator> enum SSNValidator: StringValidator { /// Accepts ssn with or without the dash, but it always stores without the dashes static func validate(_ string: String) -> String? { let string = string.trimmingCharacters(in: .whitespacesAndNewlines) let regex = #"\d{3}-?\d{2}-?\d{4}"# guard NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: string) else {return nil} return string.replacingOccurrences(of: "-", with: "") } }

And change our model to use those new types

struct User { let username: Username let ssn: SSN }

The features you get for free

Convertible from string literals:

let usernames: [Username] = [ "Gonzula", "jgcmarins" ] let ssn: SSN = "078-05-1120" let user = User(username: "gonzula", ssn: "078051120")

Or init from expressions, resulting in a optional

let userInput = "Gonzula" let optionalUsername1 = Username(userInput) // Optional("Gonzula") let invalidUserInput = "InvalidUserInputBucauseItsVeryBig" let optionalUsername2 = Username(invalidUserInput) // nil

LosslessStringConvertible/CustomStringConvertible

let username: Username = "Gonzula" let convertedToString = String(username) // Gonzula print("The username is \(username)") // The username is Gonzula

Codable support

extension User: Codable {} let json = """ {  "username": "Gonzula",  "ssn": "078051120" } """.data(using: .utf8)! let decoder = JSONDecoder() let decodedUser = try! decoder.decode(User.self, from: json) let encoder = JSONEncoder() let encodedJson = try! encoder.encode(decodedUser) String(data: encodedJson, encoding: .utf8)! // {"ssn":"078051120","username":"Gonzula"}

Custom hashables and comparisons

Useful for case insensitive comparison, but still preserving input's original case

extension UsernameValidator: StringNormalizer, StringComparator{ static func normalize(_ rawValue: String) -> String { return rawValue.lowercased() } static func areInIncreasingOrder(_ lhs: String, _ rhs: String) -> Bool { return normalize(lhs).localizedCaseInsensitiveCompare(normalize(rhs)) == .orderedAscending } } let scores: [Username: Int] = ["Foo": 10, "Bar": 5] scores["FOO"] // 10 scores["bar"] // 5 let players: [Username] = ["Foo", "bar"].sorted() // [bar, Foo] // while a simple string sorting will result in a different order let stringPlayers: [String] = ["Foo", "bar"].sorted() // [Foo, bar]

Custom computed properties, useful for human printable formats

extension SSN { var formatted: String { let digits = Array(rawValue) // Don't have to worry about out of range index because the data was already validated let groups = [digits[0..<3], digits[3..<5], digits[5..<9]] let formatted = groups.joined(separator: "-") return String(formatted) } } let ssnNumber = "078051120" SSN(ssnNumber)?.formatted // 078-05-1120

About

A list of awesome value types in Swift, for a more type safe code

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages