Git Product home page Git Product logo

burritos's Introduction

๐ŸŒฏ๐ŸŒฏ Burritos

Bitrise Build Status Swift Package Manager Platform

A collection of well tested Swift Property Wrappers.

๐Ÿšง Beta Software: ๐Ÿšง

Property Wrappers were announced by Apple during WWDC 2019. They are a fundamental component in SwiftUI syntax sugar hence Apple pushed them into the Swift 5.1 beta, skipping the normal Swift Evolution process. Final proposals are being discussed right now in the forums so keep in ming the API can change.

Requirements

Xcode Beta 11.0 & Swift 5.1

Installation

Swift Package Manager

Xcode 11.0+ integration

  1. Open MenuBar โ†’ File โ†’ Swift Packages โ†’ Add Package Dependency...
  2. Paste the package repository url https://github.com/guillermomuntaner/Burritos and hit Next.
  3. Select your rules. Since this package is in pre-release development, I suggest you specify a concrete tag to avoid pulling breaking changes.

Package.swift

If you already have a Package.swift or you are building your own package simply add a new dependency:

dependencies: [
    .package(url: "https://github.com/guillermomuntaner/Burritos", from: "0.0.2")
]

Cocoapods

Add Burritos to your Podfile:

pod 'Burritos', '~> 0.0.2'

Each wrapper is a submodule, so you add just the one(s) you want

pod 'Burritos/Copying', '~> 0.0.2'
pod 'Burritos/UndoRedo', '~> 0.0.2'
pod 'Burritos/UserDefault', '~> 0.0.2'

@AtomicWrite

A property wrapper granting atomic write access to the wrapped property. Reading access is not atomic but is exclusive with write & mutate operations. Atomic mutation (read-modify-write) can be done using the wrapper mutate method.

@Atomic var count = 0

// You can atomically write (non-derived) values directly:
count = 99

// To mutate (read-modify-write) always use the wrapper method:
DispatchQueue.concurrentPerform(iterations: 1000) { index in
    $count.mutate { $0 += 1 }
}

print(count) // 1099

@Clamping

A property wrapper that automatically clamps its wrapped value in a range.

@Clamping(value: 0.0, range: 0...1)
var alpha: Double

alpha = 2.5
print(alpha) // 1.0

alpha = -1.0
print(alpha) // 0.0

@Copying

A property wrapper arround NSCopying that copies the value both on initialization and reassignment. If you are tired of calling .copy() as! X you will love this one.

@Copying var path: UIBezierPath = .someInitialValue

public func updatePath(_ path: UIBezierPath) {
    self.path = path
    // You don't need to worry whoever called this method mutates the passed by reference path.
    // Your stored self.path contains a copy.
}

@DynamicUIColor

A property wrapper arround UIColor to easily support dark mode.

@DynamicUIColor(light: .white, dark: .black)
var backgroundColor: UIColor

view.backgroundColor = backgroundColor // .white for light mode, .black for dark mode.

To propery support dark mode you should set your colors on load and any time the userInterfaceStyle changes. You can use the UIViewController.traitCollectionDidChange method to detect changes:

override func viewDidLoad() {
    super.viewDidLoad()
    bindColors() // Set initial value
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if previousTraitCollection.userInterfaceStyle != UITraitCollection.current.userInterfaceStyle {
        bindColors() // userInterfaceStyle has changed; reset colors
    }
}

func bindColors() {
    view.backgroundColor = backgroundColor
    ...
}

Original idea courtesy of @bardonadam

@Expirable

A property wrapper arround a value that can expire. Getting the value after given duration or expiration date will return nil.

@Expirable(duration: 60)
var apiToken: String?

// New values will be valid for 60s
apiToken = "123456abcd"
print(apiToken) // "123456abcd"
sleep(61)
print(apiToken) // nil

// You can also construct an expirable with an initial value and expiration date:
@Expirable(initialValue: "zyx987", expirationDate: date, duration: 60)
var apiToken: String?
// or just update an existing one:
$apiToken.set("zyx987", expirationDate: date)

Courtesy of @v_pradeilles

@LateInit

A reimplementation of Swift Implicitly Unwrapped Optional using a property wrapper.

var text: String!
// or 
@LateInit var text: String

// Note: Accessing it before initializing will result in a fatal error:
// print(text) // -> fatalError("Trying to access LateInit.value before setting it.")

// Later in your code:
text = "Hello, World!"

@Lazy

A property wrapper which delays instantiation until first read access. It is a reimplementation of Swift lazy modifier using a property wrapper.

@Lazy var result = expensiveOperation()
...
print(result) // expensiveOperation() is executed at this point

As an extra on top of lazy it offers reseting the wrapper to its "uninitialized" state.

@LazyConstant

Same as @Lazy + prevents changing or mutating its wrapped value.

@LazyConstant var result = expensiveOperation()
...
print(result) // expensiveOperation() is executed at this point

result = newResult // Compiler error

Note: This wrapper prevents reassigning the wrapped property value but NOT the wrapper itself. Reassigning the wrapper $value = LazyConstant(initialValue: "Hola!") is possible and since wrappers themselves need to be declared variable there is no way to prevent it.

@UndoRedo

A property wrapper that automatically stores history and supports undo and redo operations.

@UndoRedo var text = ""

text = "Hello"
text = "Hello, World!"

$text.canUndo // true
$text.undo() // text == "Hello"

$text.canRedo // true
$text.redo() // text == "Hello, World!"

You can check at any time if there is an undo or a redo stack using canUndo & canRedo properties, which might be particularly usefull to enable/disable user interface buttons.

Original idea by @JeffHurray

@UserDefault

Type safe access to UserDefaults with support for default values.

@UserDefault("test", defaultValue: "Hello, World!")
var test: String

By default it uses the standard user defauls. You can pass any other instance of UserDefaults you want to use via its constructor, e.g. when you use app groups:

let userDefaults = UserDefaults(suiteName: "your.app.group")
@UserDefault("test", defaultValue: "Hello, World!", userDefaults: userDefaults)
var test: String

@Cached

TODO

@Dependency (Service locator pattern)

TODO

Thread safety

TODO

Command line parameters

TODO

Property observer -> willSet, didSet !

TODO: Reimplement

Print/Log

TODO: A property wrapper that prints/logs any value set.

About Property Wrappers

A property wrapper, or property delegate, is a pattern to abstract custom read and writte behaviours. In Swift we can use a generic struct exposing a computed property whose get & set methods we can implement however we want. This allows us to reuse some get/set logic with any type:

struct SomeWrapper<T> {
    ...
    var value: T {
        get {
            // Your custom get logic
            ...
        }
        set {
            // Your custom set logic
            ...
        }
    }
}

In plain old Swift you would have used this as follows:

// Instantiate your property wrapper
var wrappedProperty = SomeWrapper<String>("Hello, World!")

// Access its value
wrappedProperty.value

// In order to avoid having to unwrap wrappedProperty.value all the time, you can use a computed property
var value: String {
    get { return wrappedProperty.value }
    set { wrappedProperty.value = newValue }
}

// So now accessing the value is much easier
value

Swift 5.1 leverages annotations and the compiler to generate this code for you. It also bridges the assignment operator to the wrapper constructor which leads to a really nice syntax. So now, we can simply use:

@SomeWrapper var value = "Hello, World!"

// Access the wrapped value:
value

// You can also access the wrapper by using $
$value // <- This is the SomeWrapper<String> instance

Interesting reads:

Equivalents in other languages:

License

Burritos is released under the MIT license.

burritos's People

Contributors

guillermomuntaner avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.