29 Commits

Author SHA1 Message Date
Daniel Arantes Loverde
a24b79f443 only remote crypto kit 2026-05-24 10:26:25 -03:00
c0eb5f95c6 Merge pull request 'fix: update keyboard notification handling for Swift concurrency' (#7) from bugfix/snackbar_for_iOS26 into main
Reviewed-on: #7
2026-04-07 11:22:02 -03:00
a15d95ed59 Merge branch 'main' into bugfix/snackbar_for_iOS26 2026-04-07 11:21:55 -03:00
Daniel Arantes Loverde
40581d791a fix: update keyboard notification handling for Swift concurrency
Keep snackbar keyboard animations on the main actor and read UIKit keyboard values using the expected notification payload types.

Made-with: Cursor
2026-04-07 11:19:39 -03:00
57ce0dc87a Merge pull request 'iPhone UIDevice names updated' (#6) from feature/LCECryptoKit into main
Reviewed-on: #6
2026-02-11 20:57:39 +00:00
Daniel Arantes Loverde
d06a66226e iPhone UIDevice names updated 2026-02-11 17:56:53 -03:00
336b47ace2 Merge pull request 'Optional' (#5) from feature/LCECryptoKit into main
Reviewed-on: #5
2026-02-06 15:38:09 +00:00
b4a081c4d2 Merge branch 'main' into feature/LCECryptoKit 2026-02-06 15:37:58 +00:00
Daniel Arantes Loverde
57696e3036 Optional 2026-02-06 12:25:34 -03:00
560efd2c8b Merge pull request 'Added more options' (#4) from feature/LCECryptoKit into main
Reviewed-on: https://loverde_git.localhost:3334/Loverde-Company-LTDA/LCEssentials/pulls/4
2025-10-07 23:35:46 +00:00
7e24bdf3d5 Merge branch 'main' into feature/LCECryptoKit 2025-10-07 23:35:37 +00:00
Daniel Arantes Loverde
5df2499c4a Added more options 2025-10-07 20:34:33 -03:00
53ac2c7001 Merge pull request 'Fix LCECryptoKit methods' (#3) from feature/LCECryptoKit into main
Reviewed-on: https://loverde_git.localhost:3334/Loverde-Company-LTDA/LCEssentials/pulls/3
2025-10-07 23:22:04 +00:00
d909709254 Merge branch 'main' into feature/LCECryptoKit 2025-10-07 23:21:59 +00:00
Daniel Arantes Loverde
9a7981b845 Fix LCECryptoKit methods 2025-10-07 20:21:28 -03:00
8f3cbc5024 Merge pull request 'New LCECryptoKit version' (#2) from feature/LCECryptoKit into main
Reviewed-on: https://loverde_git.localhost:3334/Loverde-Company-LTDA/LCEssentials/pulls/2
2025-10-07 18:35:29 +00:00
Daniel Arantes Loverde
fd9eff5226 Merge branch 'main' into feature/LCECryptoKit 2025-10-07 15:34:11 -03:00
Daniel Arantes Loverde
7b86616f92 LCECryptoKit new version 2025-10-07 15:06:10 -03:00
Daniel Arantes Loverde
4f84dfb108 Update README.md 2025-09-02 17:28:28 -03:00
729812d20b Merge pull request 'v1.0.1' (#1) from main into feature/LCECryptoKit
Reviewed-on: https://loverde_git.localhost:3334/Loverde-Company-LTDA/LCEssentials/pulls/1
2025-08-25 19:54:37 +00:00
08519d1aca Merge branch 'feature/LCECryptoKit' into main 2025-08-25 19:54:11 +00:00
Daniel Arantes Loverde
7ac2ccb21f v1.0.1
new repo access updated
2025-08-25 16:52:42 -03:00
Daniel Arantes Loverde
d2ca6e54d2 Improvements 2025-08-15 10:57:14 -03:00
Loverde Co - Git
0eb4f355df Merge branch 'feature/LCECryptoKit' of git/LCEssentials into main
approved
2025-07-29 13:47:32 -03:00
Daniel Arantes Loverde
3e3e181b36 Update Package.swift 2025-07-29 13:46:38 -03:00
Loverde Co - Git
175816dff8 Merge branch 'feature/LCECryptoKit' of git/LCEssentials into main
Approved
2025-07-29 12:00:36 -03:00
Daniel Arantes Loverde
daae48817a LCECripto new methods 2025-07-29 11:59:11 -03:00
Daniel Arantes Loverde
241d69ecc1 LCECryptoKit 2025-07-05 12:34:25 -03:00
Loverde Co - Git
f4fade0442 Merge branch 'feature/documentation' of git/LCEssentials into main 2025-06-23 09:49:56 -03:00
40 changed files with 429 additions and 138 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.DS_Store
/.build
/build
/Packages
xcuserdata/
DerivedData/

View File

@@ -22,3 +22,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Autor: Daniel Arantes Loverde

15
Package.resolved Normal file
View File

@@ -0,0 +1,15 @@
{
"originHash" : "33e7d52ad13cf774717778548edb365d33ff62d766d0049165bc8970f19a23ef",
"pins" : [
{
"identity" : "lcecryptokitbinary",
"kind" : "remoteSourceControl",
"location" : "https://60c260c85d3a2fe840411b0ff98f521b5eca3c56@git.loverde.com.br/Loverde-Company-LTDA/LCECryptoKitBinary.git",
"state" : {
"revision" : "2c5c47cebef40a8adc5557d071a35be405c05e30",
"version" : "1.0.2"
}
}
],
"version" : 3
}

View File

@@ -1,16 +1,43 @@
// swift-tools-version: 6.0
import PackageDescription
import Foundation
let isLocalDevelopment = false //FileManager.default.fileExists(atPath: "../LCECryptoKit/PrivateLib/LCECryptoKitBinary")
let enableCryptoBinary = ProcessInfo.processInfo.environment["LCE_ENABLE_CRYPTO_BINARY"] != "0"
let cryptoPackageURL = isLocalDevelopment
? "../LCECryptoKit/PrivateLib/LCECryptoKitBinary"
: "https://60c260c85d3a2fe840411b0ff98f521b5eca3c56@git.loverde.com.br/Loverde-Company-LTDA/LCECryptoKitBinary.git"
let packageDependencies: [Package.Dependency] = enableCryptoBinary
? [
.package(url: cryptoPackageURL, exact: "1.0.2")
]
: []
let targetDependencies: [Target.Dependency] = enableCryptoBinary
? [
.product(name: "LCECryptoKit", package: "lcecryptokitbinary")
]
: []
let package = Package(
name: "LCEssentials",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
.watchOS(.v6)
],
products: [
.library(
name: "LCEssentials",
targets: ["LCEssentials"]),
],
dependencies: packageDependencies,
targets: [
.target(
name: "LCEssentials"),
name: "LCEssentials",
dependencies: targetDependencies),
]
)

View File

@@ -17,14 +17,14 @@ Installation
#### Swift Package Manager (SPM)
``` swift
dependencies: [
.package(url: "http://git.loverde.com.br:3000/git/LCEssentials.git", .upToNextMajor(from: "1.0.0"))
.package(url: "https://git.loverde.com.br/Loverde-Company-LTDA/LCEssentials", .upToNextMajor(from: "1.0.0"))
]
```
You can also add it via XCode SPM editor with URL:
``` swift
http://git.loverde.com.br:3000/git/LCEssentials.git
https://git.loverde.com.br/Loverde-Company-LTDA/LCEssentials
```
## Usage example
@@ -59,12 +59,11 @@ And then import `LCEssentials ` wherever you import UIKit or SwiftUI
import LCEssentials
```
Author:
----
Any question or doubts, please send thru email
Daniel Arantes Loverde - <daniel@loverde.com.br>
[![Alt text](https://loverde.com.br/_signature/loverde_github_mail.gif "My Resume")](https://github.com/loverde-co/resume/)
[![Alt text](https://loverde.com.br/_signature/loverde_github_mail.gif "Loverde Co. Github")](https://github.com/loverde-co)
Autor: Daniel Arantes Loverde

View File

@@ -5,8 +5,10 @@
// Created by iOSDevCenters on 11/12/15.
// Copyright © 2016 iOSDevCenters. All rights reserved.
//
#if canImport(UIKit)
import UIKit
#if os(iOS) || os(macOS)
#endif
#if canImport(UIKit)
import ImageIO
//let jeremyGif = UIImage.gifWithName("jeremy")

View File

@@ -0,0 +1,96 @@
//
// Copyright (c) 2025 Loverde Co.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
#if canImport(LCECryptoKit)
import LCECryptoKit
public final class LCECryptoKitManager {
private let hashKey: String
public init() {
self.hashKey = ""
}
public init(privateKey: String){
self.hashKey = privateKey
}
public static func generateKey() -> String {
LCECryptoKit.generateRandomAESKeyString()
}
public func encodeTP(email: String, password: String) -> String? {
return LCECryptoKit.encodeSeed(email: email, password: password)
}
public func decodeOTP(_ otpHash: String) -> String? {
return LCECryptoKit.decodeSeed(otpKey: otpHash)
}
// MARK: Need hashKey to decode
public func encodeOTPWithKey(email: String, password: String) -> String? {
return LCECryptoKit.encodeSeed(email: email, password: password, hashKey: self.hashKey)
}
public func decodeOTPWithKey(_ otpHash: String) -> Bool {
LCECryptoKit.decodeSeed(otpKey: otpHash, hashKey: self.hashKey)
}
}
#else
public final class LCECryptoKitManager {
private let hashKey: String
public init() {
self.hashKey = ""
}
public init(privateKey: String){
self.hashKey = privateKey
}
public static func generateKey() -> String {
""
}
public func encodeTP(email: String, password: String) -> String? {
nil
}
public func decodeOTP(_ otpHash: String) -> String? {
nil
}
public func encodeOTPWithKey(email: String, password: String) -> String? {
nil
}
public func decodeOTPWithKey(_ otpHash: String) -> Bool {
false
}
}
#endif

View File

@@ -26,8 +26,10 @@ import Foundation
import Security
#endif
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
#endif
/// A generic `Result` enumeration to represent either a success `Value` or a failure `Error`.
public enum Result<Value, Error: Swift.Error> {
@@ -129,15 +131,26 @@ public struct API {
// Add the file
let fileName = fileURL.lastPathComponent
let mimeType = mimeTypeForPath(path: fileName)
printInfo(title: "Body size before", msg: "\(body.count) bytes")
let fileData: Data
do {
fileData = try Data(contentsOf: fileURL)
printInfo(title: "Body size after", msg: "\(body.count) bytes")
} catch {
printError(title: "Upload File", msg: error.localizedDescription)
throw error
}
do {
let fileData = try Data(contentsOf: fileURL)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
body.append("Content-Type: \(mimeType)\r\n\r\n".data(using: .utf8)!)
body.append(fileData)
body.append("\r\n".data(using: .utf8)!)
} catch {
printError(title: "Upload File", msg: error.localizedDescription)
let fileDataCopy = Data(fileData)
body.append(fileDataCopy)
let dataUTF8 = "\r\n".data(using: .utf8)!
body.append(dataUTF8)
printInfo(title: "Body size after", msg: "\(body.count) bytes")
}
// Finalize the request body

View File

@@ -21,8 +21,12 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if canImport(AVFoundation)
import AVFoundation
#endif
#if os(watchOS)
import WatchKit
#endif
@@ -155,7 +159,7 @@ public struct LCEssentials {
diskPath: "file_cache"
)
#if os(iOS) || os(macOS)
#if canImport(UIKit)
/// Extract the file name from the file path
///
/// - Parameter filePath: Full file path in bundle
@@ -266,7 +270,11 @@ public struct LCEssentials {
/// - LoverdeCo: Check if app is running in debug mode.
@MainActor
public static var isInDebuggingMode: Bool {
#if canImport(UIKit)
return UIApplication.inferredEnvironment == .debug
#else
return false
#endif
}
#if !os(macOS)
@@ -416,7 +424,7 @@ public struct LCEssentials {
// MARK: - Methods
public extension LCEssentials {
#if os(iOS) || os(macOS)
#if canImport(UIKit)
/// - LoverdeCo: Share link with message
///
/// - Parameters:

View File

@@ -20,7 +20,9 @@
// THE SOFTWARE.
import Foundation
#if canImport(UIKit)
import UIKit
#endif
@objc public protocol LCESingletonDelegate: AnyObject {
@objc optional func singleton(object: Any?, withData: Any)

View File

@@ -21,7 +21,9 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
// MARK: - Methods (Equatable)
public extension Array where Element: Equatable {
@@ -148,6 +150,7 @@ public extension Array where Element: Equatable {
}
}
#if canImport(UIKit)
extension Array where Element == NSLayoutConstraint {
@MainActor
@@ -169,3 +172,5 @@ extension Array where Element == NSLayoutConstraint {
}
}
}
#endif

View File

@@ -21,8 +21,12 @@
import Foundation
#if canImport(CryptoKit)
import CryptoKit
#endif
#if canImport(CommonCrypto)
import CommonCrypto
#endif
public extension Data {
@@ -71,17 +75,25 @@ public extension Data {
@available(iOS 13.0, *)
func HMACSHA512(key: Data) -> Data {
#if canImport(CryptoKit)
var hmac = HMAC<SHA512>.init(key: SymmetricKey(data: key))
hmac.update(data: self)
return Data(hmac.finalize())
#else
return Data()
#endif
}
func SHA512() -> Data {
#if canImport(CommonCrypto)
var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
self.withUnsafeBytes {
_ = CC_SHA512($0.baseAddress, CC_LONG(self.count), &digest)
}
return Data(digest)
#else
return Data()
#endif
}
func XOR(with other: Data) -> Data {
@@ -89,11 +101,15 @@ public extension Data {
}
func SHA256() -> Data {
#if canImport(CommonCrypto)
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(self.count), &digest)
}
return Data(digest)
#else
return Data()
#endif
}
func object<T: Codable>() -> T? {
@@ -124,9 +140,13 @@ public extension Data {
///print("md5Hex: \(md5Hex)")
@available(iOS 13.0, *)
static func MD5(string: String) -> Data {
#if canImport(CryptoKit)
let messageData = string.data(using: .utf8)!
let digestData = Insecure.MD5.hash (data: messageData)
let digestHex = String(digestData.map { String(format: "%02hhx", $0) }.joined().prefix(32))
return Data(digestHex.utf8)
#else
return Data()
#endif
}
}

View File

@@ -21,7 +21,9 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
public extension Dictionary {

View File

@@ -21,7 +21,9 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
public extension FileManager {
@@ -54,7 +56,7 @@ public extension FileManager {
}
}
#if os(iOS) || os(macOS)
#if canImport(UIKit)
func saveImageToDirectory( _ imageWithPath : String, imagem : UIImage ) -> Bool {
let data = imagem.pngData()

View File

@@ -21,8 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if canImport(UIKit)
extension NSLayoutConstraint {
func constraintWithMultiplier(_ multiplier: CGFloat) -> NSLayoutConstraint {
@@ -72,3 +75,4 @@ extension NSLayoutConstraint {
return false
}
}
#endif

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public extension NSMutableAttributedString {
@discardableResult func customize(_ text: String,

View File

@@ -20,7 +20,9 @@
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#endif
public extension Optional {
/// Get self of default value (if self is nil).

View File

@@ -21,7 +21,9 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if canImport(CommonCrypto)
import CommonCrypto
#endif
@@ -48,10 +50,14 @@ public extension String {
}
var convertToHTML: NSAttributedString? {
#if canImport(UIKit)
return convertHtmlToAttributedStringWithCSS(font: nil,
csscolor: "",
lineheight: 0,
csstextalign: "")
#else
return convertHtmlToNSAttributedString
#endif
}
/// Check if string is a valid URL.
@@ -646,7 +652,7 @@ public extension String {
return strDate?.date(withCurrFormatt: newFormatt, localeIdentifier: localeIdentifier, timeZone: timeZone)
}
#if os(iOS) || os(macOS)
#if canImport(UIKit)
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
@@ -757,7 +763,7 @@ public extension String {
}
return self
}
#if canImport(UIKit)
/// Converte String para HTML com CSS.
///
/// - Parameters:
@@ -801,6 +807,7 @@ public extension String {
return nil
}
}
#endif
/// Float value from string (if applicable).
///

View File

@@ -19,8 +19,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit) && os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public extension UIApplication {
/// Application running environment.

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
// MARK: - Properties
public extension UIButton {

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public extension UICollectionView {

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
public extension UIColor {
var redValue: CGFloat{ return CIColor(color: self).red }

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
public extension UIDevice {
static var topNotch: CGFloat {
@@ -78,7 +80,6 @@ public extension UIDevice {
case "iPhone8,2": return "iPhone 6s Plus"
case "iPhone9,1", "iPhone9,3": return "iPhone 7"
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus"
case "iPhone8,4", "iPhone12,8": return "iPhone SE"
case "iPhone10,1", "iPhone10,4": return "iPhone 8"
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
case "iPhone10,3", "iPhone10,6": return "iPhone X"
@@ -92,30 +93,76 @@ public extension UIDevice {
case "iPhone13,2": return "iPhone 12"
case "iPhone13,3": return "iPhone 12 Pro"
case "iPhone13,4": return "iPhone 12 Pro Max"
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2"
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3"
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4"
case "iPhone14,4": return "iPhone 13 mini"
case "iPhone14,5": return "iPhone 13"
case "iPhone14,2": return "iPhone 13 Pro"
case "iPhone14,3": return "iPhone 13 Pro Max"
case "iPhone14,7": return "iPhone 14"
case "iPhone14,8": return "iPhone 14 Plus"
case "iPhone15,2": return "iPhone 14 Pro"
case "iPhone15,3": return "iPhone 14 Pro Max"
case "iPhone15,4": return "iPhone 15"
case "iPhone15,5": return "iPhone 15 Plus"
case "iPhone16,1": return "iPhone 15 Pro"
case "iPhone16,2": return "iPhone 15 Pro Max"
case "iPhone17,3": return "iPhone 16"
case "iPhone17,4": return "iPhone 16 Plus"
case "iPhone17,1": return "iPhone 16 Pro"
case "iPhone17,2": return "iPhone 16 Pro Max"
case "iPhone17,5": return "iPhone 16e"
case "iPhone18,3": return "iPhone 17"
case "iPhone18,4": return "iPhone Air"
case "iPhone18,1": return "iPhone 17 Pro"
case "iPhone18,2": return "iPhone 17 Pro Max"
case "iPhone8,4": return "iPhone SE"
case "iPhone12,8": return "iPhone SE (2nd generation)"
case "iPhone14,6": return "iPhone SE (3rd generation)"
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2"
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)"
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)"
case "iPad6,11", "iPad6,12": return "iPad (5th generation)"
case "iPad7,5", "iPad7,6": return "iPad (6th generation)"
case "iPad7,11", "iPad7,12": return "iPad (7th generation)"
case "iPad11,6", "iPad11,7": return "iPad (8th generation)"
case "iPad12,1", "iPad12,2": return "iPad (9th generation)"
case "iPad13,18", "iPad13,19": return "iPad (10th generation)"
case "iPad15,7", "iPad15,8": return "iPad (11th generation)"
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air"
case "iPad5,3", "iPad5,4": return "iPad Air 2"
case "iPad6,11", "iPad6,12": return "iPad 5"
case "iPad7,5", "iPad7,6": return "iPad 6"
case "iPad7,11", "iPad7,12": return "iPad 7"
case "iPad11,4", "iPad11,5": return "iPad Air (3rd generation)"
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini"
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2"
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3"
case "iPad5,1", "iPad5,2": return "iPad Mini 4"
case "iPad11,1", "iPad11,2": return "iPad Mini 5"
case "iPad11,3", "iPad11,4": return "iPad Air (3rd generation)"
case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)"
case "iPad13,16", "iPad13,17": return "iPad Air (5th generation)"
case "iPad14,8", "iPad14,9": return "iPad Air (11-inch) (M2)"
case "iPad14,10", "iPad14,11": return "iPad Air (13-inch) (M2)"
case "iPad15,3", "iPad15,4": return "iPad Air (11-inch) (M3)"
case "iPad15,5", "iPad15,6": return "iPad Air (13-inch) (M3)"
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini"
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2"
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3"
case "iPad5,1", "iPad5,2": return "iPad mini 4"
case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)"
case "iPad14,1", "iPad14,2": return "iPad mini (6th generation)"
case "iPad16,1", "iPad16,2": return "iPad mini (A17 Pro)"
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)"
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch)"
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return "iPad Pro (11-inch)"
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return "iPad Pro (12.9-inch) (3rd generation)"
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return "iPad Pro (11-inch) (1st generation)"
case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)"
case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": return "iPad Pro (11-inch) (3rd generation)"
case "iPad14,3", "iPad14,4": return "iPad Pro (11-inch) (4th generation)"
case "iPad16,3", "iPad16,4": return "iPad Pro (11-inch) (M4)"
case "iPad17,1", "iPad17,2": return "iPad Pro (11-inch) (M5)"
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)"
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return "iPad Pro (12.9-inch) (3rd generation)"
case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)"
case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11":return "iPad Pro (12.9-inch) (5th generation)"
case "iPad14,5", "iPad14,6": return "iPad Pro (12.9-inch) (6th generation)"
case "iPad16,5", "iPad16,6": return "iPad Pro (13-inch) (M4)"
case "iPad17,3", "iPad17,4": return "iPad Pro (13-inch) (M5)"
case "AppleTV5,3": return "Apple TV"
case "AppleTV6,2": return "Apple TV 4K"
case "AudioAccessory1,1": return "HomePod"
case "i386", "x86_64": return "Simulator \(identifier)"
case "AudioAccessory5,1": return "HomePod mini"
default: return identifier
}
#endif

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
public extension UIImage {
//Extension Required by RoundedButton to create UIImage from UIColor
func imageWithColor(color: UIColor) -> UIImage {

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
@MainActor
public extension UIImageView {

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
public extension UILabel {
func lineNumbers() -> Int{

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
import QuartzCore
public extension UINavigationController {

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public extension UIResponder {

View File

@@ -21,7 +21,9 @@
#if canImport(UIKit) && !os(watchOS)
#if canImport(UIKit)
import UIKit
#endif
// MARK: - Methods
public extension UIScrollView {

View File

@@ -21,7 +21,9 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS)
// MARK: - Initializers

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public class CustomTabBadge: UILabel {

View File

@@ -21,9 +21,11 @@
import Foundation
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
//MARK: - UITableView Animation Cell
public typealias UITableViewCellAnimation = (UITableViewCell, IndexPath, UITableView) -> Void

View File

@@ -21,8 +21,10 @@
import Foundation
#if canImport(UIKit) && os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public extension UITapGestureRecognizer {

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
public extension UITextField {

View File

@@ -21,8 +21,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
typealias GradientPoints = (startPoint: CGPoint, endPoint: CGPoint)

View File

@@ -22,8 +22,10 @@
import Foundation
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
import QuartzCore
public enum ToastPosition {

View File

@@ -20,8 +20,10 @@
// THE SOFTWARE.
#if os(iOS) || os(macOS)
#if canImport(UIKit)
#if canImport(UIKit)
import UIKit
#endif
import AVFoundation
import Photos

View File

@@ -19,9 +19,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(macOS)
#if canImport(UIKit)
/// A protocol for delegates of `ImageZoomController` to provide callbacks for zoom and close events.
@objc public protocol ImageZoomControllerDelegate {
/// Called when the image in the controller is zoomed.

View File

@@ -20,6 +20,8 @@
// THE SOFTWARE.
// MARK: - Framework headers
#if canImport(UIKit)
import Foundation
import UIKit
// MARK: - Protocols
@@ -231,54 +233,37 @@ public extension LCSnackBarView {
/// Handles the `keyboardWillShowNotification` to adjust the snackbar's position.
/// - Parameter notification: The `Notification` object containing keyboard information.
@objc private func keyboardWillShow(_ notification: Notification?) -> Void {
if let info = notification?.userInfo {
@MainActor
@objc private func keyboardWillShow(_ notification: Notification?) {
guard let info = notification?.userInfo else { return }
systemKeyboardVisible = true
//
let curveUserInfoKey = UIResponder.keyboardAnimationCurveUserInfoKey
let durationUserInfoKey = UIResponder.keyboardAnimationDurationUserInfoKey
let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey
//
var animationCurve: UIView.AnimationOptions = .curveEaseOut
var animationDuration: TimeInterval = 0.25
var height:CGFloat = 0.0
// Getting keyboard animation.
if let curve = info[curveUserInfoKey] as? UIView.AnimationOptions {
animationCurve = curve
}
let animationCurveRaw = (info[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue
?? UInt(UIView.AnimationCurve.easeOut.rawValue)
let animationDuration = (info[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
?? 0.25
let keyboardFrame = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
?? .zero
let height = keyboardFrame.height
let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw << 16)
// Getting keyboard animation duration
if let duration = info[durationUserInfoKey] as? TimeInterval {
animationDuration = duration
}
// Getting UIKeyboardSize.
if let kbFrame = info[frameEndUserInfoKey] as? CGRect {
height = kbFrame.size.height
}
DispatchQueue.main.async { [weak self] in
UIView.animate(withDuration: animationDuration,
UIView.animate(
withDuration: animationDuration,
delay: 0,
options: animationCurve,
animations: {
options: animationCurve
) { [weak self] in
self?.frame.origin.y += height
})
}
}
}
/// Handles the `keyboardWillHideNotification`.
/// - Parameter notification: The `Notification` object.
@objc private func keyboardWillHide(_ notification: Notification?) -> Void {
DispatchQueue.main.async { [weak self] in
self?.systemKeyboardVisible = false
@MainActor
@objc private func keyboardWillHide(_ notification: Notification?) {
systemKeyboardVisible = false
// keyboard is hidded
}
}
/// Updates the snackbar's style properties, such as width and corner radius, based on `_style`.
private func updateStyle() {
@@ -512,3 +497,5 @@ public extension LCSnackBarView {
}
}
}
#endif

View File

@@ -18,7 +18,7 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if canImport(SwiftUI)
#if canImport(SwiftUI) && os(iOS)
import SwiftUI
/// `LCENavigationState` is an `ObservableObject` that manages the state for `LCENavigationView`.
@@ -47,6 +47,8 @@ class LCENavigationState: ObservableObject {
@Published var title: (any View) = Text("")
/// The subtitle view of the navigation bar.
@Published var subTitle: (any View) = Text("")
/// The background color of the navigation bar.
@Published var navigationBarBackgroundColor: Color = .clear
}
/// `LCENavigationView` is a SwiftUI `View` that provides a customizable navigation bar.
@@ -79,7 +81,7 @@ public struct LCENavigationView<Content: View>: View {
/// The body of the `LCENavigationView`.
public var body: some View {
VStack {
VStack(spacing: 0) {
if !state.hideNavigationBar {
NavigationBarView
}
@@ -100,7 +102,7 @@ public struct LCENavigationView<Content: View>: View {
.font(.headline)
.padding()
.background {
Color.clear.ignoresSafeArea(edges: .top)
state.navigationBarBackgroundColor.ignoresSafeArea(edges: .top)
}
}
@@ -217,6 +219,14 @@ public struct LCENavigationView<Content: View>: View {
state.hideNavigationBar = hide
return self
}
/// Sets the background color for the navigation bar.
/// - Parameter color: The color to use as the navigation bar background.
/// - Returns: The `LCENavigationView` instance for chaining.
public func setNavigationBarBackgroundColor(_ color: Color) -> LCENavigationView {
state.navigationBarBackgroundColor = color
return self
}
}
/// Extension to `FormatStyle` to format any value as a string.