Git Product home page Git Product logo

praykit's Introduction

PrayKit

Platform Swift Xcode SPM MIT

PrayKit is a Swift package that powers the Pray Watch app and used for rapid development of Apple platform prayer apps and services. It is a collection of micro utilities and extensions around the Adhan prayer library.

Note: This library is highly volatile and changes often to stay ahead of cutting-edge technologies. It is recommended to copy over code that you want into your own libraries or fork it.

Installation

Swift Package Manager

.package(url: "[email protected]:ZamzamInc/PrayKit.git", .upToNextMajor(from: "1.0.0"))

Usage

The PrayKit package is divided into four different targets:

  • PrayCore: Utilities, extensions, service protocols
  • PrayServices: Concrete services that conform to the core protocols
  • PrayMocks: Resources for creating test instances
  • PrayKit: Dependency injection container

Dependency Injection

In PrayKit, there is the PrayKitDependency protocol that represents the dependency container that wraps all services:

public protocol PrayKitDependency {
    // Settings
    func constants() -> Constants
    func preferences() -> Preferences
    func localStorage() -> UserDefaults

    // Network
    func networkManager() -> NetworkManager
    func networkService() -> NetworkService
    func networkAdapter() -> URLRequestAdapter?

    // Services
    func prayerManager() -> PrayerManager
    func prayerService() -> PrayerService
    func prayerServiceLondon() -> PrayerService

    func qiblaService() -> QiblaService
    func hijriService() -> HijriService
    func notificationService() -> NotificationService

    func locationManager() -> LocationManager
    func locationService() -> LocationService

    // Diagnostics
    func log() -> LogManager
    func logServices() -> [LogService]
}

A Swift property wrapper can be created to conform to the PrayKitDependency protocol and supply the concrete instances:

@propertyWrapper
struct PrayDependency: PrayKitDependency {
    private static let shared = PrayDependency()

    var wrappedValue: PrayKitDependency? { Self.shared }
}

The property wrapper can be extended from here to satisfy the dependency container requirements:

extension PrayDependency {
    // Thread-safe single instance
    private static let preferences = Preferences(
        defaults: UserDefaults(suiteName: "{{your suite name}}") ?? .standard
    )

    public func preferences() -> Preferences {
        Self.preferences
    }
}

extension PrayDependency {
    private static let localStorage: UserDefaults = .standard

    public func localStorage() -> UserDefaults {
        Self.localStorage
    }
}

// MARK: Network

extension PrayDependency {
    private static let networkManager = NetworkManager(
        service: networkService,
        adapter: networkAdapter
    )

    public func networkManager() -> NetworkManager {
        Self.networkManager
    }
}

extension PrayDependency {
    private static let networkService = NetworkServiceFoundation()

    public func networkService() -> NetworkService {
        Self.networkService
    }

    public func networkAdapter() -> URLRequestAdapter? { nil }
}

// MARK: Services

extension PrayDependency {
    private static let notificationService = NotificationServiceUN(
        prayerManager: prayerManager,
        userNotification: .current(),
        preferences: preferences,
        constants: constants,
        localized: NotificationServiceLocalize(),
        log: log
    )

    public func notificationService() -> NotificationService {
        Self.notificationService
    }
}

extension PrayDependency {
    private static let prayerManager = PrayerManager(
        service: prayerService,
        londonService: prayerServiceLondon,
        preferences: preferences,
        log: log
    )

    public func prayerManager() -> PrayerManager {
        Self.prayerManager
    }
}

extension PrayDependency {
    private static let prayerService = PrayerServiceAdhan(log: log)

    public func prayerService() -> PrayerService {
        Self.prayerService
    }
}

extension PrayDependency {
    private static let prayerServiceLondon = PrayerServiceLondon(
        networkManager: networkManager,
        apiKey: "{{your api key}}",
        log: log
    )

    public func prayerServiceLondon() -> PrayerService {
        Self.prayerServiceLondon
    }
}

extension PrayDependency {
    private static let qiblaService = QiblaServiceAdhan()

    public func qiblaService() -> QiblaService {
        Self.qiblaService
    }
}

extension PrayDependency {
    private static let hijriService = HijriServiceStatic(
        prayerManager: prayerManager,
        preferences: preferences
    )

    public func hijriService() -> HijriService {
        Self.hijriService
    }
}

extension PrayDependency {
    private static let locationManager = LocationManager(service: locationService)

    public func locationManager() -> LocationManager {
        Self.locationManager
    }
}

extension PrayDependency {
    private static let locationService = LocationServiceCore(
        desiredAccuracy: kCLLocationAccuracyThreeKilometers,
        distanceFilter: 1000
    )

    public func locationService() -> LocationService {
        Self.locationService
    }
}

// MARK: Diagnostics

extension PrayDependency {
    private static let log = LogManager(services: logServices)

    public func log() -> LogManager {
        Self.log
    }
}

extension PrayDependency {
    private static let logServices: [LogService] = [
        LogServiceConsole(
            minLevel: constants.isDebug || constants.isRunningOnSimulator ? .verbose : .none,
            subsystem: "{{your app}}"
        )
    ]

    public func logServices() -> [LogService] {
        Self.logServices
    }
}

Lazy thread-safety was provided for free by using the static properties, but this is not necessary if new instances every time is actually intended. More importantly though, PrayDependency now conforms to the dependency container and any component can grab it using:

@PrayDependency var dependency

let prayerManager = dependency?.prayerManager()

Now in SwiftUI, you can add the following property wrapper to access the dependency injection container:

struct ContentView: View {
    @PrayDependency var dependency

    var body: some View {
        Text("Salam, world!")
            .task {
                guard let preferences = dependency?.preferences() else { return }
                let request = PrayerAPI.Request(from: preferences)
                let prayerDay = await dependency?.prayerManager().fetch(for: .now, with: request)
            }
    }
}

Author

License

PrayKit is available under the MIT license. See the LICENSE file for more info.

praykit's People

Contributors

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