A collection of Swift Property Wrappers (formerly "Property Delegates")

Overview

🌯 🌯 Burritos

Bitrise Build Status Swift Package Manager Platform

A collection of well tested Swift Property Wrappers.

Requirements

Xcode 11 & Swift 5

Installation

Swift Package Manager

Xcode 11+ 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.3")
]

Cocoapods

Add Burritos to your Podfile:

pod 'Burritos', '~> 0.0.3'

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

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

@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(range: 0...1)
var alpha: Double = 0.0

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.
}

@DefaultValue

A property wrapper arround an implicitly unwrapped optional value which fallbacks to a given default value.

@DefaultValue(default: 0)
var count
count = 100
// or
@DefaultValue(default: 0)
var count = 100

// Assigning nil resets to the default value
print(count) // 100
count = nil
print(count) // 0

@DynamicUIColor

A property wrapper arround UIColor to support dark mode.

By default in iOS >= 13 it uses the new system wide user interface style trait and dynamic UIColor constructor to support dark mode without any extra effort. On prior iOS versions it defaults to light.

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

// The color will automatically update when traits change
view.backgroundColor = backgroundColor

To support older iOS versions and custom logics (e.g. a switch in your app settings) the constructor can take an extra style closure that dynamically dictates which color to use. Returning a nil value results in the prior default behaviour. This logic allows easier backwards compatiblity by doing:

let color = DynamicUIColor(light: .white, dark: .black) {
    if #available(iOS 13.0, *) { return nil }
    else { return Settings.isDarkMode ? .dark : .light }
}

view.backgroundColor = color.value

// On iOS <13 you might need to manually observe your custom dark
// mode settings & re-bind your colors on changes:
if #available(iOS 13.0, *) {} else {
    Settings.onDarkModeChange { [weak self] in
        self?.view.backgroundColor = self?.color.value
    }
}

Original idea courtesy of @bardonadam

@EnvironmentVariable

A property wrapper to set and get system environment variables values.

@EnvironmentVariable(name: "PATH")
var path: String?

// You can set the environment variable directly:
path = "~/opt/bin:" + path!

@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(wrappedValue: "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(wrappedValue: "Hola!") is possible and since wrappers themselves need to be declared variable there is no way to prevent it.

@Trimmed

A wrapper that automatically trims strings both on initialization and reassignment.

@Trimmed
var text = " \n Hello, World! \n\n    "

print(text) // "Hello, World!"

// By default trims white spaces and new lines, but it also supports any character set
@Trimmed(characterSet: .whitespaces)
var text = " \n Hello, World! \n\n    "
print(text) // "\n Hello, World! \n\n"

@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

Quoting the Property Wrappers Proposal description:

A property wrapper is a mechanism to abstract property implementation patterns that come up repeatedly.

👉 Did you know: Property Wrappers were announced by Apple during WWDC 2019. They are a fundamental component in SwiftUI syntax sugar hence Apple pushed them into the initial Swift 5.1 beta, skipping the normal Swift Evolution process. This process continued after WWDC and it took 3 reviews to reach their final form on Xcode 11 beta 4.

Interesting reads:

Equivalents in other languages:

License

Burritos is released under the MIT license.

Comments
  • The deploy target version is too high in the podspec

    The deploy target version is too high in the podspec

    The deployment target seems too high:

      s.ios.deployment_target = '13.0'
      s.osx.deployment_target = '10.15'
      s.tvos.deployment_target = '13.0'
      s.watchos.deployment_target = '6.0'
    

    The wrapper is a compiler feature, not the runtime. Is it right?

    opened by Whirlwind 4
  •  Update DynamicUIColor to use UIColor(dynamicProvider:)

    Update DynamicUIColor to use UIColor(dynamicProvider:)

    This creates a color that will auto-update on iOS 13, and falls back to the light color on any version less.

    I'm very unsure about my approach with the tests - what's best practice for testing different iOS versions behavior? Open to suggestions.

    opened by kylebshr 3
  • AtomicWrite Collections

    AtomicWrite Collections

    First of all, amazing collection of Property Wrappers you have here!! Truly a great resource! So thank you!! 🎉

    One thing I noticed, is the AtomicWrite property wrapper doesn't handle collections the best. I could be thinking about this wrong, but when accessing the underlying properties or functions, it doesn't do it in an Atomic way as far as I can tell. Since collections are just pointers to other items in memory, accessing those other items is not handled in an atomic way.

    What are your thoughts on this? Do you think there is potential for maybe an extension on top of AtomicWrite that would help access the items in a collection in an Atomic way?

    opened by fishcharlie 1
  • Improve AtomicWrite performance with pthread_mutex and os_unfair_lock

    Improve AtomicWrite performance with pthread_mutex and os_unfair_lock

    Use os_unfair_lock if it is available, otherwise fall back to pthread_mutex.

    This should give a nice performance boost according to the tests in this article: https://www.cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html

    opened by yvbeek 1
  • Rename Expirable as Cached

    Rename Expirable as Cached

    Wouldn't it be intuitive to have @Expirable as @Cached instead? Coz token usually isn't expired(if we're talking from the app's point of view) i.e. the server manages token's life time and client simply caches it for limited time.

    opened by own2pwn 1
  • Could it be possible using a property wrapper with Decodable and optional value?

    Could it be possible using a property wrapper with Decodable and optional value?

    Hi @guillermomuntaner , thanks your great repo, It's very helpful for swift developing.

    It' seems that you have learn a lot about using property wrapper to provide convenient feature. Could you do me a favor, for helping me resolve the problem, just like what title says.

    The situation is that, when using Decodable We need to know every property that is optional or not to define the model That's too hard to ensure you get what you want Sometime server just update, you fail to parse the data

    But like other framework as HandyJSON, they provide default value for model We can safely define every property as non-optional with that

    Is there a way to achieve this with Property Wrapper for Decodable? Thanks in advance

    feature request not yet possible 
    opened by CodeEagle 1
  • Getting compile error when using @LazyConstant

    Getting compile error when using @LazyConstant

    I used Swift 5.1/Xcode 11.1 to reproduce this issue. Consider the following code:

    protocol P {
        func f()
    }
    
    struct S: P {
        func f() { }
    }
    
    func f(p: P) {
        p.f()
    }
    
    class C {
        @LazyConstant
        var s: S = {
            return S()
        }()
    
        func g() {
            f(p: s)
        }
    }
    

    Getting the following error: Expression type '()' is ambiguous without more context. To fix it replace f(p: s) with f(p: s as P). But as for me this is a bug. I also created an issue at https://bugs.swift.org.

    opened by RomanPodymov 0
  • MappingSetter

    MappingSetter

    Hello. Thank you for Burritos. In this pull request I added MappingSetter. I was inspired by Trimmed, however MappingSetter is more flexible, you can set any mapping function.

    opened by RomanPodymov 0
  • @Lazy does not work with self

    @Lazy does not work with self

    One of the main reasons I use lazy is when I need access to self, lazy allows me to defer creation of the property to access-time (i.e. when self is available).

    Is there a way to make @Lazy support self?

    opened by davidbjames 0
  • "Float80 is not available on target platform" when building for macOS on Apple Silicon

    I get this error building for macOS on Apple Silicon using 12.5 beta 3 (12E5244e):

    'Float80' is unavailable: Float80 is not available on target platform.
    

    I suspect that has something to do with Apple Silicon so the check should be different based on architecture.

    opened by insidegui 1
  • Lazy property wrapper doesn't work with closures

    Lazy property wrapper doesn't work with closures

    Simple playground test:

    class Test {
        lazy var myLazyVar: Void = {
            print("crazy")
        }()
    }
    
    print("Initialize: ")
    var test = Test()
    print("call first")
    test.myLazyVar
    

    will print:

    Initialize: 
    call first
    crazy
    

    but then you have:

    class Test {
        @Lazy var myLazyVar: Void = {
            print("crazy")
        }()
    }
    
    print("Initialize: ")
    var test = Test()
    print("call first")
    test.myLazyVar
    

    which will print:

    Initialize: 
    call first
    
    opened by mmdock 0
  • Update Package.swift

    Update Package.swift

    Bumped minimum iOS deployment version from 8 to 9 in order to comply with Xcode 12's minimum deployment range. Fixes compiler warning when installing via SPM.

    opened by Sam-Spencer 0
  • @Lazy* property wrappers are not thread safe

    @Lazy* property wrappers are not thread safe

    The @Lazy and @LazyConstant property wrappers do not appear to be thread safe. That is, if two threads attempt to hit the initial getter at the exact same time, they will both attempt to initialize the value. Running the initializer multiple times can have the adverse effect of executing the "expensive operation" multiple times, which is not a good thing.

    Note: This only would happen if the @Lazy objects are accessed in multiple threads. For UIKit initializers, everything must be done on the main (synchronous) thread, and so we don't have this issue.

    The "standard" solution for such a case is creating a concurrent queue, and doing the work within the getter in a queue.sync{} block, and (for the non-constant @Lazy implementation), putting the setter into an async{} .barrier to ensure the reads and writes are synchronized. This would require that a DispatchQueue be created for every @Lazy property, but this has the side-effect of possibly creating many, many GCD queues, which isn't needed for the UIKit exception I mentioned above. This might require maintaining a pool of thread objects, assigned to each @Lazy object, but we're now adding even more complexity to what should be a simple property wrapper.

    So, my suggestion is to provide two additional property wrappers to have a total of four @Lazy style objects:

    1. @Lazy -- Current modifiable implementation
    2. @LazyConstant -- Current constant implementation
    3. @LazyQueue -- @Lazy + barrier queues
    4. @LazyQueueConstant -- @LazyConstant + synchronous queues

    An even better implementation would allow the passing of an OptionSet to supply [.constant, .queue] to the initializer, but that makes the @Lazy(flags: [], wrappedValue: { ... } var foo quite complex to write.

    opened by Larry-Gensch 0
Releases(0.0.3)
Owner
Guillermo Muntaner
Telecom engineer · iOS developer
Guillermo Muntaner
In SwiftUI, a property-wrapper provides velocity in pt/s from gesture

swiftui-GestureVelocity In SwiftUI, a property-wrapper provides velocity in pt/s from gesture Instructions @GestureVelocity private var velocity: CGVe

Hiroshi Kimura 9 Oct 3, 2022
A tiny category on UIView that allows you to set one property: "parallaxIntensity" to achieve a parallax effect with UIMotionEffect

NGAParallaxMotion A tiny category on UIView that allows you to set one property: parallaxIntensity to achieve a parallax effect with UIMotionEffect. S

Michael Bishop 650 Sep 26, 2022
swiftUIviews is an online collection of beautifly designed swiftUIViews by the swift community

swiftUIViews | ?? swiftUIviews is an online collection of beautifly designed swiftUIViews by the swift community. Feelin like contributing? Follow the

emin 28 Aug 18, 2022
TicTacToe Game Collection View With Swift

TicTacToe---Collection-View Game Rules A game will consist of a sequence of the following actions: Initially, the "X" marks will play first (we call h

Hardik 3 Apr 27, 2022
A Code challenge I solved leveraging a lot on Composite collection view layout written in swift

AsthmApp Mobile app designed as a support aid for people with Asthma Accounts Google and Firebase [email protected] dICerytiMPSI Facebook asthmp.ap

null 0 Dec 13, 2021
A Code challenge I solved leveraging a lot on Composite collection view layout...written in swift

Space44 Code Challenge Space44 Code Challenge iOS application for Space 44 hiring process, it leverages on Image download and composite collection vie

null 0 Dec 16, 2021
A Swift utility to make updating table views/collection views trivially easy and reliable.

ArrayDiff An efficient Swift utility to compute the difference between two arrays. Get the removedIndexes and insertedIndexes and pass them directly a

Adlai Holler 100 Jun 5, 2022
Flow layout / tag cloud / collection view in SwiftUI.

SwiftUIFlowLayout A Flow Layout is a container that orders its views sequentially, breaking into a new "line" according to the available width of the

Gordan Glavaš 115 Dec 28, 2022
A collection of operators and utilities that simplify iOS layout code.

Anchorage A lightweight collection of intuitive operators and utilities that simplify Auto Layout code. Anchorage is built directly on top of the NSLa

Rightpoint 620 Jan 3, 2023
A flexible collection view with proper horizontal layout flow

FlexCollection A very simple flexible collection view using SwiftUI that automat

null 1 Dec 29, 2021
Horizontal and Vertical collection view for infinite scrolling that was designed to be used in SwiftUI

InfiniteScroller Example struct ContentView: View { @State var selected: Int = 1 var body: some View { InfiniteScroller(direction: .ve

Serhii Reznichenko 5 Apr 17, 2022
Reframing SwiftUI Views. A collection of tools to help with layout.

Overview A Swift Package with a collection of SwiftUI framing views and tools to help with layout. Size readers like WidthReader, HeightReader, and on

Ryan Lintott 84 Dec 16, 2022
A collection of session summaries in markdown format, from WWDC 20, 19 & 17

WWDC-Recap Summaries for the sessions of WWDC 20, 19 & 17 in a markdown format. These notes are not intended to replace the full experience. They are

Eren Kabakçı 125 Apr 12, 2021
MisterFusion is Swift DSL for AutoLayout. It is the extremely clear, but concise syntax, in addition, can be used in both Swift and Objective-C. Support Safe Area and Size Class.

MisterFusion MisterFusion makes more easier to use AutoLayout in Swift & Objective-C code. Features Simple And Concise Syntax Use in Swift and Objecti

Taiki Suzuki 316 Nov 17, 2022
VidyoPlatform Basic CustomLayouts Reference App for iOS (Swift)VidyoPlatform Basic CustomLayouts Reference App for iOS (Swift)

VidyoPlatform Basic CustomLayouts Reference App for iOS (Swift) VidyoPlatform reference application highlighting how to integrate video chat into a na

Taras Melko 0 Nov 19, 2021
100-days-swift-project-8 - The eighth project from 100 days of Swift course

7 Swifty Words This is the eighth project from Hacking With Swift 100 days of Sw

Bruno Guirra 0 Jan 24, 2022
SwiftLanguageWeather-master - Swift Language Weather is an iOS weather app developed in Swift 4

Swift Language Weather SwiftWeather has renamed to Swift Language Weather. Becau

Kushal Shingote 1 Feb 5, 2022
Fancy Swift implementation of the Visual Format Language (experimental and doesn't work with the recent version of Swift)

VFLToolbox Autolayout is awesome! VFL a.k.a Visual Format Language is even more awesome because it allows you to shorten constraints setting code. The

0xc010d 144 Jun 29, 2022
BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift!

BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift! Deprecated BrickKit is being phased out at Wayfair, and the

Wayfair Tech – Archive 608 Sep 15, 2022