Swiftpack.co - pawello2222/PhantomKit as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by pawello2222.
pawello2222/PhantomKit 0.3.0
All Swift & SwiftUI extensions and components in one place.
⭐️ 17
🕓 3 weeks ago
iOS
.package(url: "https://github.com/pawello2222/PhantomKit.git", from: "0.3.0")

PhantomKit logo

Build Code coverage Language Supported Platform Swift Package Manager Release version License

PhantomKit

PhantomKit is a collection of many useful Swift and SwiftUI extensions and components. It is an invisible layer that supports your application and makes writing Swift code easier.

Requirements

  • iOS 15.0+
  • Xcode 13.0+
  • Swift 5.3+

Installation

Swift Package Manager

PhantomKit is available as a Swift Package.

.package(url: "https://github.com/pawello2222/PhantomKit.git", .upToNextMajor(from: "0.3.0"))

Tip: to make PhantomKit truly invisible add the below line in the main file:

@_exported import PhantomKit

Highlights

Opening a NavigationLink on tap gesture

  • Natively
@State private var isActive = false
NavigationLink("", destination: Text("Destination"), isActive: $isActive)
Text("Go to...")
    .onTapGesture {
        isActive = true
    }
  • With PhantomKit
Text("Go to...")
    .link(triggeredBy: .tap, destination: Text("Destination"))

Executing an action before and after opening a NavigationLink

Text("Go to...")
    .presentation(method: .link, onTrigger: {}, onDismiss: {}) {
        Text("Destination")
    }

Opening a Web View

extension WebEndpoint {
    static var feedback: Self {
        .init(
            title: "Feedback",
            url: URL(string: "<feedback_url>")
        )
    }
}
Text("Share feedback")
    .webView(endpoint: .feedback)

Text("Share feedback")
    .webView(triggeredBy: .plainButton, endpoint: .feedback)
    
Text("Share feedback")
    .webView(openedAs: .fullScreen, endpoint: .feedback, edgesIgnoringSafeArea: .all) {
        print("onDismiss")
    }

SwiftUI extensions

  • aligned
public func aligned(_ alignemnt: HorizontalAlignment) -> some View

public func aligned(_ alignemnt: VerticalAlignment) -> some View

Before

HStack {
    Spacer()
    Text("Hello World")
}

After

Text("Hello World")
    .aligned(.trailing)
  • expandingBackgroundColor
public func expandingBackgroundColor(_ color: Color, edgesIgnoringSafeArea: Edge.Set = .all) -> some View

Before

ZStack {
    Color.red
        .edgesIgnoringSafeArea(edgesIgnoringSafeArea)
    Text("Hello World")
}

After

Text("Hello World")
    .expandingBackgroundColor(.red)

Xcore extensions

PhantomKit also uses the Xcore framework to import some useful extensions:

  • Date manipulation

Before

let nextMonth = Calendar.current.date(byAdding: .month, value: 1, to: Date())!
let isSameMonth = Calendar.current.compare(nextMonth, to: Date(), toGranularity: .month) == .orderedSame

After

let nextMonth = Date().adjusting(.month, by: 1)
let isSameMonth = Date().isSame(nextMonth, granularity: .month)
  • In-flight modifications with Appliable and MutableAppliable

Before

static let numberFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.locale = .init(identifier: "en_US_POSIX")
    formatter.maximumFractionDigits = 3
    return formatter
}()

After

static let numberFormatter = NumberFormatter().apply {
    $0.locale = .usPosix
    $0.maximumFractionDigits = 3
}

and many more...!

Reference

Routing

Two enums for native SwiftUI routing:

extension PresentationMethod {
    public enum Transition {
        case link
        case sheet
        case fullScreen
    }
}
extension PresentationMethod {
    public enum Trigger {
        case tap
        case button(style: AnyButtonStyle)
        case primitiveButton(style: AnyPrimitiveButtonStyle)
    }
}

View helpers:

extension View {
    public func presentation<Content>(
        method: PresentationMethod,
        onTrigger: (() -> Void)? = nil,
        onDismiss: (() -> Void)? = nil,
        @ViewBuilder content: @escaping () -> Content
    ) -> some View where Content: View

Examples:

Text("Go to...")
    .presentation(method: .link, onTrigger: {}, onDismiss: {}) {
        Text("Destination")
    }

Text("Go to...")
    .link(triggeredBy: .tap, destination: Text("Destination"))

Text("Go to...")
    .sheet(triggeredBy: .plainButton, destination: Text("Sheet"))

Text("Go to...")
    .fullScreen(triggeredBy: .styledButton(BorderlessButtonStyle()), destination: Text("Full Screen"))

Text("Go to...")
    .actionSheet(triggeredBy: .fillButton) {
        ActionSheet(...)
    }

Formatting

LocalizedFormatter

A custom LocalizedFormatter that automatically formats numbers, decimals and currencies for a specified locale:

extension LocalizedFormatter {
    public static var currency = makeCurrencyFormatter(locale: .current, currencyCode: "USD")
    public static var decimal = makeDecimalFormatter(locale: .current)
    public static var percent = makePercentFormatter(locale: .current)
}

Examples:

let formatter = LocalizedFormatter.makeDecimalFormatter(locale: .init(identifier: "en_US"))

expect(formatter.string(from: 48_729_432)).to(equal("48,729,432"))
expect(formatter.string(from: 48_729_432, abbreviation: .capitalized)).to(equal("48.73M"))
expect(formatter.string(from: 123.456, sign: .both)).to(equal("+123.46"))
expect(formatter.string(from: 0.123456789, precision: .default)).to(equal("0.12"))
expect(formatter.string(from: 0.123456789, precision: .constant(4))).to(equal("0.1235"))
let formatter = LocalizedFormatter.makeCurrencyFormatter(locale: .init(identifier: "en_US"), currencyCode: "USD")

expect(formatter.string(from: 432)).to(equal("$432.00"))
expect(formatter.string(from: 1432.99, abbreviation: .default)).to(equal("$1.43k"))
expect(formatter.string(from: 123.456, sign: .both)).to(equal("+$123.46"))

LocalizedDateFormatter

A custom LocalizedDateFormatter that automatically formats date and time for a specified locale:

extension LocalizedDateFormatter {
    public static var date = makeDateFormatter(localizedFormat: "yyyyMMdd")
    public static var datetime = makeDateFormatter(localizedFormat: "yyyyMMddjjmmss")
}

Examples:

let formatter = LocalizedDateFormatter.makeDateFormatter(locale: .init(identifier: "pl_PL"))

let date = Date(year: 2000, month: 3, day: 24)

expect(formatter.string(from: date)).to(equal("24.03.2000"))
let formatter = LocalizedDateFormatter.makeDateFormatter(
    locale: .init(identifier: "en_US"),
    localizedFormat: "yyyyMMddjjmmss"
)

let date = Date(year: 2000, month: 3, day: 24, hour: 16, minute: 14, second: 44)

expect(formatter.string(from: date)).to(equal("03/24/2000, 4:14:44 PM"))

Roadmap

  • ☑ SwiftUI
  • ☑ Routing
  • ☑ Localized formatters
  • ☑ Network layer
  • ☑ Third party extensions
  • ☑ SPM compatibility
  • ☐ Database extensions
    • ☑ Core Data
    • ☐ Realm
  • ☐ Complete documentation

Dependencies

PhantomKit uses the following frameworks:

License

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

GitHub

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

Release Notes

0.3.0
3 weeks ago

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