Swiftpack.co -  pawello2222/PhantomKit as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
pawello2222/PhantomKit
All Swift & SwiftUI extensions and components in one place.
.package(url: "https://github.com/pawello2222/PhantomKit.git", from: "0.1.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 14.0+
  • Xcode 12.0+
  • Swift 5.3+

Installation

Swift Package Manager

PhantomKit is available as a Swift Package.

Alternatively, you can add PhantomKit as a SPM dependency:

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

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

  • 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 exports globally the following frameworks making them accessible just by importing PhantomKit alone:

License

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

GitHub

link
Stars: 3
Last commit: 2 days ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Release Notes

0.1.0
1 week ago

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