Swiftpack.co - VakhoKontridze/VCore as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by VakhoKontridze.
VakhoKontridze/VCore 4.10.0
VCore is a Swift collection containing objects, functions, and extensions that I use for my projects
⭐️ 42
🕓 16 hours ago
iOS macOS watchOS tvOS
.package(url: "https://github.com/VakhoKontridze/VCore.git", from: "4.10.0")

VCore

Table of Contents

Description

VCore is a Swift collection containing objects, functions, and extensions that I use for my projects.

Structure

Package files are grouped as:

  • Services and Managers. Services, managers, controllers, and formatters. For instance, NetworkClient.

  • Views. Reusable non-scene Views, UIViews, and UIViewControllers. For instance BaseButtons.

  • Models. Reusable models. For instance, EdgeInsetss.

  • Helpers. Non-service, non-extension objects and methods. For instance, architectural pattern helpers.

  • Extensions. Global extensions. Methods and properties are grouped by frameworks of origin—Core Frameworks, Foundation, SwiftUI, UIKit, and AppKit (only for supporting underlying SwiftUI types).

  • Global Functions. Global functions. For instance, TODO.

  • API. Objects used for interfacing from you app/package to VCore. For instance, VCoreLocalizationManager.

Package incudes folder Extra, which contains:

  • XCode Templates. Templates that can be used for accelerating workflow. Currently, templates cover scenes and gateways. For additional info, refer to documentation folder.

Project includes folder Documentation, which contains:

  • Swift style guide

  • Documentation and an example app of UIKit VIPER architecture

  • Documentation and an example app of SwiftUI VIPER architecture

  • Documentation of CLEAN Interactors and Gateways

Showcase

Network Client

NetworkClient with customizable requests, responses, and return types:

do {
    var request: NetworkRequest = .init(url: "https://httpbin.org/post")
    request.method = .POST
    try request.addHeaders(encodable: JSONRequestHeaders())
    try request.addBody(json: ["key": "value"])

    let result: [String: Any?] = try await NetworkClient.default.json(from: request)

    print(result)

} catch {
    print(error.localizedDescription)
}

Multipart/Form-Data Builder

MultipartFormDataBuilder with a Dictionary-based file API:

do {
    let json: [String: Any?] = [
        "key": "value"
    ]

    let files: [String: (some AnyMultipartFormDataFile)?] = [
        "profile": MultipartFormDataFile(
            mimeType: "image/jpeg",
            data: profileImage?.jpegData(compressionQuality: 0.25)
        ),

        "gallery": galleryImages?.enumerated().compactMap { (index, image) in
            MultipartFormDataFile(
                filename: "IMG_\(index).jpg",
                mimeType: "image/jpeg",
                data: image?.jpegData(compressionQuality: 0.25)
            )
        }
    ]

    let (boundary, data): (String, Data) = try MultipartFormDataBuilder().build(
        json: json,
        files: files
    )

    var request: NetworkRequest = .init(url: "https://somewebsite.com/api/some_endpoint")
    request.method = .POST
    try request.addHeaders(encodable: MultipartFormDataAuthorizedRequestHeaders(
        boundary: boundary,
        token: "token"
    ))
    request.addBody(data: data)

    let result: [String: Any?] = try await NetworkClient.default.json(from: request)

    print(result)

} catch {
    print(error.localizedDescription)
}

Localization Manager

LocalizationManager that manages localizations without interacting with raw Strings:

extension Locale {
    static var english: Self { .init(identifier: "en") }
    static var english_uk: Self { .init(identifier: "en-GB") }
    static var spanish: Self { .init(identifier: "es") }
}

LocalizationManager.shared.addLocales([.english, .english_uk, .spanish])
LocalizationManager.shared.setDefaultLocale(to: .english)
LocalizationManager.shared.setCurrentLocale(to: .english)
let lhs: Locale = .init(identifier: "en")
let rhs: Locale = .init(identifier: "en-US")

lhs == rhs // false

lhs.isEquivalent(to: rhs) // true, if `Locale.current.regionCode` is "US"

Keychain Service

KeychainService that supports custom queries, and has a dedicated property wrapper:

KeychainService.default.get(key: "SomeKey")
KeychainService.default.set(key: "SomeKey", data: data)
KeychainService.default.delete(key: "SomeKey")
@KeychainStorage("AccessToken") var accessToken: String?

Various Helpful Declarations

KeyPathInitializableEnumeration that allows for initialization of an enum with a KeyPath:

enum SomeEnum: KeyPathInitializableEnumeration {
    case first
    case second

    var someProperty: Int {
        switch self {
        case .first: return 1
        case .second: return 2
        }
    }
}

let value: SomeEnum? = .aCase(key: \.someProperty, value: 2)

DigitalTimeFormatter with various configurations:

let formatter: DigitalTimeFormatter = .init()
formatter.string(from: 905048) // "10:11:24:08"

Various UIKit Views/ViewControllers

KeyboardResponsiveUIViewController that handles keyboard notifications:

final class ViewController: KeyboardResponsiveUIViewController {
    private let textField: UITextField = { ... }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addsupview(textField)

        NSLayoutConstraint.activate([
            ...
        ])
    }

    override func keyboardWillShow(_ systemKeyboardInfo: SystemKeyboardInfo) {
        super.keyboardWillShow(systemKeyboardInfo)

        UIView.animateKeyboardResponsiveness(
            systemKeyboardInfo: systemKeyboardInfo,
            animations: { [weak self] in
                guard let self else { return }

                view.bounds.origin.y = -systemKeyboardInfo.frame.size.height
                view.superview?.layoutIfNeeded()
            }
        )
    }

    override func keyboardWillHide(_ systemKeyboardInfo: SystemKeyboardInfo) {
        super.keyboardWillHide(systemKeyboardInfo)

        UIView.animateKeyboardResponsiveness(
            systemKeyboardInfo: systemKeyboardInfo,
            animations: { [weak self] in
                guard let self else { return }

                view.bounds.origin.y = 0
                view.superview?.layoutIfNeeded()
            }
        )
    }
}

Various SwiftUI Views

FetchDelegatingAsyncImage that asynchronously loads and displays an Image with a delegated fetch handler. You can customize request with access token and headers, implement custom caching, and more.

var body: some View {
    FetchDelegatingAsyncImage(
        from: URL(string: "https://somewebsite.com/content/image.jpg")!,
        fetch: fetchImage,
        content: { phase in
            if let image = phase.image {
                image
                    .resizable()
                    .fitToAspect(1, contentMode: .fill)

            } else if phase.error != nil {
                ErrorView()

            } else {
                ProgressView()
            }
        }
    )
    .frame(dimension: 200)
}

...

private var cache: NSCache<NSString, UIImage> = .init()

private func fetchImage(url: URL) async throws -> Image {
    let key: NSString = .init(string: url.absoluteString)

    switch cache.object(forKey: key) {
    case nil:
        var request: NetworkRequest = .init(url: url)
        try request.addHeaders(encodable: JSONAuthorizedRequestHeaders(token: "token"))

        let data: Data = try await NetworkClient.default.data(from: request)
        guard let uiImage: UIImage = .init(data: data) else { throw NetworkClientError.invalidData }

        cache.setObject(uiImage, forKey: key)

        return Image(uiImage: uiImage)

    case let uiImage?:
        return Image(uiImage: uiImage)
    }
}

Various Extensions and Global Functions

Optional comparison:

let a: Int? = 10
let b: Int? = nil

a.isOptionalLess(than: b, order: .nilIsLess) // false
a.isOptionalLess(than: b, order: .nilIsGreater) // true

Detecting changes in View size:

@State private var size: CGSize = .zero

var body: some View {
    VStack(content: {
        Color.accentColor
            .onSizeChange(perform: { size = $0 })
    })
}

Installation

Swift Package Manager

Add https://github.com/VakhoKontridze/VCore as a Swift Package in Xcode and follow the instructions.

Compatibility

Platform and Version Support

Package provides limited macOS, tvOS, and watchOS support.

Versions with different majors are not directly compatible. When a new major is released, deprecated symbols are removed.

Versioning

Major. Major changes, such as big overhauls

Minor. Minor changes, such as new objects, functions, and extensions

Patch. Bug fixes and improvements

History

Version Release Date Swift SDK Comment
4.0
(4.0.0 - 4.x.x)
2022 09 14 5.8
(4.7.0 - 4.x.x)
5.7
(4.0.0 - 4.6.1)
iOS 13.0
macOS 10.15
tvOS 13.0
watchOS 6.0
API changes.
3.0
(3.0.0 - 3.20.2)
2022 05 17 5.6 iOS 13.0
macOS 10.15
tvOS 13.0
watchOS 6.0
Multiplatform support.
SPM support.
2.0
(2.0.0 - 2.3.0)
2021 12 28 5.3 iOS 13.0 iOS 13.0 support
1.0
(1.0.0 - 1.4.0)
2021 10 07 5.3 iOS 14.0 -

For additional info, refer to the CHANGELOG.

Contact

e-mail: [email protected]

GitHub

link
Stars: 42
Last commit: 17 hours ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Release Notes

4.10.0
17 hours ago

General

  • Several APIs are now exposed to mac Catalyst

Views

  • ResponderChainToolBar, alongside with ResponderChainToolBarManager is added, that handles focus navigation in the responder chain
  • View.responderChainToolBar(focus:_) method is added that manages focus navigation in the responder chain
  • Several InnerShadowUIView members now have public/open access level

Extensions

  • Array.binaryAppend(_:by:) method is added that inserts elements in appropriate place
  • Array.asyncSorted(by:) and Array.asyncSort(by:) methods are added that asynchronously sorts an Array
  • Collection.firstElement(ofType:where:) and Collection.lastElement(ofType:where:) methods are added
  • Collection.firstIndex(ofType:where:) and Collection.lastIndex(ofType:where:) methods are added
  • Collection.lastIndex(ofType:where:) and Collection.lastIndex(ofType:where:) methods are added
  • BidirectionalCollection.lastIndexAndElement(where:) method is added that complements similar method that seeks first index and element
  • CaseIterable.aCase(offseteBy:) method is added that returns a case with specified distance from the current case. CaseIterable.previousCase and CaseIterable.nextCase properties are also added.
  • Another Task.sleep(seconds:) method is added that takes TimeInterval as a parameter
  • Color.init?(hex:) method is added that creates Color from a hex UInt64 or String
  • UIColor.init?(hex:) and UIColor.init?(displayP3Hex:) methods is added that creates UIColor from a hex UInt64 or String
  • Array.subscript(safe:) now supports Collection

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics