A Collection of useful Swift property wrappers to make coding easier

Overview

Swift Property Wrappers

A Collection of useful Swift property wrappers to make coding easier.

  • 13 wrappers included.
  • Most wrappers are fully unit tested.
  • Most wrappers can be observed via Publishers exposed in their projectedValues.
  • PRs welcome.

Contents

Here's the list of all the wrappers included in the package.

Atomic

  • Synchronizes property reads and writes using the provided DispatchQueue.
  • projectedValue exposes the wrapper itself and allows for using its mutate method that change and set the value at the same time.

Sample use:

@Atomic var value: Int = 0

let read = value // synchonized
value = 5 // also synchronized
$value.mutate { value in value *= 2 } // synchronized mutation

Clamped

  • Ensures that the property's Comparable value is always in the bounds of the provided range. The range can be specified either with its minimum and maximum value, or by a ClosedRange:
    • If the value set is lesser than lower bound of the range, the actual value set is the lower bound itself.
    • If the value set is greater than upper bound of the range, the actual value set is the upper bound itself.
  • The initially assigned value is automatically clamped as well.
  • projectedValue provides a Publisher that emits the new value on set.

Sample use:

@Clamped(min: 10, max: 20) var minMax: Int = 10
@Clamped(5...10) var range: Int = 5

minMax = 25
minMax == 20 // clamped to upper bound
range = 1
range == 5 // clamped to lower bound
minMax = 19
minMax == 19 // the value was already within bounds

ColorHex

  • A string wrapper that exposes a SwiftUI Color via its projectedValue if the value represents a valid hex color code. Multiple color formats are supported based on this solution.
  • If the string value is not a valid hex color string, projectedValue is nil.

Sample use:

@ColorHex var colorHex = "fff"
  
$colorHex == Color(white: 1)

colorHex = "FF0000"
$colorHex == Color(red: 1, green: 0, blue: 0)

colorHex = "fail"
$colorHex == nil

CopyOnWrite

  • Allows for pass-by-copy by ensuring that copy method is invoked whenever a value is assigned.
  • Property type must conform to the Copyable protocol.

Sample use:

class CopyableItem: Copyable {
  let name: String
  let price: Int
  
  init(name: String, price: Int) {
    self.name = name
    self.price = price
  }
  
  func copy() -> Self {
    CopyableItem(name: name, price: price) as! Self
  }
}

// ...

@CopyOnWrite var item: CopyableItem = CopyableItem(name: "a", price: 0)
  
let newItem = CopyableItem(name: "test", price: 1)
item = newItem
item !== newItem // not the same reference
item.name == newItem.name // same copied value

Delayed

  • Allows for late initialization of properties, thus working around Swift's init safety checks. This can avoid the need for implicitly-unwrapped optionals in multi-phase initialization.
  • If the value is read before being written to for the first time, a Never-returning block is invoked.
    • This block can be set manually and defaults to fatalError.
  • projectedValue returns true if the value is already set, so that you can check without triggering a potentially fatal `get.

Sample use:

@Delayed var value: String

$value == false // not set yet
let read = value // fatal error

value = ""
let read = value // works!
$value == true // was set

Expirable

  • Property wrapper for a value that "expires" after a set period of time - trying to read the value expirationPeriod seconds after it was last set will return nil.
  • Useful for properties whose underlying data should periodically be refreshed without having to resort to scheduled notifications.

Sample use:

@Expirable(10) var value: Int? // expires 10 seconds after it is set

value == nil // not set yet
value = 10
value == 10 // works
// sleep for 5 seconds...
value == 10 // still there
// sleep for 5 more seconds
value == nil // expired and nulled

Localized

  • Allows for direct mapping of localized keys to their string values without using NSLocalizedString.
  • Simply assign the key to the property and you'll get the localized string out.
  • projectedValue provides a publisher that emits a new value on set.

Sample use:

@Localized var emailTitle = "email-title-key"

// Providing that your Localized.strings contains "email-title-key" = "Email";
Text(emailTitle) // shows Email

Logged

  • Allows for custom blocks of code to be invoked whenever the property is read or written to. The intended use case for this is to log access to the property, but the generic nature of the callbacks makes this wrapper quite versatile.
  • By default, read block is nil and write block prints the newly set value.
  • projectedValue provides a publisher that emits a new value on set.

Sample use:

@Logged var myValue: Int = 10

myValue = 10 // prints 10 in the log

// custom actions on read and write
@Logged(read: { readLog += "Read: \($0)\n" },
        write: { writeLog += "Write: \($0)\n" }) var value: Int = 0

Mocked

  • Always returns the value specified by the mock block.
  • The most common use case for this is to easily inject temporary mock functionality in a single place without having to modify code anywhere else.
  • While assignments don't have effect on the returned value, they are still accessible via `projectedValue.

Sample use:

protocol ItemRepo {
  func fetch() -> [Item]
  func upsert(item: Item)
}

class RealRepo: ItemRepo {
  private var items = Set<Item>()
  
  func fetch() -> [Item] {
    Array(items)
  }
  
  func upsert(item: Item) {
    items.insert(item)
  }
}

class MockRepo: ItemRepo {
  static let shared = MockRepo()
  
  func fetch() -> [Item] {
    [Item(name: "test", price: 1)]
  }
  
  func upsert(item: Item) { }
}


@Mocked({ MockRepo.shared }) var repo: ItemRepo = RealRepo()

// always returns the mocked value
let fetched = repo.fetch()
fetched == [Item(name: "test", price: 1)]
repo.upsert(item: Item(name: "new", price: 2))
let fetchedAgain = repo.fetch()
fetchedAgain == fetched // no change as upsert in mock doesn't do anything
  
// projected value accesses real value
let fetched = $repo.fetch() // uses actual repo assigned
fetched == []
$repo.upsert(item: Item(name: "new", price: 2))
let fetchedAgain = $repo.fetch()
fetchedAgain == [Item(name: "new", price: 2)]

Rounded

  • Ensures that the floating-point value of this property is always rounded to the specified number of decimal places.
    • You can also specify the FloatingPointRoundingRule.
  • projectedValue provides a publisher that emits a new value whenever it is set.

Sample use:

@Rounded(0) var zero: Float = 1.1
@Rounded(1) var one: Float = 1.15
@Rounded(2) var two: Float = 1.125
@Rounded(2, rule: .down) var twoDown: Float = 1.135

zero == 1
zero = 2.23
zero == 2
one == 1.2
two == 1.12
twoDown == 1.13

Tranformed

  • Transforms the assigned value using the provided block, allowing for a wide array of applications, from automatically formatting strings, transforming numbers, etc.
  • projectedValue provides a publisher that emits a new value whenever it is set.

Sample use:

@Transformed({ -$0 }) var negated: Int = 0
@Transformed({ $0.trimmingCharacters(in: .whitespaces).lowercased() }) var formatted = ""
  
negated = 5
negated == -5

formatted = "  AbCDe  "
formatted == "abcde"

UnitInterval

  • Normalizes the assigned value to a value between 0 and 1 based on the provided range. E.g, color components are normally expressed as values between 0 and 255, while iOS requires them to be set as values between 0 and 1.
  • projectedValue provides a publisher that emits a new value whenever it is set.

Sample use:

@UnitInterval(0...255) var red: CGFloat = 0

red == 0
red = 255
red == 1
red = 25.5
red == 0.1

Validated

  • Only sets the new value is it passes validation by the provided block, which allows for vetoing new values.
  • projectedValue provides a publisher that emits a new value whenever it is set.

Sample use:

// only non-negative values, please
@Validated({ $0 >= 0 }) var value: Int = 0
  
value == 0
value = -1
value == 0 // -1 isn't a valid value so the old one is used
value = 1
value == 1 // 1 is a valid value so it overwrites the old one

Installation

This component is distrubuted as a Swift package. Just add this URL to your package list:

https://github.com/globulus/swift-property-wrappers

Changelog

  • 1.0.0 - Initial release.
You might also like...
Useful extensions for my Swift code
Useful extensions for my Swift code

UIViewController extensions presentAlert(withTitle title: String, message : String) presentAlertDialog(withTitle title: String, message : String, acti

Personally useful Swift Extensions for iOS Development

Useful-Swift-Extensions Personally useful Swift Extensions for iOS Development; cobbled together from a variety of development projects and StackOverf

Useful functions and extensions for sorting in Swift

SwiftSortUtils Motivation This library takes a shot at making comparing and sorting in Swift more pleasant. It also allows you to reuse your old NSSor

 Extendy - A set of useful string extensions.
Extendy - A set of useful string extensions.

Extendy A set of useful string extensions. Requirements iOS 11.0+ Swift 5+ Installation CocoaPods Extendy is available through CocoaPods. To install i

Easier sharing of structured data between iOS applications and share extensions
Easier sharing of structured data between iOS applications and share extensions

XExtensionItem XExtensionItem is a tiny library allowing for easier sharing of structured data between iOS applications and share extensions. It is ta

Vaccine is a framework that aims to make your apps immune to recompile-disease.
Vaccine is a framework that aims to make your apps immune to recompile-disease.

Vaccine Description Vaccine is a framework that aims to make your apps immune to recompile-disease. Vaccine provides a straightforward way to make you

Make trains of constraints with a clean style!

Constren 🚂 . 🚃 . 🚋 Make trains of constraints with style! button.constren.centerY() .lead(spacing: 16) .trail(image.l

Tools and helpers to make building apps faster and safer.

The UBFoundation framework provides a set of useful tools and helpers to make building apps faster and safer.

DGPreview - Make UIKit project enable preview feature of SwiftUI
DGPreview - Make UIKit project enable preview feature of SwiftUI

DGPreview Make UIKit project enable preview feature of SwiftUI Requirements iOS

Owner
Gordan Glavaš
Gordan Glavaš
BFKit-Swift is a collection of useful classes, structs and extensions to develop Apps faster.

Features • Classes and Extensions Compatibility • Requirements • Communication • Contributing • Installing and Usage • Documentation • Changelog • Exa

Fabrizio Brancati 992 Dec 2, 2022
A collection of useful result builders for Swift and Foundation value types

Swift Builders A collection of useful result builders for Swift and Foundation value types. Motivation Arrays, dictionaries, and other collection-base

David Roman 3 Oct 14, 2022
BFKit is a collection of useful classes and categories to develop Apps faster.

Swift Version • What does it do • Language support • Requirements • Communication • Contributing • Installing and Usage • Documentation • Changelog •

Fabrizio Brancati 806 Dec 2, 2022
Enzyme is a spm package for the make easier development on iOS

Enzyme Enzyme is a spm package for the make easier development on iOS. Installation For the installation you just need to add the url of the project t

Ebubekir 2 Jan 20, 2022
A Swift property wrapper which stores the previous value

swift-with-previous A Swift property wrapper which stores the previous value. The previous value can be get by the projected value $propertyName. impo

IKEDA Sho 3 Feb 22, 2022
Backports the new @Invalidating property wrapper to older platforms

ViewInvalidating A property wrapper that backports the new @Invalidating property wrapper to older versions of iOS/tvOS/macOS. For more information on

Suyash Srijan 61 Nov 23, 2022
StoredIn is a simple property wrapper library to store any value in anywhere

StoredIn StoredIn is a simple property wrapper library to store any value in anywhere. Installation Please use the Swift Package Manager. dependencies

Henrique Sasaki Yuya 2 Jul 4, 2022
`Republished` is a property wrapper enabling nested ObservableObjects in SwiftUI.

Republished The @Republished proprty wrapper allows an ObservableObject nested within another ObservableObject to naturally notify SwiftUI of changes.

Adam Zethraeus 13 Dec 5, 2022
Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects

SwiftyPick ?? ?? Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects. Purpose The idea behind t

Manu Herrera 19 May 12, 2022