struct PasswordGenerator {
var length: Int
var characterSet: CharacterSet {
didSet {
// re-compute the characters array after the character set changing.
characters = Array(characterSet.characters)
}
}
// cached characters.
private var characters: [Character] // cached characters.
// default options of just using letters (upper and lowercase)
init(length: Int, characterSet: CharacterSet = .letters) {
self.length = length
self.characterSet = characterSet
self.characters = Array(characterSet.characters)
}
func/// generate()Generate ->a Stringnew {
password with a given length from the given character set.
/// we cannot generate a/// password- ifPrecondition: there'sThe nocharacter charactersset must be non-empty.
func generate() -> String //{
to generate it from.
guard precondition(!characters.isEmpty,
else {
return ""
"Cannot generate password }from an empty character set.")
let characterCount = UInt32(characters.count)
let result = (0 ..< length).map { _ in
characters[Int(arc4random_uniform(characterCount))]
}
return String(result)
}
}
- Caching the characters to generate the password from (re-creating upon the character set changing, with a
didSet
observer) - Using our custom
.letters
option as the default character set to generate from. - Guarding against an empty character option set being selected by just returning an empty stringusing a
precondition
check.
struct PasswordGenerator {
var length: Int
var characterSet: CharacterSet {
didSet {
// re-compute the characters array after the character set changing.
characters = Array(characterSet.characters)
}
}
// cached characters.
private var characters: [Character]
// default options of just using letters (upper and lowercase)
init(length: Int, characterSet: CharacterSet = .letters) {
self.length = length
self.characterSet = characterSet
self.characters = Array(characterSet.characters)
}
func generate() -> String {
// we cannot generate a password if there's no characters
// to generate it from.
guard !characters.isEmpty else {
return ""
}
let characterCount = UInt32(characters.count)
let result = (0 ..< length).map { _ in
characters[Int(arc4random_uniform(characterCount))]
}
return String(result)
}
}
- Caching the characters to generate the password from (re-creating upon the character set changing, with a
didSet
observer) - Using our custom
.letters
option as the default character set to generate from. - Guarding against an empty character option set being selected by just returning an empty string.
struct PasswordGenerator {
var length: Int
var characterSet: CharacterSet {
didSet {
// re-compute the characters array after the character set changing.
characters = Array(characterSet.characters)
}
}
private var characters: [Character] // cached characters.
// default options of just using letters (upper and lowercase)
init(length: Int, characterSet: CharacterSet = .letters) {
self.length = length
self.characterSet = characterSet
self.characters = Array(characterSet.characters)
}
/// Generate a new password with a given length from the given character set.
/// /// - Precondition: The character set must be non-empty.
func generate() -> String {
precondition(!characters.isEmpty,
"Cannot generate password from an empty character set.")
let characterCount = UInt32(characters.count)
let result = (0 ..< length).map { _ in
characters[Int(arc4random_uniform(characterCount))]
}
return String(result)
}
}
- Caching the characters to generate the password from (re-creating upon the character set changing, with a
didSet
observer) - Using our custom
.letters
option as the default character set to generate from. - Guarding against an empty character option set being selected by using a
precondition
check.
(Note that this will conflict with Foundation
's CharacterSet
, but given as it's a nested type, this shouldn't be problematic, as it will only conflict in the scope of PasswordGenerator
)
(Note that this will conflict with Foundation
's CharacterSet
, but given as it's a nested type, this shouldn't be problematic)
(Note that this will conflict with Foundation
's CharacterSet
, but given as it's a nested type, this shouldn't be problematic, as it will only conflict in the scope of PasswordGenerator
)
extension PasswordGenerator {
struct CharacterSet : OptionSet {
static let lowercase = CharacterSet(rawValue: 1 << 0)
static let uppercase = CharacterSet(rawValue: 1 << 1)
static let numbers = CharacterSet(rawValue: 1 << 2)
static let symbols = CharacterSet(rawValue: 1 << 3)
static let letters: CharacterSet = [.lowercase, .uppercase]
static let alphanumeric: CharacterSet = [.letters, .numbers]
let rawValue: Int
var characters: Set<Character> {
var characters = Set<Character>()
if contains(.lowercase) { // in Swift 4, remove ".characters"
characters.formUnion("abcdefghijklmnopqrstuvwxyz".characters)
}
if contains(.uppercase) {
characters.formUnion("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
}
if contains(.numbers) {
characters.formUnion("1234567890".characters)
}
if contains(.symbols) {
characters.formUnion("!@#$%^&*".characters)
}
return characters
}
}
}
This also lets us define some custom character sets, such as letters
and alphanumeric
, which are a combination of other sets – as well as encapsulate the logic to produce the set of characters to work with, as this could well be re-used.
Note that we're now using a Set
to represent the collection of characters we can choose from, as it better describes the collection (unique characters that aren't ordered) – not to mention allowing for constant lookup time of a given character in the set.
You can see we're now also:
extension PasswordGenerator {
struct CharacterSet : OptionSet {
static let lowercase = CharacterSet(rawValue: 1 << 0)
static let uppercase = CharacterSet(rawValue: 1 << 1)
static let numbers = CharacterSet(rawValue: 1 << 2)
static let symbols = CharacterSet(rawValue: 1 << 3)
static let letters: CharacterSet = [.lowercase, .uppercase]
static let alphanumeric: CharacterSet = [.letters, .numbers]
let rawValue: Int
var characters: Set<Character> {
var characters = Set<Character>()
if contains(.lowercase) {
characters.formUnion("abcdefghijklmnopqrstuvwxyz".characters)
}
if contains(.uppercase) {
characters.formUnion("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
}
if contains(.numbers) {
characters.formUnion("1234567890".characters)
}
if contains(.symbols) {
characters.formUnion("!@#$%^&*".characters)
}
return characters
}
}
}
This also lets us define some custom character sets, such as letters
and alphanumeric
, which are a combination of other sets – as well as encapsulate the logic to produce the set of characters to work with, as this could well be re-used. Note that we're now using a Set
to represent the collection of characters we can choose from, as it better describes the collection (unique characters that aren't ordered).
You can see we're now:
extension PasswordGenerator {
struct CharacterSet : OptionSet {
static let lowercase = CharacterSet(rawValue: 1 << 0)
static let uppercase = CharacterSet(rawValue: 1 << 1)
static let numbers = CharacterSet(rawValue: 1 << 2)
static let symbols = CharacterSet(rawValue: 1 << 3)
static let letters: CharacterSet = [.lowercase, .uppercase]
static let alphanumeric: CharacterSet = [.letters, .numbers]
let rawValue: Int
var characters: Set<Character> {
var characters = Set<Character>()
if contains(.lowercase) { // in Swift 4, remove ".characters"
characters.formUnion("abcdefghijklmnopqrstuvwxyz".characters)
}
if contains(.uppercase) {
characters.formUnion("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
}
if contains(.numbers) {
characters.formUnion("1234567890".characters)
}
if contains(.symbols) {
characters.formUnion("!@#$%^&*".characters)
}
return characters
}
}
}
This also lets us define some custom character sets, such as letters
and alphanumeric
, which are a combination of other sets – as well as encapsulate the logic to produce the set of characters to work with, as this could well be re-used.
Note that we're now using a Set
to represent the collection of characters we can choose from, as it better describes the collection (unique characters that aren't ordered) – not to mention allowing for constant lookup time of a given character in the set.
You can see we're now also: