Skip to content

Commit

Permalink
Feature/Extensions (#320)
Browse files Browse the repository at this point in the history
  • Loading branch information
borut-t committed May 16, 2024
1 parent 440835e commit 1b02fbb
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 6 deletions.
34 changes: 34 additions & 0 deletions Sources/Core/Extensions/Foundation/Date+PovioKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Date+PovioKit.swift
// PovioKit
//
// Created by Borut Tomazin on 14/05/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import Foundation

public extension Date {
var isToday: Bool { calendar.isDateInToday(self) }
var isYesterday: Bool { calendar.isDateInYesterday(self) }
var isInFuture: Bool { self > Date() }

/// Returns first day of the week
var startOfWeek: Date? {
let components: Set<Calendar.Component> = [.yearForWeekOfYear, .weekOfYear, .hour, .minute, .second, .nanosecond]
return calendar.date(from: calendar.dateComponents(components, from: self))
}

/// Returns last day of the week
var endOfWeek: Date? {
guard let startOfWeek = Date().startOfWeek else { return nil }
return calendar.date(byAdding: .day, value: 6, to: startOfWeek)
}
}

// MARK: - Private Methods
private extension Date {
var calendar: Calendar {
Calendar.autoupdatingCurrent
}
}
12 changes: 12 additions & 0 deletions Sources/Core/Extensions/Foundation/String+PovioKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ public extension String {
return emailTest.evaluate(with: self)
}

/// Returns initials from string
/// `John Doe` -> `JD`
/// `Elena Wayne Gomez` -> `EWG`
var initials: String {
let formatter = PersonNameComponentsFormatter()
if let components = formatter.personNameComponents(from: self) {
formatter.style = .abbreviated
return formatter.string(from: components)
}
return self
}

/// Returns substring containing up to `maxLength` characters from the beginning of the string.
///
/// This method is just a wrapper around swift's standard library `prefix` method, but it ensures only positive values are accepted.
Expand Down
19 changes: 19 additions & 0 deletions Sources/Core/Extensions/SwiftUI/AnyTransition+PovioKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// AnyTransition+PovioKit.swift
// PovioKit
//
// Created by Borut Tomazin on 14/05/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

public extension AnyTransition {
/// `slideLeft` is a inverse transition of system `slide`
static var slideLeft: AnyTransition {
.asymmetric(
insertion: .move(edge: .trailing),
removal: .move(edge: .leading)
)
}
}
46 changes: 46 additions & 0 deletions Sources/Core/Extensions/SwiftUI/Color+PovioKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// Color+PovioKit.swift
// PovioKit
//
// Created by Borut Tomazin on 14/05/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

public extension Color {
/// Initialize Color with `red`, `green` and `blue` components.
///
/// Example: `Color(red: 0, green: 0, blue: 0)`
init(red: Int, green: Int, blue: Int) {
self.init(red: Double(red) / 255.0, green: Double(green) / 255.0, blue: Double(blue) / 255.0)
}

/// Initialize Color with given `hex` value.
///
/// Example: `Color(hex: "000000")`
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let alpha, red, green, blue: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(alpha, red, green, blue) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(alpha, red, green, blue) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(alpha, red, green, blue) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(alpha, red, green, blue) = (1, 1, 1, 0)
}

self.init(
.sRGB,
red: Double(red) / 255,
green: Double(green) / 255,
blue: Double(blue) / 255,
opacity: Double(alpha) / 255
)
}
}
26 changes: 26 additions & 0 deletions Sources/Core/Extensions/SwiftUI/View+PovioKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// View+PovioKit.swift
// PovioKit
//
// Created by Borut Tomazin on 02/03/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

import SwiftUI

public extension View {
/// Returns square frame for given `size`.
func frame(size: CGFloat? = nil, alignment: Alignment = .center) -> some View {
frame(width: size, height: size, alignment: alignment)
}

/// Hides view using opacity.
func hidden(_ hidden: Bool) -> some View {
opacity(hidden ? 0 : 1)
}

/// Disables animation on view.
func noAnimation() -> some View {
animation(nil, value: UUID())
}
}
18 changes: 18 additions & 0 deletions Sources/Core/Extensions/UIKit/CGSize+PovioKit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// CGSize+PovioKit.swift
// PovioKit
//
// Created by Borut Tomazin on 14/05/2024.
// Copyright © 2024 Povio Inc. All rights reserved.
//

#if os(iOS)
import UIKit

public extension CGSize {
init(size: CGFloat) {
self.init(width: size, height: size)
}
}

#endif
12 changes: 12 additions & 0 deletions Sources/Core/Extensions/UIKit/UIDevice+PovioKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ public extension UIDevice {
return identifier + String(UnicodeScalar(UInt8(value)))
}
}

/// Returns `UIEdgeInsets` for the possible (top/bottom) safe areas
var safeAreaInsets: UIEdgeInsets {
let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
let window = windowScene?.windows.first
return window?.safeAreaInsets ?? .init()
}

/// Returns `true` on devices with notch
var hasNotch: Bool {
safeAreaInsets.bottom > 0
}
}

#endif
17 changes: 17 additions & 0 deletions Sources/Core/Extensions/UIKit/UIImage+PovioKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ public extension UIImage {
UIGraphicsEndImageContext()
return image ?? UIImage()
}

/// Returns existing image clipped to a circle
var clipToCircle: UIImage {
let layer = CALayer()
layer.frame = .init(origin: .zero, size: size)
layer.contents = cgImage
layer.masksToBounds = true

layer.cornerRadius = size.width / 2

UIGraphicsBeginImageContext(size)
guard let context = UIGraphicsGetCurrentContext() else { return self }
layer.render(in: context)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage ?? self
}
}

#endif
2 changes: 1 addition & 1 deletion Sources/Core/Extensions/UIKit/UIView+PovioKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
#if os(iOS)
import UIKit

// MARK: - Shadow and border
public extension UIView {
// Add shadow and border
func dropShadow(path: UIBezierPath?, shadowColor: UIColor, radius: CGFloat, opacity: Float, offset: CGSize) {
layer.shadowPath = path?.cgPath
layer.shadowColor = shadowColor.cgColor
Expand Down
20 changes: 15 additions & 5 deletions Sources/Core/Extensions/UIKit/UIWindow+PovioKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@
import UIKit

public extension UIWindow {
/// Returns `UIEdgeInsets` for the possible (top/bottom) safe areas
static var safeAreaInsets: UIEdgeInsets {
UIApplication.shared.windows
.first { $0.isKeyWindow }
.map { $0.rootViewController?.view?.safeAreaInsets ?? .zero } ?? .zero
/// Returns `viewController` that currently sits on top
var topViewController: UIViewController? {
func topViewController(with rootViewController: UIViewController?) -> UIViewController? {
guard rootViewController != nil else { return nil }

if let tabBarController = rootViewController as? UITabBarController {
return topViewController(with: tabBarController.selectedViewController)
} else if let navigationController = rootViewController as? UINavigationController {
return topViewController(with: navigationController.visibleViewController)
} else if rootViewController?.presentedViewController != nil {
return topViewController(with: rootViewController?.presentedViewController)
}
return rootViewController
}
return topViewController(with: rootViewController)
}
}

Expand Down

0 comments on commit 1b02fbb

Please sign in to comment.