22 Commits

Author SHA1 Message Date
Daniel Arantes Loverde
2c781fbac6 Update LCEssentials+API.swift 2026-06-06 11:00:08 -03:00
Daniel Arantes Loverde
966b439277 Update LCEssentials+API.swift 2026-06-06 10:53:16 -03:00
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
40 changed files with 358 additions and 151 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.DS_Store .DS_Store
/.build /.build
/build
/Packages /Packages
xcuserdata/ xcuserdata/
DerivedData/ 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, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
Autor: Daniel Arantes Loverde

View File

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

View File

@@ -2,7 +2,24 @@
import PackageDescription import PackageDescription
import Foundation import Foundation
let isLocalDevelopment = FileManager.default.fileExists(atPath: "../LCECryptoKit/PrivateLib/LCECryptoKitBinary") 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( let package = Package(
name: "LCEssentials", name: "LCEssentials",
@@ -17,17 +34,10 @@ let package = Package(
name: "LCEssentials", name: "LCEssentials",
targets: ["LCEssentials"]), targets: ["LCEssentials"]),
], ],
dependencies: [ dependencies: packageDependencies,
.package(
url: isLocalDevelopment ?
"../LCECryptoKit/PrivateLib/LCECryptoKitBinary" :
"https://60c260c85d3a2fe840411b0ff98f521b5eca3c56@git.loverde.com.br/Loverde-Company-LTDA/LCECryptoKitBinary.git",
exact: "1.0.0"
)
],
targets: [ targets: [
.target( .target(
name: "LCEssentials", name: "LCEssentials",
dependencies: [.product(name: "LCECryptoKit", package: "lcecryptokitbinary")]), dependencies: targetDependencies),
] ]
) )

View File

@@ -17,14 +17,14 @@ Installation
#### Swift Package Manager (SPM) #### Swift Package Manager (SPM)
``` swift ``` swift
dependencies: [ 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: You can also add it via XCode SPM editor with URL:
``` swift ``` swift
http://git.loverde.com.br:3000/git/LCEssentials.git https://git.loverde.com.br/Loverde-Company-LTDA/LCEssentials
``` ```
## Usage example ## Usage example
@@ -59,12 +59,11 @@ And then import `LCEssentials ` wherever you import UIKit or SwiftUI
import LCEssentials import LCEssentials
``` ```
Author:
----
Any question or doubts, please send thru email Any question or doubts, please send thru email
Daniel Arantes Loverde - <daniel@loverde.com.br> 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 "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) [![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. // Created by iOSDevCenters on 11/12/15.
// Copyright © 2016 iOSDevCenters. All rights reserved. // Copyright © 2016 iOSDevCenters. All rights reserved.
// //
#if canImport(UIKit)
import UIKit import UIKit
#if os(iOS) || os(macOS) #endif
#if canImport(UIKit)
import ImageIO import ImageIO
//let jeremyGif = UIImage.gifWithName("jeremy") //let jeremyGif = UIImage.gifWithName("jeremy")

View File

@@ -21,21 +21,76 @@
import Foundation import Foundation
#if canImport(LCECryptoKit)
import LCECryptoKit import LCECryptoKit
public final class LCECrypto { public final class LCECryptoKitManager {
private let hashKey: String private let hashKey: String
init(privateKey: String){ public init() {
self.hashKey = ""
}
public init(privateKey: String){
self.hashKey = privateKey self.hashKey = privateKey
} }
func encodeOTP(email: String, password: String) -> String? { 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) return LCECryptoKit.encodeSeed(email: email, password: password, hashKey: self.hashKey)
} }
func decodeOTP(_ otpHash: String) -> Bool { public func decodeOTPWithKey(_ otpHash: String) -> Bool {
LCECryptoKit.decodeSeed(otpKey: otpHash, hashKey: self.hashKey) 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

@@ -45,6 +45,8 @@ public enum httpMethod: String {
case get = "GET" case get = "GET"
/// The PUT method. /// The PUT method.
case put = "PUT" case put = "PUT"
/// The PATCH method.
case patch = "PATCH"
/// The DELETE method. /// The DELETE method.
case delete = "DELETE" case delete = "DELETE"
} }
@@ -88,7 +90,7 @@ public struct API {
/// - Parameters: /// - Parameters:
/// - url: The URL string for the request. /// - url: The URL string for the request.
/// - params: Optional parameters for the request. Can be `[String: Any]` for JSON/form-data, or `Data` for raw body. /// - params: Optional parameters for the request. Can be `[String: Any]` for JSON/form-data, or `Data` for raw body.
/// - method: The HTTP method to use for the request (`.get`, `.post`, `.put`, `.delete`). /// - method: The HTTP method to use for the request (`.get`, `.post`, `.put`, `.delete`,`.patch` ).
/// - headers: Optional custom HTTP headers to be added to the request. These override default headers if there are conflicts. /// - headers: Optional custom HTTP headers to be added to the request. These override default headers if there are conflicts.
/// - jsonEncoding: A boolean indicating whether parameters should be JSON encoded. Defaults to `true`. /// - jsonEncoding: A boolean indicating whether parameters should be JSON encoded. Defaults to `true`.
/// - debug: A boolean indicating whether to print debug logs for the request and response. Defaults to `true`. /// - debug: A boolean indicating whether to print debug logs for the request and response. Defaults to `true`.
@@ -108,8 +110,8 @@ public struct API {
persistConnection: Bool = false) async throws -> T { persistConnection: Bool = false) async throws -> T {
if let urlReq = URL(string: url.replaceURL(params as? [String: Any] ?? [:] )) { if let urlReq = URL(string: url.replaceURL(params as? [String: Any] ?? [:] )) {
var request = URLRequest(url: urlReq, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30) var request = URLRequest(url: urlReq, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeoutInterval)
if method == .post || method == .put || method == .delete { if method == .post || method == .put || method == .delete || method == .patch {
if let params = params as? [String: Any], if let params = params as? [String: Any],
let pathFile = params["file"] as? String, let pathFile = params["file"] as? String,
let fileURL = URL(string: pathFile) { let fileURL = URL(string: pathFile) {

View File

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

View File

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

View File

@@ -21,7 +21,9 @@
import Foundation import Foundation
#if canImport(UIKit)
import UIKit import UIKit
#endif
// MARK: - Methods (Equatable) // MARK: - Methods (Equatable)
public extension Array where Element: 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 { extension Array where Element == NSLayoutConstraint {
@MainActor @MainActor
@@ -169,3 +172,5 @@ extension Array where Element == NSLayoutConstraint {
} }
} }
} }
#endif

View File

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

View File

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

View File

@@ -21,7 +21,9 @@
import Foundation import Foundation
#if canImport(UIKit)
import UIKit import UIKit
#endif
public extension FileManager { 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 { func saveImageToDirectory( _ imageWithPath : String, imagem : UIImage ) -> Bool {
let data = imagem.pngData() let data = imagem.pngData()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,9 +21,11 @@
import Foundation import Foundation
#if canImport(UIKit)
import UIKit import UIKit
#endif
#if os(iOS) || os(macOS) #if canImport(UIKit)
public extension UIDevice { public extension UIDevice {
static var topNotch: CGFloat { static var topNotch: CGFloat {
@@ -64,59 +66,104 @@ public extension UIDevice {
#if os(iOS) #if os(iOS)
switch identifier { switch identifier {
case "iPod5,1": return "iPod touch (5th generation)" case "iPod5,1": return "iPod touch (5th generation)"
case "iPod7,1": return "iPod touch (6th generation)" case "iPod7,1": return "iPod touch (6th generation)"
case "iPod9,1": return "iPod touch (7th generation)" case "iPod9,1": return "iPod touch (7th generation)"
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4"
case "iPhone4,1": return "iPhone 4s" case "iPhone4,1": return "iPhone 4s"
case "iPhone5,1", "iPhone5,2": return "iPhone 5" case "iPhone5,1", "iPhone5,2": return "iPhone 5"
case "iPhone5,3", "iPhone5,4": return "iPhone 5c" case "iPhone5,3", "iPhone5,4": return "iPhone 5c"
case "iPhone6,1", "iPhone6,2": return "iPhone 5s" case "iPhone6,1", "iPhone6,2": return "iPhone 5s"
case "iPhone7,2": return "iPhone 6" case "iPhone7,2": return "iPhone 6"
case "iPhone7,1": return "iPhone 6 Plus" case "iPhone7,1": return "iPhone 6 Plus"
case "iPhone8,1": return "iPhone 6s" case "iPhone8,1": return "iPhone 6s"
case "iPhone8,2": return "iPhone 6s Plus" case "iPhone8,2": return "iPhone 6s Plus"
case "iPhone9,1", "iPhone9,3": return "iPhone 7" case "iPhone9,1", "iPhone9,3": return "iPhone 7"
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" 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,1", "iPhone10,4": return "iPhone 8" case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" case "iPhone10,3", "iPhone10,6": return "iPhone X"
case "iPhone10,3", "iPhone10,6": return "iPhone X" case "iPhone11,2": return "iPhone XS"
case "iPhone11,2": return "iPhone XS" case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
case "iPhone11,4", "iPhone11,6": return "iPhone XS Max" case "iPhone11,8": return "iPhone XR"
case "iPhone11,8": return "iPhone XR" case "iPhone12,1": return "iPhone 11"
case "iPhone12,1": return "iPhone 11" case "iPhone12,3": return "iPhone 11 Pro"
case "iPhone12,3": return "iPhone 11 Pro" case "iPhone12,5": return "iPhone 11 Pro Max"
case "iPhone12,5": return "iPhone 11 Pro Max" case "iPhone13,1": return "iPhone 12 mini"
case "iPhone13,1": return "iPhone 12 mini" case "iPhone13,2": return "iPhone 12"
case "iPhone13,2": return "iPhone 12" case "iPhone13,3": return "iPhone 12 Pro"
case "iPhone13,3": return "iPhone 12 Pro" case "iPhone13,4": return "iPhone 12 Pro Max"
case "iPhone13,4": return "iPhone 12 Pro Max" case "iPhone14,4": return "iPhone 13 mini"
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2" case "iPhone14,5": return "iPhone 13"
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3" case "iPhone14,2": return "iPhone 13 Pro"
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4" case "iPhone14,3": return "iPhone 13 Pro Max"
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" case "iPhone14,7": return "iPhone 14"
case "iPad5,3", "iPad5,4": return "iPad Air 2" case "iPhone14,8": return "iPhone 14 Plus"
case "iPad6,11", "iPad6,12": return "iPad 5" case "iPhone15,2": return "iPhone 14 Pro"
case "iPad7,5", "iPad7,6": return "iPad 6" case "iPhone15,3": return "iPhone 14 Pro Max"
case "iPad7,11", "iPad7,12": return "iPad 7" case "iPhone15,4": return "iPhone 15"
case "iPad11,4", "iPad11,5": return "iPad Air (3rd generation)" case "iPhone15,5": return "iPhone 15 Plus"
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini" case "iPhone16,1": return "iPhone 15 Pro"
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2" case "iPhone16,2": return "iPhone 15 Pro Max"
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3" case "iPhone17,3": return "iPhone 16"
case "iPad5,1", "iPad5,2": return "iPad Mini 4" case "iPhone17,4": return "iPhone 16 Plus"
case "iPad11,1", "iPad11,2": return "iPad Mini 5" case "iPhone17,1": return "iPhone 16 Pro"
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)" case "iPhone17,2": return "iPhone 16 Pro Max"
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch)" case "iPhone17,5": return "iPhone 16e"
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)" case "iPhone18,3": return "iPhone 17"
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)" case "iPhone18,4": return "iPhone Air"
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return "iPad Pro (11-inch)" case "iPhone18,1": return "iPhone 17 Pro"
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return "iPad Pro (12.9-inch) (3rd generation)" case "iPhone18,2": return "iPhone 17 Pro Max"
case "AppleTV5,3": return "Apple TV" case "iPhone8,4": return "iPhone SE"
case "AppleTV6,2": return "Apple TV 4K" case "iPhone12,8": return "iPhone SE (2nd generation)"
case "AudioAccessory1,1": return "HomePod" case "iPhone14,6": return "iPhone SE (3rd generation)"
case "i386", "x86_64": return "Simulator \(identifier)" case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2"
default: return identifier 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 "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 "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
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 "AudioAccessory5,1": return "HomePod mini"
default: return identifier
} }
#endif #endif
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,8 @@
// THE SOFTWARE. // THE SOFTWARE.
// MARK: - Framework headers // MARK: - Framework headers
#if canImport(UIKit)
import Foundation
import UIKit import UIKit
// MARK: - Protocols // MARK: - Protocols
@@ -231,53 +233,36 @@ public extension LCSnackBarView {
/// Handles the `keyboardWillShowNotification` to adjust the snackbar's position. /// Handles the `keyboardWillShowNotification` to adjust the snackbar's position.
/// - Parameter notification: The `Notification` object containing keyboard information. /// - Parameter notification: The `Notification` object containing keyboard information.
@objc private func keyboardWillShow(_ notification: Notification?) -> Void { @MainActor
@objc private func keyboardWillShow(_ notification: Notification?) {
guard let info = notification?.userInfo else { return }
if let info = notification?.userInfo { systemKeyboardVisible = true
systemKeyboardVisible = true let animationCurveRaw = (info[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue
// ?? UInt(UIView.AnimationCurve.easeOut.rawValue)
let curveUserInfoKey = UIResponder.keyboardAnimationCurveUserInfoKey let animationDuration = (info[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let durationUserInfoKey = UIResponder.keyboardAnimationDurationUserInfoKey ?? 0.25
let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey let keyboardFrame = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
// ?? .zero
var animationCurve: UIView.AnimationOptions = .curveEaseOut let height = keyboardFrame.height
var animationDuration: TimeInterval = 0.25 let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw << 16)
var height:CGFloat = 0.0
// Getting keyboard animation. UIView.animate(
if let curve = info[curveUserInfoKey] as? UIView.AnimationOptions { withDuration: animationDuration,
animationCurve = curve delay: 0,
} options: animationCurve
) { [weak self] in
// Getting keyboard animation duration self?.frame.origin.y += height
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,
delay: 0,
options: animationCurve,
animations: {
self?.frame.origin.y += height
})
}
} }
} }
/// Handles the `keyboardWillHideNotification`. /// Handles the `keyboardWillHideNotification`.
/// - Parameter notification: The `Notification` object. /// - Parameter notification: The `Notification` object.
@objc private func keyboardWillHide(_ notification: Notification?) -> Void { @MainActor
DispatchQueue.main.async { [weak self] in @objc private func keyboardWillHide(_ notification: Notification?) {
self?.systemKeyboardVisible = false systemKeyboardVisible = false
// keyboard is hidded // keyboard is hidded
}
} }
/// Updates the snackbar's style properties, such as width and corner radius, based on `_style`. /// Updates the snackbar's style properties, such as width and corner radius, based on `_style`.
@@ -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, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#if canImport(SwiftUI) #if canImport(SwiftUI) && os(iOS)
import SwiftUI import SwiftUI
/// `LCENavigationState` is an `ObservableObject` that manages the state for `LCENavigationView`. /// `LCENavigationState` is an `ObservableObject` that manages the state for `LCENavigationView`.
@@ -47,6 +47,8 @@ class LCENavigationState: ObservableObject {
@Published var title: (any View) = Text("") @Published var title: (any View) = Text("")
/// The subtitle view of the navigation bar. /// The subtitle view of the navigation bar.
@Published var subTitle: (any View) = Text("") @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. /// `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`. /// The body of the `LCENavigationView`.
public var body: some View { public var body: some View {
VStack { VStack(spacing: 0) {
if !state.hideNavigationBar { if !state.hideNavigationBar {
NavigationBarView NavigationBarView
} }
@@ -100,7 +102,7 @@ public struct LCENavigationView<Content: View>: View {
.font(.headline) .font(.headline)
.padding() .padding()
.background { .background {
Color.clear.ignoresSafeArea(edges: .top) state.navigationBarBackgroundColor.ignoresSafeArea(edges: .top)
} }
} }
@@ -217,6 +219,14 @@ public struct LCENavigationView<Content: View>: View {
state.hideNavigationBar = hide state.hideNavigationBar = hide
return self 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. /// Extension to `FormatStyle` to format any value as a string.