This is a Swift port of Ruby's Faker library that generates fake data.

Last update: May 16, 2022

Fakery logo CI Status Carthage Version License Platform

This is a Swift port of Ruby's Faker library that generates fake data.

Are you still bothered with meaningless randomly character strings? Just relax and leave this job to Fakery. It's useful in all the cases when you need to use some dummy data for testing, population of database during development, etc.

NOTE: Generated data is pretty realistic, supports a range of locales, but returned values are not guaranteed to be unique.

Table of Contents

Usage

"Emilie" let lastName = faker.name.lastName() //=> "Hansen" let city = faker.address.city() //=> "Oslo" ">
import Fakery

let faker = Faker(locale: "nb-NO")

let firstName = faker.name.firstName()  //=> "Emilie"
let lastName = faker.name.lastName()    //=> "Hansen"
let city = faker.address.city()         //=> "Oslo"

Localization

Fakery is quite powerful when it comes to generation of locale-specific data. In Resources/Locales you can find JSON files for more than 20 locales, and, of course, it's not a limit. Feel free to contribute and add more!

The default locale is English. When you use one of the available generators and the corresponding key is not found in a JSON file for the currently selected locale Fakery will also check if it exists in "en.json" file.

Generators

Address

"North Avenue" faker.address.secondaryAddress() //=> "Apt. 123" faker.address.streetAddress(includeSecondary: Bool) //=> "12 North Avenue" faker.address.buildingNumber() //=> "123" faker.address.postcode(stateAbbreviation: String) //=> "0884" faker.address.timeZone() //=> "America/Los_Angeles" faker.address.streetSuffix() //=> "Avenue" faker.address.citySuffix() //=> "town" faker.address.cityPrefix() //=> "North" faker.address.stateAbbreviation() //=> "CA" faker.address.state() //=> "California" faker.address.county() //=> "Autauga County" faker.address.country() //=> "United States of America" faker.address.countryCode() //=> "US" faker.address.latitude() //=> -58.17256227443719 faker.address.longitude() //=> -156.65548382095133 faker.address.coordinate() //=> CLLocationCoordinate2D(latitude: 40.97868, longitude: 29.09306) ">
faker.address.city() //=> "Oslo"
faker.address.streetName() //=> "North Avenue"
faker.address.secondaryAddress() //=> "Apt. 123"
faker.address.streetAddress(includeSecondary: Bool) //=> "12 North Avenue"
faker.address.buildingNumber() //=> "123"
faker.address.postcode(stateAbbreviation: String) //=> "0884"
faker.address.timeZone() //=> "America/Los_Angeles"
faker.address.streetSuffix() //=> "Avenue"
faker.address.citySuffix() //=> "town"
faker.address.cityPrefix() //=> "North"
faker.address.stateAbbreviation() //=> "CA"
faker.address.state() //=> "California"
faker.address.county() //=> "Autauga County"
faker.address.country() //=> "United States of America"
faker.address.countryCode() //=> "US"
faker.address.latitude() //=> -58.17256227443719
faker.address.longitude() //=> -156.65548382095133
faker.address.coordinate() //=> CLLocationCoordinate2D(latitude: 40.97868, longitude: 29.09306)

App

"0.1.1" faker.app.author() //=> "Ida Adams" ">
faker.app.name() //=> "Namfix"
faker.app.version() //=> "0.1.1"
faker.app.author() //=> "Ida Adams"

Business

"visa" faker.business.creditCardExpiryDate() //=> "2020-10-12" ">
faker.business.creditCardNumber() //=> "1234-2121-1221-1211"
faker.business.creditCardType() //=> "visa"
faker.business.creditCardExpiryDate() //=> "2020-10-12"

Cat

"British Semipi-longhair" faker.cat.registry() //=> "American Cat Fanciers Association" ">
faker.cat.name() //=> "Shadow"        
faker.cat.breed() //=> "British Semipi-longhair"
faker.cat.registry() //=> "American Cat Fanciers Association"

Commerce

"Music" faker.commerce.productName() //=> "Awesome Wooden Hat" faker.commerce.price() // 90.5 ">
faker.commerce.color() //=> "black"
faker.commerce.department(maximum: Int, fixedAmount: Bool) //=> "Music"
faker.commerce.productName() //=> "Awesome Wooden Hat"
faker.commerce.price() // 90.5

Company

"Inc" faker.company.catchPhrase() //=> "Universal software" faker.company.bs() //=> "implement innovative methodologies" faker.company.logo() // "http://pigment.github.io/fake-logos/logos/medium/color/1.png" ">
faker.company.name() //=> "Adams Inc"       
faker.company.suffix() //=> "Inc"
faker.company.catchPhrase() //=> "Universal software"        
faker.company.bs() //=> "implement innovative methodologies"
faker.company.logo() // "http://pigment.github.io/fake-logos/logos/medium/color/1.png"

Zelda

faker.zelda.game() //=> "Ocarina of Time"   

Gender

"Male" ">
faker.gender.type() //=> "Agender"
faker.gender.binaryType() //=> "Male"

Internet

"example.com" faker.internet.domainWord(alphaNumericOnly: Bool) //=> "domainword" faker.internet.domainSuffix() //=> "com" faker.internet.email() // => "[email protected]" faker.internet.freeEmail() //=> "gmail.com" faker.internet.safeEmail() //=> "[email protected]" faker.internet.password(minimumLength: Int, maximumLength: Int) //=> "e2dddhwd1g5qhvhgfi" faker.internet.ipV4Address() //=> "24.29.18.175" faker.internet.ipV6Address() //=> "ac5f:d696:3807:1d72:2eb5:4e81:7d2b:e1df" faker.internet.url() //=> "http://example.com/ida4" faker.internet.image() //=> "http://lorempixel.com/320/200" faker.internet.templateImage() //=> "http://dummyimage.com/320x200/000000/ffffff" faker.internet.hashtag() //=> "#art" ">
faker.internet.username(separator: String?) //=> "ida4"       
faker.internet.domainName(alphaNumericOnly: Bool) //=> "example.com"        
faker.internet.domainWord(alphaNumericOnly: Bool) //=> "domainword"        
faker.internet.domainSuffix() //=> "com"
faker.internet.email() // => "[email protected]"
faker.internet.freeEmail() //=> "gmail.com"
faker.internet.safeEmail() //=> "[email protected]"
faker.internet.password(minimumLength: Int, maximumLength: Int) //=> "e2dddhwd1g5qhvhgfi"
faker.internet.ipV4Address() //=> "24.29.18.175"
faker.internet.ipV6Address() //=> "ac5f:d696:3807:1d72:2eb5:4e81:7d2b:e1df"
faker.internet.url() //=> "http://example.com/ida4"
faker.internet.image() //=> "http://lorempixel.com/320/200"
faker.internet.templateImage() //=> "http://dummyimage.com/320x200/000000/ffffff"
faker.internet.hashtag() //=> "#art"

Lorem

["dolores", "adipisci", "nesciunt"] faker.lorem.character() //=> "a" faker.lorem.characters(amount: Int) // Default = 255 faker.lorem.sentence(wordsAmount: Int) // Default = 4 faker.lorem.sentences(amount: Int) // Default = 3 faker.lorem.paragraph(sentencesAmount: Int) // Default = 3 faker.lorem.paragraphs(amount: Int) // Default = 3 ">
faker.lorem.word() //=> "repellendus"         
faker.lorem.words(amount: Int) //=> ["dolores", "adipisci", "nesciunt"]      
faker.lorem.character() //=> "a"        
faker.lorem.characters(amount: Int) // Default = 255
faker.lorem.sentence(wordsAmount: Int) // Default = 4
faker.lorem.sentences(amount: Int) // Default = 3
faker.lorem.paragraph(sentencesAmount: Int) // Default = 3
faker.lorem.paragraphs(amount: Int) // Default = 3

Name

"Ida" faker.name.lastName() //=> "Adams" faker.name.prefix() //=> "Mrs." faker.name.suffix() //=> "PhD" faker.name.title() //=> "Lead" ">
faker.name.name() //=> "Ida Adams"        
faker.name.firstName() //=> "Ida"
faker.name.lastName() //=> "Adams"
faker.name.prefix() //=> "Mrs."
faker.name.suffix() //=> "PhD"
faker.name.title() //=> "Lead"

Number

faker.number.randomBool() //=> true or false
faker.number.randomInt() //=> some Int between 0 and 1000
faker.number.randomInt(min: -100, max:50) //=> some Int between -100 and 50
faker.number.randomFloat() //=> some Float between 0 and 1000
faker.number.randomFloat(min: -10.4, max:50) //=> some Float between -10.4 and 50
faker.number.randomCGFloat() //=> some CGFloat between 0 and 1000
faker.number.randomCGFloat(min: 42.42, max:86) //=> some CGFloat between -42.42 and 86
faker.number.randomDouble() //=> some Double between 0 and 1000
faker.number.randomDouble(min: 0, max:1) //=> some Double between 0 and 1
faker.number.increasingUniqueId() //=> every call returns an unique int

Phone number

"333-333-3333" faker.phoneNumber.areaCode() //=> "201" faker.phoneNumber.exchangeCode() //=> "201" faker.phoneNumber.subscriberNumber() //=> "1234" faker.phoneNumber.numberExtension(length: Int) // "123" ">
faker.phoneNumber.phoneNumber() //=> "1-333-333-3333"        
faker.phoneNumber.cellPhone() //=> "333-333-3333"
faker.phoneNumber.areaCode() //=> "201"
faker.phoneNumber.exchangeCode() //=> "201"
faker.phoneNumber.subscriberNumber() //=> "1234"
faker.phoneNumber.numberExtension(length: Int) // "123"

Team

"Alabama bats" faker.team.state() // => "Alabama" ">
faker.team.name() //=> "bats"         
faker.team.creature() //=> "Alabama bats"
faker.team.state() // => "Alabama"

Bank

"AAFMGB21" faker.bank.iban() // => "NL45BUNQ2209931378" faker.bank.bban() //=> ABNA0136468471 ">
faker.bank.name() //=> "ABN AMRO CORPORATE FINANCE LIMITED"         
faker.bank.swiftBic() //=> "AAFMGB21"
faker.bank.iban() // => "NL45BUNQ2209931378"
faker.bank.bban() //=> ABNA0136468471

Hobbit

"Thorin Oakenshield" faker.hobbit.quote() // => "Do you wish me a good morning, or mean that it is a good morning whether I want it or not; or that you feel good this morning; or that it is a morning to be good on?" faker.hobbit.location() //=> "Bree" ">
faker.hobbit.character() //=> "Bilbo Baggins"         
faker.hobbit.thorinsCompany() //=> "Thorin Oakenshield"
faker.hobbit.quote() // => "Do you wish me a good morning, or mean that it is a good morning whether I want it or not; or that you feel good this morning; or that it is a morning to be good on?"
faker.hobbit.location() //=> "Bree"

Car

faker.car.brand() //=> "BMW"

Programming language

"José Valim" ">
faker.programming_language.name() //=> "Elixir"         
faker.programming_language.creator() //=> "José Valim"         

Vehicle

"BMW" faker.vehicle.colors() //=> "Red" ">
faker.vehicle.manufacture() //=> "Volkswagen" 
faker.vehicle.make() //=> "BMW"
faker.vehicle.colors() //=> "Red"

Ham

faker.ham.name() //=> "Taylor Ham"         

House

"living room" ">
faker.house.furniture() //=> "chair"
faker.house.room() //=> "living room" 

Installation

Fakery is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Fakery'

Or alternatively using the Swift Package Manager:

let package = Package(
    //
    dependencies[
        .package(name: "Fakery", url: "https://github.com/vadymmarkov/Fakery", from: "5.0.0"))
    ],
    targets: [
        .target(name: "Foo", dependencies: ["Fakery"]
    ]
)

Use of the Swift Package Manager requires Swift >=5.3.

Contributing

Please see our playbook for guidelines on contributing.

Author

Vadym Markov, [email protected]

License

Fakery is available under the MIT license. See the LICENSE file for more info.

GitHub

https://github.com/vadymmarkov/Fakery
Comments
  • 1. Date type conflicts with Foundation.Date

    When I import Fakery and dry to define anything with Date type, Xcode gives an error 'Date' is ambiguous for type lookup in this context because both Foundation and Fakery define it. Maybe to rename it, or to create a scope Fakery and put all fakery types in this scope? This will allow avoiding any conflicts

    Reviewed by ruben-samsonyan at 2019-06-07 19:13
  • 2. Swift Package Support

    From what I can tell, it is not currently possible to support bundled assets with swift packages, rendering this library incompatible with SPM based projects at the moment since it requires the .json files to generate localized data. (I am hoping to use this library on a server side project). Do you know of any ways to accomplish this with SPM or have any ideas on how to refactor this to be compatible? I made a quick proof of concept that it could technically work by simply maintaining the local source data as .swift files with static strings (in my fork here: https://github.com/Appsaurus/Fakery), however I have a feeling this is not the best approach. Guessing it could have performance issues or simply maintainability issues since Xcode seems to sometimes choke on strings of that size.

    Any thoughts on this? If you think that is actually a viable approach I wouldn't mind refactoring and putting up a PR. Otherwise, maybe the best approach is to wait until SPM updates to support bundled assets, and I can just maintain my own fork with this approach as a stopgap until then.

    Anyway...thanks for the great library!

    Reviewed by Strobocop at 2018-05-22 16:47
  • 3. SwiftyJSON depency

    SwiftyJSON decency is not pointing to any specific release, though due to the new SwiftyJSON Release (Support for Swift 3.0) my project (Swift2.2) can't use Fakery anymore.

    Should be fixed with replacing s.dependency 'SwiftyJSON', '< 3.0.0' to pod-spec.

    Reviewed by d3pwnd at 2016-09-22 09:01
  • 4. Added the fake data to generate car brands names

    Hello I would like to contribute adding the generated data for Car Brands it's update as far I saw, they have tesla so looks really update ;)

    Any change please let me know 👍

    Reviewed by felipeflorencio at 2019-05-08 15:27
  • 5. Update rangeAt so compatible with swift 4

    The following build commands failed: CompileSwift normal arm64 /Users/xxxxxx/xxxxx-ci/workspace/XCUITest/ios-xcuitest-run/Pods/Fakery/Sources/Fakery/Data/Parser.swift CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler

    Testing failed: 'range(at:)' has been renamed to 'rangeAt(:)' 'range(at:)' has been renamed to 'rangeAt(:)' 'range(at:)' has been renamed to 'rangeAt(:)' 'range(at:)' has been renamed to 'rangeAt(:)'

    Reviewed by ericlmartinezo at 2018-04-26 16:46
  • 6. Add support for SwiftPM resources via Swift 5.3

    See also this WWDC 2020 session: https://developer.apple.com/videos/play/wwdc2020/10169 Or read the related proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md

    Note that I have only made this compatible when used with SwiftPM. Integration via other dependency managers might be broken.

    Fixes #129.

    To use this branch before it's merged, use this in your Package.swift:

    .package(name: "Fakery", url: "https://github.com/Jeehut/Fakery.git", .branch("master")),
    
    Reviewed by Jeehut at 2020-07-08 07:20
  • 7. Xcode 11 install via SPM won't compile because of Nimble

    When I try to install Fakery using Xcode 11 SPM it throws an error when trying to clone Nimble

    error.txt

    Is there something we could do or we just have to wait for Nimble to get fixed?

    Reviewed by Henriquedrdc at 2019-09-24 19:33
  • 8. 1.2.0 not available on cocoapods

    When i do pod 'Fakery' I keep have the 1.1.2 version. I need to force :path in my pod file. I guess pod spec has not been update in cocoa pods repo ?

    Analyzing dependencies Pre-downloading: Fakery from https://github.com/vadymmarkov/Fakery.git Installing Fakery 1.2.0 (was 1.1.2) Generating Pods project Integrating client project Sending stats

    Reviewed by loicgriffie at 2016-06-30 10:38
  • 9. Random Numbers

    I would love to easily generate random numbers and unique/continuously increasing Ids with this library, too. arc4random and co aren't kind a "swifty" to use so it would be nice to have a wrapper around it.

    Would you consider this to be part of this lib and would appreciate a pull request, since it's nothing that needs localisation?

    Reviewed by FGoessler at 2016-03-22 15:39
  • 10. ipV6Address() bug

    ipV6Address() makes, in example, following String. "5EF8:D497:B05E:2E34:337C:38A2:6F7B" Components are just 7. I think it should be 8. The problem is 1..<8. It iterates only 7, not 8.

    And there is following code in ipV6Address() arc4random() % 65535 I think 65535 ( 0xFFFF) should be possible, but this code can't make 65535.

    Reviewed by delixion at 2018-09-12 07:16
  • 11. Add framework resources path

    This pull request adds support for Fakery import by framework (Carthage).

    When Fakery is imported as Fakery.framework all the assets of the project are placed in the root folder, like the example shown below: screen shot 2015-11-23 at 15 15 51

    With this change Fakery will find for the json resources on the root folder and at the Resources/Locales folder. This will add support for both CocoaPods and Carthage.

    Reviewed by diogoguimaraes at 2015-11-23 15:23
  • 12. Support seeded random number generators

    Hi!

    I've added support for seeded RNG by allowing the passing of any RandomNumberGenerator to Faker.

    Caveats:

    • I ignored Swift <= 4.2. It might not compile with the older swift versions; if it does, it won't used the RNG that was passed in. Not sure if dropping support for 4.2 is an option at this point.
    • I didn't write a unit test (I'm not familiar with QuickSpec and don't have more time to spend on this right now).

    That being said, you can use this like this:

    let faker = Faker(randomNumberGenerator: SomeAdvancedRandomNumberGenerator())

    Unfortunately, seeded RNG is somewhat hidden in iOS; my first idea was to use the RNG provided by GameKit:

     import GameKit
    
     extension GKMersenneTwisterRandomSource: RandomNumberGenerator {}
     let faker = Faker(randomNumberGenerator: GKMersenneTwisterRandomSource(seed: 42))
    

    We need to make GKMersenneTwisterRandomSource conform to RandomNumberGenerator, which is fortunately quite easy. We can then use it as the seeded RNG.

    However, this requires that GameKit is available. In the end, I didn't want introduce a GameKit dependency, and the whole thing is overkill anyway for my testing purposes, so I implemented a custom "RNG" which works fine for my case:

    public class AdvancedRandomNumberGenerator: RandomNumberGenerator {
        var currentIndex = 0
        
        // chosen by fair dice roll
        // guaranteed to be random
        lazy var randomValues: [UInt64] = [4151371853236615391, 7134936793715064765, 8637388537612094686, ...]
        
        public func next() -> UInt64 {
            currentIndex += 1
            return randomValues[currentIndex % randomValues.count]
        }
    }
    
    Reviewed by julasamer at 2021-06-11 09:47
  • 13. Set iOS deployment target to 9.0 on pbxproj

    Hi

    I use this lib thought Carthage, but once I updated from version 5.0.0 to 5.1.0 I noticed that the minimum deployment target has changed.


    This PR sets it to version 9.0, the same defined in the Package.swift file.

    Reviewed by grsouza at 2021-04-16 13:26
  • 14. Add ability to load a file from local bundle instead

    Using this library with SPM will not bring in the resources (those lovely little json files)

    I've added an alternative initializer where you can specify a path.

    e.g

    let locale = "en"
    let stringPath = Bundle.main.path(forResource: locale, ofType: "json")
    let faker = Faker(locale: locale, path: stringPath!)
    

    BEFORE this is considered merge worthy...

    • [ ] Can only load base translation files: 'en, es, ru, etc...' Trying to load a .json with "-" will fail (en-GB) I couldn't be bothered yet to step through the parser to find where it loads the "en" after loading the extras from "en-gb"
    • [ ] I added some simple tests but better if the creator can review and amend to properly cover.
    Reviewed by morgz at 2020-06-19 14:23
  • 15. Random non-numeric or non-alphanumeric strings

    Hi @vadymmarkov,

    Fakery's been useful in my project so far. Very happy about it. It'd be nice to have some methods to generate strings that non-numerical and non-alphanumerical.

    Thanks!

    Reviewed by tony-hoang999 at 2020-05-01 16:17
  • 16. Invalid email addresses being generated

    I am currently using Faker().internet.safeEmail(), and it's producing invalid results. Right now, I have an email address of @example.com returned from that method, so something is going awry.

    I've seen similar when using the plain Faker().internet.email() method, except in that case it was missing everything after the @.

    I'm using Xcode 11.2b1 (although this happens under 11.0 and 11.1 GM as well).

    Reviewed by tonyarnold at 2019-10-03 04:35
Fake iPhone real devices location using this small app

Project Title iPhone IP Spoofer for Real Devices. Description IP, or internet protocol, is a string of numbers that identifies your iPhone, iPad, or w

Nov 25, 2021
SwiftCheck is a testing library that automatically generates random data for testing of program properties

SwiftCheck QuickCheck for Swift. For those already familiar with the Haskell library, check out the source. For everybody else, see the Tutorial Playg

May 13, 2022
AutoMocker is a Swift framework that leverages the type system to let you easily create mocked instances of your data types.

AutoMocker Context AutoMocker is a Swift framework that leverages the type system to let you easily create mocked instances of your data types. Here's

Jan 26, 2022
This is a simple test app getting data from network to practice a tad bit.

test This is a simple test app getting data from network to practice a tad bit. Start Nothing fancy, no CocoaPods, just clone and run! Architecture Ju

Oct 9, 2021
Creation of data model easily, with no headache.

DataFixture Create data models easily, with no headache. DataFixture is a convenient way to generate new data for testing / seeding your Realm Databas

Dec 9, 2020
The Big List of Naughty Strings is a list of strings which have a high probability of causing issues when used as user-input data.
The Big List of Naughty Strings is a list of strings which have a high probability of causing issues when used as user-input data.

The Big List of Naughty Strings is a list of strings which have a high probability of causing issues when used as user-input data. I have put together

Apr 25, 2022
Test-To-Do-List - Test To Do List with core data
Test-To-Do-List - Test To Do List with core data

test-To-Do-List This is my first pet project with core data Launch screen Main s

Feb 26, 2022
PinpointKit is an open-source iOS library in Swift that lets your testers and users send feedback with annotated screenshots using a simple gesture.
PinpointKit is an open-source iOS library in Swift that lets your testers and users send feedback with annotated screenshots using a simple gesture.

PinpointKit is an open-source iOS library in Swift that lets your testers and users send feedback with annotated screenshots using a simple gesture. F

Apr 25, 2022
The XCTest Project, A Swift core library for providing unit test support

XCTest The XCTest library is designed to provide a common framework for writing unit tests in Swift, for Swift packages and applications. This version

May 10, 2022
An elegant library for stubbing HTTP requests with ease in Swift

Mockingjay An elegant library for stubbing HTTP requests in Swift, allowing you to stub any HTTP/HTTPS using NSURLConnection or NSURLSession. That inc

May 11, 2022
MockSwift is a Mock library written in Swift.

Welcome to MockSwift MockSwift allows you to write mocks and make better tests. Because MockSwift is an open source library 100% written in Swift, it

Mar 28, 2022
Test Library for Swift's Error Handling

CatchingFire CatchingFire is a Swift test framework, which helps making expectations against the error handling of your code. It provides for this pur

May 16, 2022
Lightweight touch visualization library in Swift. A single line of code and visualize your touches!
Lightweight touch visualization library in Swift. A single line of code and visualize your touches!

TouchVisualizer is a lightweight pure Swift implementation for visualising touches on the screen. Features Works with just a single line of code! Supp

May 13, 2022
A Swift test double library. Guava - looks like an apple but it's not.

Guava Guava helps you to make your unit tests more flexible. It allows you to replace parts of your system under test with a test double objects. Tabl

Mar 22, 2022
Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class.

Freezer Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class. Usage Once Freezer.start() has been invoked,

Jul 17, 2017
Trust - Swift DCI Test Library

Trust Swift - DCI Pattern Test Library Support SPM How to use ? struct User {

Feb 13, 2022
ShortWebCore - This iOS library lets you run automations on Web Views.

This iOS library lets you run automations on Web Views. Example (Optional) Declare class conforming to AutomationRunnerDelegate: import S

May 14, 2022
Library for unifying the approach to network mocking in iOS unit- & UI-tests.

TinkoffMockStrapping Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installati

May 4, 2022
A simple and lightweight matching library for XCTest framework.
A simple and lightweight matching library for XCTest framework.

Match A simple and lightweight matching library for XCTest framework. Getting started Swift Package Manager You can add Match to your project by addin

Nov 14, 2021