A property wrapper to enforce that closures are called exactly once!

Related tags

Miscellaneous Once
Overview

Once

A property wrapper that allows you to enforce that a closure is called exactly once. This is especially useful after the introduction of SE-0293 which makes it legal to place property wrappers on function and closure parameters.

Motivation

It’s very common to write code where you have a function with a completion handler which must be called with a success or failure value based on the underlying result of the work that the function does. But if you’re not careful, it can be easy to make mistakes where you don’t call the completion handler at all or call it more than once, especially if there’s complicated business logic or error handling.

func fetchSpecialUser(@Once completion: @escaping (Result
   Error>) 
   -> 
   Void) {
  fetchUserImpl { user 
   in
    
   guard 
   let user 
   = user 
   else {
      
   // oops, forgot to call completion(...) here!

         
   return
    }
                 
    
   guard user.
   isPendingEmailVerification 
   == 
   false 
   else {
      
   completion(.
   failure(.
   userNotVerified))
      
   return
    }
                 
    
   if user.
   hasSubscription {
      
   switch 
   getSubscriptionType(user) {
        
   case .
   ultimate, .
   premium
   :
          
   completion(.
   success(user))
        
   case .
   standard
   :
          
   completion(.
   failure(.
   noPaidSubscription))
      }
    }
                 
    
   // ... more business logic here

       
    
   // oops, forgot a 'return' in the if-statement above, 

       
   // so execution continues and closure is called twice 

       
   // (and with an invalid result!).

       
   completion(.
   failure(.
   generic))
}
  

Usage

Simply annotate your function parameters with @Once and you will get a runtime error if the closure is not called at all or called more than once.

func fetchSpecialUser(@Once completion: @escaping (Result
   Error>) 
   -> 
   Void) {
  fetchUserImpl { user 
   in
    
   guard 
   let user 
   = user 
   else {
      
   // runtime error: expected closure to have already been executed once!

         
   return
    }
                 
    
   guard user.
   isPendingEmailVerification 
   == 
   false 
   else {
      
   completion(.
   failure(.
   userNotVerified))
      
   return
    }
                 
    
   if user.
   hasSubscription {
      
   switch 
   getSubscriptionType(user) {
        
   case .
   ultimate, .
   premium
   :
          
   completion(.
   success(user))
        
   case .
   standard
   :
          
   completion(.
   failure(.
   noPaidSubscription))
      }
    }
                 
    
   // ... more business logic here

       
    
   // runtime error: closure has already been invoked!

       
   completion(.
   failure(.
   generic))
}
  

Note: There's also @ThrowingOnce which you can use if the closure throws.

Requirements

  • Swift 5.2 or above

Limitations

  • The property wrapper can only be used with escaping closures. If you want to use it on a non-escaping closure, you will need to annotate it with @escaping. This is not ideal, but because the closure is stored inside the property wrapper, there is no way to say to the compiler that the closure does not escape when you know for a fact it doesn't (perhaps because your code is synchronous).

Installation

Add the following to your project's Package.swift file:

.package(url: "https://github.com/theblixguy/Once", from: "0.0.1")

or add this package via the Xcode UI by going to File > Swift Packages > Add Package Dependency.

License

MIT License

Copyright (c) 2021 Suyash Srijan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
You might also like...
Easily add vertical and horizontal pull to refresh to any UIScrollView. Can also add multiple pull-to-refesh views at once.

This is a fork from the famous SVPullToRefresh pod with 2 additional functionalities: Can add multiple pull-to-refresh views into one single UIScrollV

A declarative, thread safe, and reentrant way to define code that should only execute at most once over the lifetime of an object.

SwiftRunOnce SwiftRunOnce allows a developer to mark a block of logic as "one-time" code – code that will execute at most once over the lifetime of an

Watchbuild - Get a notification once your iTunes Connect build is finished processing
Watchbuild - Get a notification once your iTunes Connect build is finished processing

fastlane WatchBuild Get a notification once your App Store Connect build is finished processing When you upload a new binary from Xcode to App Store C

A tool to check which keychain items are available to an attacker once an iOS device has been jailbroken

Keychain Dumper Usage All that should be needed to use keychain_dumper is the binary that is checked in to the Keychain-Dumper Git repository. This bi

NumberMorphView a view like label for displaying numbers which animate with transition using a technique called number tweening or number morphing.
NumberMorphView a view like label for displaying numbers which animate with transition using a technique called number tweening or number morphing.

NumberMorphView a view like label for displaying numbers which animate with transition using a technique called number tweening or num

OysterKit is a framework that provides a native Swift scanning, lexical analysis, and parsing capabilities. In addition it provides a language that can be used to rapidly define the rules used by OysterKit called STLR

OysterKit A Swift Framework for Tokenizing, Parsing, and Interpreting Languages OysterKit enables native Swift scanning, lexical analysis, and parsing

Hyperledger Sawtooth is an enterprise solution for building, deploying, and running distributed ledgers (also called blockchains).
Hyperledger Sawtooth is an enterprise solution for building, deploying, and running distributed ledgers (also called blockchains).

Hyperledger Sawtooth SDK Hyperledger Sawtooth is an enterprise solution for building, deploying, and running distributed ledgers (also called blockcha

An executable that can be called from a Run Script Build Phase that makes comments such as // TODO: or // SERIOUS: appear in Xcode's Issue Navigator giving them project-wide visibility.

XcodeIssueGenerator An executable that can be called from a Run Script Build Phase that makes comments such as // TODO: or // SERIOUS: appear in Xcode

BucketServer - Small API with SQLite database that saves notes for an iOS appliction called Bucket list

BucketList Server-Side Small API with SQLite database that saves notes for an iO

This is an app for a craft beer company called Cacique Bier that allows to see their available products and place orders. Made with Swift and SwiftUI.

Cacique Bier App This is an app for a Craft Beer company that shows their catalogue of products and in the future will allow purchases. The app is com

AutoKeyboardScrollView is an UIScrollView subclass which makes showing and dismissing keyboard for UITextFields much easier. So called keyboard avoidance.
AutoKeyboardScrollView is an UIScrollView subclass which makes showing and dismissing keyboard for UITextFields much easier. So called keyboard avoidance.

AutoKeyboardScrollView AutoKeyboardScrollView is a smart UIScrollView which can: Scroll to proper position and make sure the active textField is visib

A Realm-like dynamic property wrapper that maps to APFS extended file attributes.
A Realm-like dynamic property wrapper that maps to APFS extended file attributes.

TOFileAttributes TOFileAttributes is an abstract class that can be extended to enable reading and writing custom data to a local file's extended attri

Backports the new @Invalidating property wrapper to older platforms
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

Demonstration code for a simple Swift property-wrapper, keypath-based dependency injection system. The keypaths ensure compile-time safety for all injectable services.

Injectable Demo Preliminary musings and demonstration code for a simple Swift property-wrapper, keypath-based dependency injection system. The keypath

A SwiftUI dynamic property wrapper for fetching media from your photo library. (iOS, tvOS, macOS)

Media Also available as a part of my SwiftUI+ Collection – just add it to Xcode 13+ A package for simplifying the user of the camera and the user's ph

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

Powerful property wrapper to back codable properties.

BackedCodable Powerful property wrapper to back codable properties. Why Swift's Codable is a great language feature but easily becomes verbose and req

A property wrapper for displaying up-to-date database content in SwiftUI views

@Query Latest release: November 25, 2021 • version 0.1.0 • CHANGELOG Requirements: iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ • Swift 5.5+ /

A new property wrapper for SwiftUI ObservableObject.

SharedObject 🍱 @SharedObject is an alternative to @StateObject, @ObservedObject, @EnvironmentObject to handle ObservableObject. If you need to have m

Releases(0.0.1)
Owner
Suyash Srijan
25. iOS Engineer currently working at @theappbusiness. Swift compiler collaborator.
Suyash Srijan
A property finder application written using React Native

React Native PropertyFinder App This repository accompanies the tutorial I published on Ray Wenderlich's website, which describes the process of build

Colin Eberhardt 276 Aug 14, 2022
ProximitySensor - Property wrappers for using the Proximity Sensor from the SwiftUI app

ProximitySensor Property wrappers for using the Proximity Sensor from the SwiftU

null 2 Aug 20, 2022
A Swift wrapper around the CoreSymbolication private framework on macOS.

CoreSymbolication provides a very powerful system for looking up and extracting symbolic information from mach-o executables, dyld shared caches, and dSYMs.

Stacksift 7 Nov 21, 2022
A Swift wrapper around the JSONbin.io API

A Swift wrapper around the JSONbin.io API

Fleuronic 5 Dec 16, 2021
ExtraLottie is a SwiftUI wrapper of Lottie-iOS

?? What is ExtraLottie? ExtraLottie is a SwiftUI wrapper of Lottie-iOS. ℹ️ Info Currently ExtraLottie supports custom loop mode, LottieLoopMode, start

null 3 May 9, 2022
SwiftyXPC - a wrapper for Apple’s XPC interprocess communication library that gives it an easy-to-use, idiomatic Swift interface.

SwiftyXPC is a wrapper for Apple’s XPC interprocess communication library that gives it an easy-to-use, idiomatic Swift interface.

null 36 Jan 1, 2023
A collection of Swift Property Wrappers (formerly "Property Delegates")

?? ?? Burritos A collection of well tested Swift Property Wrappers. @AtomicWrite @Clamping @Copying @DefaultValue @DynamicUIColor @EnvironmentVariable

Guillermo Muntaner 1.3k Dec 26, 2022
SwiftLint - A tool to enforce Swift style and conventions, loosely based on Swift Style Guide.

SwiftLint - A tool to enforce Swift style and conventions, loosely based on Swift Style Guide.

Realm 16.9k Dec 30, 2022
A tool to enforce Swift style and conventions.

SwiftLint A tool to enforce Swift style and conventions, loosely based on the now archived GitHub Swift Style Guide. SwiftLint enforces the style guid

Realm 16.9k Jan 9, 2023
A custom TextField with a switchable icon which shows or hides the password and enforce good password policies

PasswordTextField A custom TextField with a switchable icon which shows or hides the password and enforces good password policies, written in Swift. ⭐

Chris Jimenez 304 Dec 29, 2022