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

Overview

Injectable Demo

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

Injectable also supports overriding services for mocking and testing purposes, as well as a rudimentary thread-safe scoping system that enables unique, shared, cached, and application-level scopes for services.

Demo Code

Here's a SwiftUI view that uses an injectable view model.

struct ContentView: View {
    
    @InjectableObject(\.contentViewModel) var viewModel: ContentViewModel
    
    var body: some View {
        VStack(spacing: 16) {
            Text("\(viewModel.id)")
                .font(.footnote)
            
            NavigationLink("Next", destination: ContentView())
        }
        .onAppear(perform: {
            viewModel.test()
        })
    }
}

And here's the code for the view model which in turn has its own injectable service.

class ContentViewModel {
    
    @Injectable(\.myServiceType) var service: MyServiceType
    
    var id: String {
        service.service()
    }
    
    func test() {
        print(service.service())
    }
}

Note that MyServiceType is a protocol and as such can be overridden with other values for testing.

The service protocol, service, and a mock service appear as follows.

String { "Mock \(id)" } } ">
protocol MyServiceType  {
    func service() -> String
}

class MyService: MyServiceType {
    private let id = UUID()
    func service() -> String {
        "Service \(id)"
    }
}

class MockService: MyServiceType {
    private let id = UUID()
    func service() -> String {
        "Mock \(id)"
    }
}

Resolving the ViewModel and Services

Here's are the registrations that resolve the various keypaths.

extension Injections {
    var contentViewModel: ContentViewModel { shared( ContentViewModel() ) }
    var myServiceType: MyServiceType { shared( MyService() ) }
}

For each one we extend Injections to add a factory closure that will be called to provide a new instance of the viewmodel or service when needed.

Note that we're using shared scopes here in order to ensure persistance across view updates in SwiftUI.

Mocking and Testing

The key to overriding a given service for mocking and testing lies in adding a Resolver-style inferred-type registration factory that will override the keypath registration.

extension Injections {
    static func registerMockServices() {
        container.register { MockService() as MyServiceType }
        // others as needed
    }
}

Here's an example of the mocks being used in the ContentView preview.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Injections.registerMockServices()
        return ContentView()
    }
}

Injectable

And finally, here's part of the @Injectable property wrapper that demonstrates the basic technique used. The initialization function checks to see if an override exists (optional). If not it resorts to using the required keypath.

@propertyWrapper public struct Injectable<Service> {
    
    private var service: Service
    
    public init(_ keyPath: KeyPath) {
        self.service = Injections.container.resolve() ?? Injections.container[keyPath: keyPath]
    }
    
    ...
    
}

As the initializer requires the keypath, it must exist. Thus all registrations are required to exist, which ensures compile-time safety.

Overrides to the keypaths are exceptions to the rule, and are treated as such.

All of the code, including the code for the scopes, requires about 160 lines of code. That also includes an addtional property wrapper, @InjectableObject, which can be used in SwiftUI code like an ObservableObject.

The Idea

The impetus for this code and demo resolves around an article written by Antoine van der Lee, titled Dependency Injection in Swift using latest Swift features.

That article, in turn, triggered my own Medium article, I Hate Swift. I Love Swift, where I detailed some of my own attempts to solve some of the issues perceived in Antoine's original approach.

And this is the final result.

You might also like...
Example app source code developed by swift language from apple
Example app source code developed by swift language from apple

AboutMe Example app source code developed by swift language from apple. Display data from a central source in multiple views. Welcome to the About Me

iOS NBA Challenge Based on Xcode 12.4, PR2S Project By: Oscar Pastás

iOS NBA Challenge Based on Xcode 12.4, PR2S Project By: Oscar Pastás iOS This test app consists of a list of users Considerations This test should be

KinoLenta - App for searching movies and creating watchlists based on open movie databases

KinoLenta App for searching movies and creating watchlists based on open movie d

The source code of the EU Digital COVID Certificate App Core for iOS

This repository contains the source code of the EU Digital COVID Certificate App Core for iOS.

Sample Code for WWDC21

WWDC21 Sample Code Accessibility Create Accessible Experiences for watchOS Creating Accessible Views WWDC21 Challenge: Large Text Challenge WWDC21 Cha

Starter project for the iOS code challenge

iOS Base Project for Podium Take-Home Challenge Introduction We have provided two version of this base project: one using UIKit, one using SwiftUI. Th

Code challenge iOS - Movies app

Code challenge iOS - Movies app Project structure The Common folder contains sha

Third Prize for Uber Hackathon China 2016. Source code for iOS client of UberGuide Project.
Third Prize for Uber Hackathon China 2016. Source code for iOS client of UberGuide Project.

Smart Traveller Intro It's the project we did for Uber Hackathon China 2016. This is the repo for iOS client. Using swift and Objective-C. The project

Screen transition with safe and clean code.

Presenter Screen transition with safe and clean code. With Presenter, you can… Assure that the ViewController's requirements are met, such as a ViewMo

Owner
Michael Long
I'm a technology consultant and specialist in mobile application development, web development, software engineering, and design.
Michael Long
OrderManagementSystem - A real-time system that simulates the fulfillment of delivery orders for a kitchen

This project showcases the implementation of an order management system used for

Amer Hukić 27 Jul 24, 2022
Code Challenge - Using Alamofire is a Swift-based, HTTP networking library, also Codable for Data Model and Combine Framework .

Code Challenge ##Using Alamofire is a Swift-based, HTTP networking library, also Codable for Data Model and Combine Framework . Alamofire is one of th

Eng Angelo Saber 0 Nov 24, 2021
ZEGOCLOUD's easy example is a simple wrapper around our RTC product.

ZEGOCLOUD's easy example is a simple wrapper around our RTC product. You can refer to the sample code for quick integration.

null 1 Dec 14, 2022
The demo app demonstrates a real-time application using FindSurface to search point clouds, which ARKit provides, for geometry shapes.

FindSurface-GUIDemo-iOS (Swift) CurvSurf FindSurface™ GUIDemo for iOS (Swift) Overview This demo app demonstrates a real-time application using FindSu

CurvSurf 0 Nov 28, 2022
AnimeSearch - A simple app that shows how to use Anilist GraphQL based API with Apollo

AnimeSearch A simple app that shows how to use Anilist GraphQL based API with Ap

Pedro J Fernandez 1 Apr 26, 2022
GoodAsOldPhones is the demo app of Swift tutorial on code school.

GoodAsOldPhones GoodAsOldPhones is the demo app of Swift tutorial on code school. This app demonstates basic use and implementation of tab bar control

Kushal Shingote 5 May 24, 2022
Code Swift iOS app showcasing basic movies list from Orange TV API.

iOS Code Test - Optiva Media Code Swift iOS app showcasing basic movies list from Orange TV API. Built using XCode 13.0 (Swift 5) How to run the examp

Manu Martinez 1 Oct 17, 2021
Sample app to demonstrate the integration code and working of Dyte SDK for iOS, using Swift

docs-template by dyte ADD_DESCRIPTION_HERE Explore the docs » View Demo · Report Bug · Request Feature Table of Contents About the Project Built With

Dyte 8 Nov 26, 2021
Some projects written in Swift 5 code designed using the MVVM design pattern.

MVVM Design Pattern Demo This repository has contained some simple demo projects written in Swift code designed using the MVVM design pattern. Project

Timmy Hsieh 2 Nov 14, 2021
A Swift sample code to reads ISO 10303-21 exchange structures (STEP P21 files for AP242) split into multiple files using external references approach.

multipleP21ReadsSample A Swift sample code to reads ISO 10303-21 exchange structures (STEP P21 files for AP242) split into multiple files using extern

Tsutomu Yoshida 1 Nov 23, 2021