From 27b6b45ee9ee4fb99465814bf8fabe6bcc5015c5 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 23 Oct 2025 13:26:03 -0300 Subject: [PATCH] Complete documentation --- LCEssentials_DOCUMENTATION.md | 3195 +++++++++++++++++++++++++++++++++ README.md | 31 +- 2 files changed, 3196 insertions(+), 30 deletions(-) create mode 100644 LCEssentials_DOCUMENTATION.md diff --git a/LCEssentials_DOCUMENTATION.md b/LCEssentials_DOCUMENTATION.md new file mode 100644 index 0000000..91103eb --- /dev/null +++ b/LCEssentials_DOCUMENTATION.md @@ -0,0 +1,3195 @@ +# 📚 LCEssentials - Documentação Completa + +> **Loverde Co. Essentials Swift Scripts** - Biblioteca completa de utilitários Swift para iOS, macOS, tvOS e watchOS + +## 📋 Índice + +- [🔧 Instalação](#-instalação) +- [🏗️ Classes Principais](#️-classes-principais) +- [🎨 Componentes UI](#-componentes-ui) +- [⚡ SwiftUI](#-swiftui) +- [🔗 Extensões](#-extensões) +- [📱 Helpers](#-helpers) +- [🔐 Criptografia](#-criptografia) +- [🌐 API & Networking](#-api--networking) + +--- + +## 🔧 Instalação + +### Swift Package Manager (SPM) + +```swift +dependencies: [ + .package(url: "https://git.loverde.com.br/Loverde-Company-LTDA/LCEssentials", .upToNextMajor(from: "1.0.0")) +] +``` + +### Xcode SPM + +``` +https://git.loverde.com.br/Loverde-Company-LTDA/LCEssentials +``` + +### Import + +```swift +import LCEssentials +``` + +--- + +## 🏗️ Classes Principais + +
+LCEssentials - Classe Principal + +### LCEssentials + +Classe principal com métodos utilitários gerais para desenvolvimento iOS. + +#### Propriedades Estáticas + +**Declaração:** + +```swift +public struct LCEssentials { + public static let DEFAULT_ERROR_DOMAIN = "LoverdeCoErrorDomain" + public static let DEFAULT_ERROR_CODE = -99 + public static let DEFAULT_ERROR_MSG = "Error Unknow" +} +``` + +**Uso:** + +```swift +// Verificar informações do app +let appName = LCEssentials.appDisplayName +let appVersion = LCEssentials.appVersion +let appBuild = LCEssentials.appBuild +let bundleID = LCEssentials.appBundleID + +// Verificar informações do dispositivo +let isDebug = LCEssentials.isInDebuggingMode +let isTestFlight = LCEssentials.isInTestFlight +let isSimulator = LCEssentials.isRunningOnSimulator +let isPad = LCEssentials.isPad +let isPhone = LCEssentials.isPhone + +// Dimensões da tela +let screenWidth = LCEssentials.screenWidth +let screenHeight = LCEssentials.screenHeight +``` + +#### Métodos Principais + +**backgroundThread** + +```swift +static func backgroundThread(delay: Double = 0.0, + background: (@Sendable () -> Void)? = nil, + completion: (@Sendable () -> Void)? = nil) +``` + +**Uso:** + +```swift +LCEssentials.backgroundThread(delay: 0.6, background: { + // Fazer algo em background + print("Processando...") +}) { + // Quando terminar, atualizar UI + DispatchQueue.main.async { + self.updateUI() + } +} +``` + +**delay** + +```swift +@discardableResult +static func delay(milliseconds: Double, + queue: DispatchQueue = .main, + completion: @escaping () -> Void) -> DispatchWorkItem +``` + +**Uso:** + +```swift +let task = LCEssentials.delay(milliseconds: 2000) { + print("Executado após 2 segundos") +} +// Para cancelar: task.cancel() +``` + +**debounce** + +```swift +static func debounce(millisecondsDelay: Int, + queue: DispatchQueue = .main, + action: @escaping (() -> Void)) -> () -> Void +``` + +**Uso:** + +```swift +let debouncedAction = LCEssentials.debounce(millisecondsDelay: 500) { + print("Executado apenas uma vez após 500ms de inatividade") +} + +// Chamar múltiplas vezes rapidamente +debouncedAction() // Primeira chamada +debouncedAction() // Segunda chamada (cancela a primeira) +debouncedAction() // Terceira chamada (cancela a segunda) +// Apenas a última será executada após 500ms +``` + +**getTopViewController** + +```swift +static func getTopViewController(base: UIViewController? = nil, + aboveBars: Bool = true) -> UIViewController? +``` + +**Uso:** + +```swift +if let topVC = LCEssentials.getTopViewController() { + // Fazer algo com o view controller do topo + topVC.present(alert, animated: true) +} +``` + +**shareApp** + +```swift +static func shareApp(message: String = "", url: String = "") +``` + +**Uso:** + +```swift +LCEssentials.shareApp(message: "Confira este app!", url: "https://appstore.com/app") +``` + +**call** + +```swift +static func call(_ number: String!) +``` + +**Uso:** + +```swift +LCEssentials.call("+5511999999999") +``` + +**openSafari** + +```swift +static func openSafari(_ urlStr: String) +``` + +**Uso:** + +```swift +LCEssentials.openSafari("https://loverde.com.br") +``` + +**downloadFileWithCache** + +```swift +static func downloadFileWithCache(from url: URL, + completion: @escaping (Result) -> Void) +``` + +**Uso:** + +```swift +LCEssentials.downloadFileWithCache(from: imageURL) { result in + switch result { + case .success(let localURL): + print("Arquivo baixado: \(localURL)") + case .failure(let error): + print("Erro: \(error)") + } +} +``` + +**cleanExpiredCache** + +```swift +static func cleanExpiredCache(expiration: TimeInterval = 7 * 24 * 60 * 60) +``` + +**Uso:** + +```swift +// Limpar cache com mais de 1 semana +LCEssentials.cleanExpiredCache(expiration: 7 * 24 * 60 * 60) +``` + +
+ +
+API - Sistema de Requisições HTTP + +### API + +Sistema completo para requisições HTTP com suporte a certificados, cache e retry automático. + +#### Configuração + +**Declaração:** + +```swift +@available(iOS 13.0.0, *) +@MainActor +public struct API { + public static let shared = API() + public static var defaultParams: [String:Any] = [String: Any]() + public static var persistConnectionDelay: Double = 3 +} +``` + +**Uso:** + +```swift +// Configurar parâmetros padrão +API.defaultParams = ["api_key": "sua_chave_aqui"] + +// Configurar certificado cliente +let certData = Data(contentsOf: Bundle.main.url(forResource: "client", withExtension: "p12")!) +API.shared.setupCertificationRequest(certData: certData, password: "senha_do_certificado") +``` + +#### Método Principal + +**request** + +```swift +public func request(url: String, + params: Any? = nil, + method: httpMethod, + headers: [String: String] = [:], + jsonEncoding: Bool = true, + debug: Bool = true, + timeoutInterval: TimeInterval = 30, + networkServiceType: URLRequest.NetworkServiceType = .default, + persistConnection: Bool = false) async throws -> T +``` + +**Uso - GET Request:** + +```swift +struct User: Codable { + let id: Int + let name: String + let email: String +} + +do { + let users: [User] = try await API.shared.request( + url: "https://api.exemplo.com/users", + method: .get, + headers: ["Authorization": "Bearer token_aqui"] + ) + print("Usuários: \(users)") +} catch { + print("Erro: \(error)") +} +``` + +**Uso - POST Request:** + +```swift +struct LoginRequest: Codable { + let email: String + let password: String +} + +struct LoginResponse: Codable { + let token: String + let user: User +} + +do { + let loginData = LoginRequest(email: "user@exemplo.com", password: "senha123") + let response: LoginResponse = try await API.shared.request( + url: "https://api.exemplo.com/login", + params: loginData, + method: .post + ) + print("Token: \(response.token)") +} catch { + print("Erro no login: \(error)") +} +``` + +**Uso - Upload de Arquivo:** + +```swift +let fileURL = URL(fileURLWithPath: "/path/to/image.jpg") +let params = [ + "title": "Minha Imagem", + "description": "Descrição da imagem", + "file": fileURL.absoluteString +] + +do { + let response: [String: Any] = try await API.shared.request( + url: "https://api.exemplo.com/upload", + params: params, + method: .post + ) + print("Upload realizado: \(response)") +} catch { + print("Erro no upload: \(error)") +} +``` + +**Uso - Com Retry Automático:** + +```swift +do { + let data: MyResponse = try await API.shared.request( + url: "https://api.exemplo.com/data", + method: .get, + persistConnection: true // Habilita retry automático + ) +} catch { + print("Erro após tentativas: \(error)") +} +``` + +
+ +
+LCECryptoKitManager - Gerenciador de Criptografia + +### LCECryptoKitManager + +Gerenciador para operações de criptografia usando LCECryptoKit. + +#### Inicialização + +**Declaração:** + +```swift +public final class LCECryptoKitManager { + public init() + public init(privateKey: String) +} +``` + +**Uso:** + +```swift +// Sem chave privada +let cryptoManager = LCECryptoKitManager() + +// Com chave privada +let cryptoManager = LCECryptoKitManager(privateKey: "minha_chave_privada") +``` + +#### Métodos + +**generateKey** + +```swift +public static func generateKey() -> String +``` + +**Uso:** + +```swift +let newKey = LCECryptoKitManager.generateKey() +print("Nova chave gerada: \(newKey)") +``` + +**encodeTP** + +```swift +public func encodeTP(email: String, password: String) -> String? +``` + +**Uso:** + +```swift +if let encoded = cryptoManager.encodeTP(email: "user@exemplo.com", password: "senha123") { + print("Dados codificados: \(encoded)") +} +``` + +**decodeOTP** + +```swift +public func decodeOTP(_ otpHash: String) -> String? +``` + +**Uso:** + +```swift +if let decoded = cryptoManager.decodeOTP("hash_codificado") { + print("Dados decodificados: \(decoded)") +} +``` + +**encodeOTPWithKey** + +```swift +public func encodeOTPWithKey(email: String, password: String) -> String? +``` + +**Uso:** + +```swift +if let encoded = cryptoManager.encodeOTPWithKey(email: "user@exemplo.com", password: "senha123") { + print("Dados codificados com chave: \(encoded)") +} +``` + +**decodeOTPWithKey** + +```swift +public func decodeOTPWithKey(_ otpHash: String) -> Bool +``` + +**Uso:** + +```swift +let isValid = cryptoManager.decodeOTPWithKey("hash_codificado") +if isValid { + print("Hash válido!") +} else { + print("Hash inválido!") +} +``` + +
+ +
+LCSingleton - Protocolo Singleton + +### LCSingleton + +Protocolo para implementação de padrão Singleton. + +**Declaração:** + +```swift +@objc public protocol LCESingletonDelegate: AnyObject { + @objc optional func singleton(object: Any?, withData: Any) +} +``` + +**Uso:** + +```swift +class MySingleton: LCESingletonDelegate { + static let shared = MySingleton() + + private init() {} + + func singleton(object: Any?, withData: Any) { + print("Singleton chamado com dados: \(withData)") + } +} + +// Uso +MySingleton.shared.singleton(object: nil, withData: "dados_importantes") +``` + +
+ +
+GifHelper - Utilitários para GIFs + +### GifHelper + +Utilitários para trabalhar com arquivos GIF animados. + +#### UIImageView Extensions + +**loadGif** + +```swift +public func loadGif(name: String) +public func loadGif(asset: String) +``` + +**Uso:** + +```swift +// Carregar GIF do bundle +imageView.loadGif(name: "animacao") + +// Carregar GIF do asset catalog +imageView.loadGif(asset: "animacao_gif") +``` + +#### UIImage Extensions + +**gif** + +```swift +public class func gif(data: Data) -> UIImage? +public class func gif(url: String) -> UIImage? +public class func gif(name: String) -> UIImage? +public class func gif(asset: String) -> UIImage? +``` + +**Uso:** + +```swift +// Criar GIF a partir de Data +if let gifData = Data(contentsOf: gifURL) { + let gifImage = UIImage.gif(data: gifData) + imageView.image = gifImage +} + +// Criar GIF a partir de URL +if let gifImage = UIImage.gif(url: "https://exemplo.com/animacao.gif") { + imageView.image = gifImage +} + +// Criar GIF a partir do bundle +if let gifImage = UIImage.gif(name: "animacao") { + imageView.image = gifImage +} + +// Criar GIF a partir do asset catalog +if let gifImage = UIImage.gif(asset: "animacao_gif") { + imageView.image = gifImage +} +``` + +
+ +
+Repositorio - Estrutura de Repositório + +### Repositorio + +Estrutura básica para repositórios. + +**Declaração:** + +```swift +struct Repositorio { + var text = "Hello, World!" + private var testBin: [Int] = [] +} +``` + +**Uso:** + +```swift +var repo = Repositorio() +print(repo.text) // "Hello, World!" +``` + +
+ +--- + +## 🎨 Componentes UI + +
+LCSnackBarView - Sistema de Notificações + +### LCSnackBarView + +Sistema completo de notificações estilo SnackBar para iOS. + +#### Inicialização + +**Declaração:** + +```swift +public final class LCSnackBarView: UIView { + public init(style: LCSnackBarViewType = .default, + orientation: LCSnackBarOrientation = .top, + delegate: LCSnackBarViewDelegate? = nil) +} +``` + +**Uso:** + +```swift +// Básico +let snackBar = LCSnackBarView() +snackBar.configure(text: "Operação realizada com sucesso!") + .present() + +// Com delegate +let snackBar = LCSnackBarView(delegate: self) +snackBar.configure(text: "Mensagem importante!") + .present() + +// Com estilo e orientação +let snackBar = LCSnackBarView(style: .rounded, orientation: .bottom) +snackBar.configure(text: "Mensagem no bottom!") + .present() +``` + +#### Configuração + +**configure(text:)** + +```swift +@discardableResult +func configure(text: String) -> Self +``` + +**Uso:** + +```swift +snackBar.configure(text: "Sua mensagem aqui") +``` + +**configure(textColor:)** + +```swift +@discardableResult +func configure(textColor: UIColor) -> Self +``` + +**Uso:** + +```swift +snackBar.configure(textColor: .white) +``` + +**configure(textFont:alignment:)** + +```swift +@discardableResult +func configure(textFont: UIFont, alignment: NSTextAlignment = .center) -> Self +``` + +**Uso:** + +```swift +snackBar.configure(textFont: .boldSystemFont(ofSize: 16), alignment: .left) +``` + +**configure(backgroundColor:)** + +```swift +@discardableResult +func configure(backgroundColor: UIColor) -> Self +``` + +**Uso:** + +```swift +snackBar.configure(backgroundColor: .systemGreen) +``` + +**configure(exibition:)** + +```swift +@discardableResult +func configure(exibition timer: LCSnackBarTimer) -> Self +``` + +**Uso:** + +```swift +snackBar.configure(exibition: .medium) // 5 segundos +snackBar.configure(exibition: .infinity) // Permanece até ser fechado manualmente +``` + +**configure(imageIconBefore:withTintColor:)** + +```swift +@discardableResult +func configure(imageIconBefore icon: UIImageView, withTintColor: UIColor? = nil) -> Self +``` + +**Uso:** + +```swift +let icon = UIImageView(image: UIImage(systemName: "checkmark.circle.fill")) +snackBar.configure(imageIconBefore: icon, withTintColor: .white) +``` + +**present(completion:)** + +```swift +func present(completion: (()->())? = nil) +``` + +**Uso:** + +```swift +snackBar.present { + print("SnackBar apresentado!") +} +``` + +#### Enums + +**LCSnackBarViewType** + +```swift +public enum LCSnackBarViewType { + case `default` // Retangular + case rounded // Arredondado +} +``` + +**LCSnackBarOrientation** + +```swift +public enum LCSnackBarOrientation { + case top // Aparece no topo + case bottom // Aparece na parte inferior +} +``` + +**LCSnackBarTimer** + +```swift +public enum LCSnackBarTimer: CGFloat { + case infinity = 0 // Permanece até ser fechado + case minimum = 2 // 2 segundos + case medium = 5 // 5 segundos + case maximum = 10 // 10 segundos +} +``` + +#### Delegate + +**LCSnackBarViewDelegate** + +```swift +@objc public protocol LCSnackBarViewDelegate { + @objc optional func snackbar(didStartExibition: LCSnackBarView) + @objc optional func snackbar(didTouchOn snackbar: LCSnackBarView) + @objc optional func snackbar(didEndExibition: LCSnackBarView) +} +``` + +**Uso:** + +```swift +class MyViewController: UIViewController, LCSnackBarViewDelegate { + + func showNotification() { + let snackBar = LCSnackBarView(delegate: self) + snackBar.configure(text: "Notificação importante!") + .present() + } + + func snackbar(didStartExibition: LCSnackBarView) { + print("SnackBar começou a ser exibido") + } + + func snackbar(didTouchOn snackbar: LCSnackBarView) { + print("Usuário tocou no SnackBar") + } + + func snackbar(didEndExibition: LCSnackBarView) { + print("SnackBar foi fechado") + } +} +``` + +#### Exemplo Completo + +```swift +class ViewController: UIViewController, LCSnackBarViewDelegate { + + @IBAction func showSuccessNotification() { + let snackBar = LCSnackBarView(style: .rounded, orientation: .top, delegate: self) + + let icon = UIImageView(image: UIImage(systemName: "checkmark.circle.fill")) + + snackBar + .configure(text: "Dados salvos com sucesso!") + .configure(textColor: .white) + .configure(backgroundColor: .systemGreen) + .configure(exibition: .medium) + .configure(imageIconBefore: icon, withTintColor: .white) + .present() + } + + @IBAction func showErrorNotification() { + let snackBar = LCSnackBarView(style: .default, orientation: .bottom) + + snackBar + .configure(text: "Erro ao salvar dados. Tente novamente.") + .configure(textColor: .white) + .configure(backgroundColor: .systemRed) + .configure(exibition: .maximum) + .present() + } +} +``` + +
+ +
+ImagePickerController - Seletor de Imagens + +### ImagePickerController + +Controlador para seleção de imagens da câmera ou biblioteca de fotos. + +#### Inicialização + +**Declaração:** + +```swift +public class ImagePickerController: UIViewController, UINavigationControllerDelegate { + public weak var delegate: ImagePickerControllerDelegate? + public var isEditable: Bool = false + public init() +} +``` + +**Uso:** + +```swift +let imagePicker = ImagePickerController() +imagePicker.delegate = self +imagePicker.isEditable = true // Permite edição da imagem +``` + +#### Delegate + +**ImagePickerControllerDelegate** + +```swift +public protocol ImagePickerControllerDelegate: AnyObject { + func imagePicker(didSelect image: UIImage?) +} +``` + +**Uso:** + +```swift +class MyViewController: UIViewController, ImagePickerControllerDelegate { + + @IBAction func selectImage() { + let imagePicker = ImagePickerController() + imagePicker.delegate = self + imagePicker.isEditable = true + present(imagePicker, animated: true) + } + + func imagePicker(didSelect image: UIImage?) { + if let selectedImage = image { + imageView.image = selectedImage + print("Imagem selecionada: \(selectedImage.size)") + } else { + print("Seleção cancelada") + } + } +} +``` + +#### Método Principal + +**openImagePicker** + +```swift +public func openImagePicker() +``` + +**Uso:** + +```swift +let imagePicker = ImagePickerController() +imagePicker.delegate = self +imagePicker.openImagePicker() // Abre automaticamente o seletor +``` + +#### Exemplo Completo + +```swift +class ProfileViewController: UIViewController, ImagePickerControllerDelegate { + + @IBOutlet weak var profileImageView: UIImageView! + + @IBAction func changeProfileImage() { + let imagePicker = ImagePickerController() + imagePicker.delegate = self + imagePicker.isEditable = true + present(imagePicker, animated: true) + } + + func imagePicker(didSelect image: UIImage?) { + dismiss(animated: true) { + if let selectedImage = image { + self.profileImageView.image = selectedImage + self.saveProfileImage(selectedImage) + } + } + } + + private func saveProfileImage(_ image: UIImage) { + // Implementar lógica de salvamento + print("Imagem do perfil atualizada!") + } +} +``` + +
+ +
+ImageZoomController - Visualizador de Imagens com Zoom + +### ImageZoomController + +Controlador para visualização de imagens com zoom e pan. + +#### Inicialização + +**Declaração:** + +```swift +public class ImageZoomController: UIViewController { + public init(_ withImage: UIImage) + public weak var delegate: ImageZoomControllerDelegate? + public var minimumZoomScale: CGFloat = 1.0 + public var maximumZoomScale: CGFloat = 6.0 + public var addGestureToDismiss: Bool = true +} +``` + +**Uso:** + +```swift +let image = UIImage(named: "minha_imagem")! +let zoomController = ImageZoomController(image) +zoomController.delegate = self +zoomController.minimumZoomScale = 0.5 +zoomController.maximumZoomScale = 8.0 +zoomController.addGestureToDismiss = true +``` + +#### Delegate + +**ImageZoomControllerDelegate** + +```swift +@objc public protocol ImageZoomControllerDelegate { + @objc optional func imageZoomController(controller: ImageZoomController, didZoom image: UIImage?) + @objc optional func imageZoomController(controller: ImageZoomController, didClose image: UIImage?) +} +``` + +**Uso:** + +```swift +class MyViewController: UIViewController, ImageZoomControllerDelegate { + + @IBAction func showImageZoom() { + guard let image = imageView.image else { return } + + let zoomController = ImageZoomController(image) + zoomController.delegate = self + zoomController.present() + } + + func imageZoomController(controller: ImageZoomController, didZoom image: UIImage?) { + print("Usuário fez zoom na imagem") + } + + func imageZoomController(controller: ImageZoomController, didClose image: UIImage?) { + print("Visualizador foi fechado") + } +} +``` + +#### Métodos + +**present(completion:)** + +```swift +public func present(completion: (()->())? = nil) +``` + +**Uso:** + +```swift +zoomController.present { + print("Visualizador apresentado!") +} +``` + +**dismiss(completion:)** + +```swift +public func dismiss(completion: (()->())? = nil) +``` + +**Uso:** + +```swift +zoomController.dismiss { + print("Visualizador fechado!") +} +``` + +#### Exemplo Completo + +```swift +class GalleryViewController: UIViewController, ImageZoomControllerDelegate { + + @IBOutlet weak var collectionView: UICollectionView! + var images: [UIImage] = [] + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let selectedImage = images[indexPath.item] + showImageZoom(selectedImage) + } + + private func showImageZoom(_ image: UIImage) { + let zoomController = ImageZoomController(image) + zoomController.delegate = self + zoomController.minimumZoomScale = 0.5 + zoomController.maximumZoomScale = 10.0 + zoomController.addGestureToDismiss = true + zoomController.present() + } + + func imageZoomController(controller: ImageZoomController, didZoom image: UIImage?) { + // Opcional: implementar lógica quando usuário faz zoom + } + + func imageZoomController(controller: ImageZoomController, didClose image: UIImage?) { + // Opcional: implementar lógica quando visualizador é fechado + } +} +``` + +
+ +--- + +## ⚡ SwiftUI + +
+LCENavigationView - Navegação Customizada SwiftUI + +### LCENavigationView + +Componente SwiftUI para navegação customizada com botões e títulos. + +#### Inicialização + +**Declaração:** + +```swift +@available(iOS 15, *) +public struct LCENavigationView: View { + public init(title: (any View) = Text(""), + subTitle: (any View) = Text(""), + @ViewBuilder content: () -> Content) +} +``` + +**Uso:** + +```swift +LCENavigationView { + Text("Conteúdo da tela") +} +``` + +#### Configuração + +**setTitle** + +```swift +public func setTitle(text: (any View) = Text(""), + subTitle: (any View)? = nil) -> LCENavigationView +``` + +**Uso:** + +```swift +LCENavigationView { + Text("Conteúdo") +} +.setTitle(text: Text("Meu Título"), subTitle: Text("Subtítulo")) +``` + +**setLeftButton** + +```swift +public func setLeftButton(text: Text = Text(""), + image: (any View)? = nil, + action: @escaping () -> Void) -> LCENavigationView +``` + +**Uso:** + +```swift +LCENavigationView { + Text("Conteúdo") +} +.setLeftButton(text: Text("Voltar")) { + print("Botão esquerdo pressionado") +} +.setLeftButton(image: Image(systemName: "arrow.left")) { + print("Seta esquerda pressionada") +} +``` + +**setRightButton** + +```swift +public func setRightButton(text: Text = Text(""), + image: (any View)? = nil, + action: @escaping () -> Void) -> LCENavigationView +``` + +**Uso:** + +```swift +LCENavigationView { + Text("Conteúdo") +} +.setRightButton(text: Text("Salvar")) { + print("Salvar pressionado") +} +.setRightButton(image: Image(systemName: "checkmark")) { + print("Checkmark pressionado") +} +``` + +**hideNavigationView** + +```swift +public func hideNavigationView(_ hide: Bool) -> LCENavigationView +``` + +**Uso:** + +```swift +LCENavigationView { + Text("Conteúdo") +} +.hideNavigationView(true) // Esconde a barra de navegação +``` + +#### Exemplo Completo + +```swift +struct ContentView: View { + @State private var isEditing = false + + var body: some View { + LCENavigationView { + VStack { + Text("Conteúdo da tela") + .padding() + + if isEditing { + Text("Modo de edição ativo") + .foregroundColor(.blue) + } + } + } + .setTitle(text: Text("Minha App"), subTitle: Text("Tela Principal")) + .setLeftButton(image: Image(systemName: "line.horizontal.3")) { + print("Menu pressionado") + } + .setRightButton(text: Text(isEditing ? "Concluir" : "Editar")) { + isEditing.toggle() + } + } +} +``` + +
+ +
+View+Ext - Extensões SwiftUI + +### View+Ext + +Extensões para Views SwiftUI. + +#### getTag + +**Declaração:** + +```swift +@available(iOS 13.0, *) +extension View { + func getTag() throws -> TagType + func extractTag(_ closure: (() throws -> TagType) -> Void) -> Self +} +``` + +**Uso:** + +```swift +struct MyView: View { + var body: some View { + Text("Hello") + .tag("myTag") + .extractTag { tag in + print("Tag extraída: \(tag)") + } + } +} +``` + +
+ +--- + +## 🔗 Extensões + +
+String - Extensões para String + +### String Extensions + +Extensões poderosas para manipulação de strings. + +#### Validações + +**isEmail** + +```swift +var isEmail: Bool +``` + +**Uso:** + +```swift +let email = "user@exemplo.com" +if email.isEmail { + print("Email válido!") +} +``` + +**isCPF** + +```swift +var isCPF: Bool +``` + +**Uso:** + +```swift +let cpf = "12345678901" +if cpf.isCPF { + print("CPF válido!") +} +``` + +**isValidCNPJ** + +```swift +var isValidCNPJ: Bool +``` + +**Uso:** + +```swift +let cnpj = "12345678000195" +if cnpj.isValidCNPJ { + print("CNPJ válido!") +} +``` + +#### Transformações + +**urlEncoded** + +```swift +var urlEncoded: String +``` + +**Uso:** + +```swift +let text = "Hello World!" +let encoded = text.urlEncoded +print(encoded) // "Hello%20World%21" +``` + +**urlDecoded** + +```swift +var urlDecoded: String +``` + +**Uso:** + +```swift +let encoded = "Hello%20World%21" +let decoded = encoded.urlDecoded +print(decoded) // "Hello World!" +``` + +**base64Encode** + +```swift +var base64Encode: String? +``` + +**Uso:** + +```swift +let text = "Hello World!" +if let encoded = text.base64Encode { + print("Base64: \(encoded)") +} +``` + +**base64Decode** + +```swift +var base64Decode: String? +``` + +**Uso:** + +```swift +let encoded = "SGVsbG8gV29ybGQh" +if let decoded = encoded.base64Decode { + print("Decoded: \(decoded)") +} +``` + +#### Limpeza e Filtros + +**onlyNumbers** + +```swift +var onlyNumbers: String +``` + +**Uso:** + +```swift +let phone = "+55 (11) 99999-9999" +let numbers = phone.onlyNumbers +print(numbers) // "5511999999999" +``` + +**removeSpecialChars** + +```swift +var removeSpecialChars: String +``` + +**Uso:** + +```swift +let text = "Hello@#$%World!" +let clean = text.removeSpecialChars +print(clean) // "HelloWorld" +``` + +**removeHTMLTags** + +```swift +var removeHTMLTags: String +``` + +**Uso:** + +```swift +let html = "

Hello World!

" +let text = html.removeHTMLTags +print(text) // "Hello World!" +``` + +**removeEmoji** + +```swift +var removeEmoji: String +``` + +**Uso:** + +```swift +let text = "Hello 😀 World 🌍!" +let clean = text.removeEmoji +print(clean) // "Hello World !" +``` + +#### Conversões + +**int** + +```swift +var int: Int? +``` + +**Uso:** + +```swift +let numberString = "123" +if let number = numberString.int { + print("Número: \(number)") +} +``` + +**double** + +```swift +var double: Double? +``` + +**Uso:** + +```swift +let numberString = "123.45" +if let number = numberString.double { + print("Número: \(number)") +} +``` + +**bool** + +```swift +var bool: Bool? +``` + +**Uso:** + +```swift +let boolString = "true" +if let value = boolString.bool { + print("Valor: \(value)") +} +``` + +#### Formatação + +**applyMask** + +```swift +func applyMask(toText: String, mask: String) -> String +``` + +**Uso:** + +```swift +let phone = "11999999999" +let masked = "".applyMask(toText: phone, mask: "(##) #####-####") +print(masked) // "(11) 99999-9999" +``` + +**currencyStringToDouble** + +```swift +var currencyStringToDouble: Double +``` + +**Uso:** + +```swift +let currency = "R$ 1.234,56" +let value = currency.currencyStringToDouble +print(value) // 1234.56 +``` + +#### Data e Hora + +**date** + +```swift +func date(withCurrFormatt: String = "yyyy-MM-dd HH:mm:ss", + localeIdentifier: String = "pt-BR", + timeZone: TimeZone? = TimeZone.current) -> Date? +``` + +**Uso:** + +```swift +let dateString = "2023-12-25 15:30:00" +if let date = dateString.date(withCurrFormatt: "yyyy-MM-dd HH:mm:ss") { + print("Data: \(date)") +} +``` + +#### Exemplo Completo + +```swift +class StringUtils { + + static func validateUserInput(_ input: String) -> Bool { + // Remover caracteres especiais + let cleanInput = input.removeSpecialChars + + // Verificar se não está vazio + guard !cleanInput.isEmpty else { return false } + + // Verificar se contém apenas letras e espaços + let lettersOnly = cleanInput.lettersWithWhiteSpace + return lettersOnly == cleanInput + } + + static func formatPhoneNumber(_ phone: String) -> String { + let numbers = phone.onlyNumbers + return "".applyMask(toText: numbers, mask: "(##) #####-####") + } + + static func validateEmail(_ email: String) -> Bool { + return email.isEmail + } + + static func validateCPF(_ cpf: String) -> Bool { + return cpf.isCPF + } +} +``` + +
+ +
+Date - Extensões para Date + +### Date Extensions + +Extensões completas para manipulação de datas. + +#### Propriedades + +**year, month, day, hour, minute, second** + +```swift +var year: Int { get set } +var month: Int { get set } +var day: Int { get set } +var hour: Int { get set } +var minute: Int { get set } +var second: Int { get set } +``` + +**Uso:** + +```swift +let now = Date() +print("Ano: \(now.year)") +print("Mês: \(now.month)") +print("Dia: \(now.day)") +print("Hora: \(now.hour)") +print("Minuto: \(now.minute)") +print("Segundo: \(now.second)") + +// Modificar data +var tomorrow = Date() +tomorrow.day += 1 +print("Amanhã: \(tomorrow)") +``` + +#### Verificações + +**isInToday, isInYesterday, isInTomorrow** + +```swift +var isInToday: Bool +var isInYesterday: Bool +var isInTomorrow: Bool +``` + +**Uso:** + +```swift +let date = Date() +if date.isInToday { + print("É hoje!") +} else if date.isInYesterday { + print("É ontem!") +} else if date.isInTomorrow { + print("É amanhã!") +} +``` + +**isInFuture, isInPast** + +```swift +var isInFuture: Bool +var isInPast: Bool +``` + +**Uso:** + +```swift +let futureDate = Date().adding(.day, value: 1) +if futureDate.isInFuture { + print("Data futura!") +} + +let pastDate = Date().adding(.day, value: -1) +if pastDate.isInPast { + print("Data passada!") +} +``` + +#### Operações + +**adding** + +```swift +func adding(_ component: Calendar.Component, value: Int) -> Date +``` + +**Uso:** + +```swift +let tomorrow = Date().adding(.day, value: 1) +let nextWeek = Date().adding(.weekOfYear, value: 1) +let nextMonth = Date().adding(.month, value: 1) +let nextYear = Date().adding(.year, value: 1) +``` + +**changing** + +```swift +func changing(_ component: Calendar.Component, value: Int) -> Date? +``` + +**Uso:** + +```swift +let newDate = Date().changing(.hour, value: 15) // Muda para 15:00 +let newDate2 = Date().changing(.day, value: 25) // Muda para dia 25 +``` + +#### Formatação + +**string** + +```swift +func string(withFormat format: String = "dd/MM/yyyy HH:mm") -> String +``` + +**Uso:** + +```swift +let date = Date() +print(date.string(withFormat: "dd/MM/yyyy")) // "25/12/2023" +print(date.string(withFormat: "HH:mm")) // "15:30" +print(date.string(withFormat: "EEEE, dd 'de' MMMM 'de' yyyy")) // "Segunda-feira, 25 de dezembro de 2023" +``` + +**dateString, timeString, dateTimeString** + +```swift +func dateString(ofStyle style: DateFormatter.Style = .medium) -> String +func timeString(ofStyle style: DateFormatter.Style = .medium) -> String +func dateTimeString(ofStyle style: DateFormatter.Style = .medium) -> String +``` + +**Uso:** + +```swift +let date = Date() +print(date.dateString()) // "25 de dez de 2023" +print(date.timeString()) // "15:30:00" +print(date.dateTimeString()) // "25 de dez de 2023 às 15:30:00" +``` + +#### Nomes + +**dayName, monthName** + +```swift +func dayName(ofStyle style: DayNameStyle = .full) -> String +func monthName(ofStyle style: MonthNameStyle = .full) -> String +``` + +**Uso:** + +```swift +let date = Date() +print(date.dayName()) // "Segunda-feira" +print(date.dayName(ofStyle: .threeLetters)) // "Seg" +print(date.monthName()) // "Dezembro" +print(date.monthName(ofStyle: .threeLetters)) // "Dez" +``` + +#### Comparações + +**secondsSince, minutesSince, hoursSince, daysSince** + +```swift +func secondsSince(_ date: Date) -> Double +func minutesSince(_ date: Date) -> Double +func hoursSince(_ date: Date) -> Double +func daysSince(_ date: Date) -> Double +``` + +**Uso:** + +```swift +let startDate = Date() +// ... alguma operação ... +let endDate = Date() + +let seconds = endDate.secondsSince(startDate) +let minutes = endDate.minutesSince(startDate) +let hours = endDate.hoursSince(startDate) +let days = endDate.daysSince(startDate) + +print("Passaram \(seconds) segundos") +print("Passaram \(minutes) minutos") +print("Passaram \(hours) horas") +print("Passaram \(days) dias") +``` + +**isBetween** + +```swift +func isBetween(_ startDate: Date, _ endDate: Date, includeBounds: Bool = false) -> Bool +``` + +**Uso:** + +```swift +let startDate = Date().adding(.day, value: -7) +let endDate = Date().adding(.day, value: 7) +let checkDate = Date() + +if checkDate.isBetween(startDate, endDate) { + print("Data está no intervalo!") +} +``` + +#### Exemplo Completo + +```swift +class DateUtils { + + static func formatRelativeDate(_ date: Date) -> String { + let now = Date() + + if date.isInToday { + return "Hoje às \(date.string(withFormat: "HH:mm"))" + } else if date.isInYesterday { + return "Ontem às \(date.string(withFormat: "HH:mm"))" + } else if date.isInTomorrow { + return "Amanhã às \(date.string(withFormat: "HH:mm"))" + } else if date.isInCurrentWeek { + return date.dayName(ofStyle: .full) + } else { + return date.string(withFormat: "dd/MM/yyyy") + } + } + + static func getAge(from birthDate: Date) -> Int { + let now = Date() + let age = now.year - birthDate.year + return age + } + + static func isWeekend(_ date: Date) -> Bool { + return date.isInWeekend + } + + static func getBusinessDaysBetween(_ startDate: Date, and endDate: Date) -> Int { + var currentDate = startDate + var businessDays = 0 + + while currentDate <= endDate { + if !currentDate.isInWeekend { + businessDays += 1 + } + currentDate = currentDate.adding(.day, value: 1) + } + + return businessDays + } +} +``` + +
+ +
+UIView - Extensões para UIView + +### UIView Extensions + +Extensões poderosas para manipulação de views. + +#### Propriedades + +**borderColor, borderWidth, cornerRadius** + +```swift +var borderColor: UIColor? { get set } +var borderWidth: CGFloat { get set } +var cornerRadius: CGFloat { get set } +``` + +**Uso:** + +```swift +let view = UIView() +view.borderColor = .systemBlue +view.borderWidth = 2.0 +view.cornerRadius = 8.0 +``` + +**screenshot** + +```swift +var screenshot: UIImage? +``` + +**Uso:** + +```swift +if let screenshot = view.screenshot { + // Usar screenshot da view + imageView.image = screenshot +} +``` + +#### Constraint Helpers + +**setConstraintsTo** + +```swift +@discardableResult +func setConstraintsTo(parentView: UIView, anchorType: AnchorType, value: CGFloat, safeArea: Bool = false) -> Self +``` + +**Uso:** + +```swift +let childView = UIView() +parentView.addSubview(childView) + +// Configurar constraints +childView.setConstraintsTo(parentView, .top, 20, true) // 20pt do topo da safe area + .setConstraints(.leading, 16) + .setConstraints(.trailing, -16) + .setConstraints(.bottom, -20) +``` + +**setHeight, setWidth** + +```swift +@discardableResult +func setHeight(size: CGFloat) -> Self +@discardableResult +func setWidth(size: CGFloat) -> Self +``` + +**Uso:** + +```swift +view.setHeight(size: 100) + .setWidth(size: 200) +``` + +#### Efeitos Visuais + +**applyShadow** + +```swift +func applyShadow(color: UIColor, offSet: CGSize, radius: CGFloat, opacity: Float, shouldRasterize: Bool = true, rasterizationScaleTo: CGFloat = UIScreen.main.scale) +``` + +**Uso:** + +```swift +view.applyShadow(color: .black, offSet: CGSize(width: 0, height: 2), radius: 4, opacity: 0.3) +``` + +**insertBlurView** + +```swift +func insertBlurView(style: UIBlurEffect.Style, color: UIColor = .black, alpha: CGFloat = 0.9) +``` + +**Uso:** + +```swift +view.insertBlurView(style: .dark, color: .black, alpha: 0.8) +``` + +#### Animações + +**fadeIn, fadeOut** + +```swift +func fadeIn(withDuration duration: TimeInterval = 1.0, withDelay delay: TimeInterval = 0, completionHandler: @escaping (Bool) -> ()) +func fadeOut(withDuration duration: TimeInterval = 1.0, withDelay delay: TimeInterval = 0, completionHandler: @escaping (Bool) -> ()) +``` + +**Uso:** + +```swift +view.fadeIn(withDuration: 0.5) { finished in + print("Fade in concluído!") +} + +view.fadeOut(withDuration: 0.3) { finished in + print("Fade out concluído!") +} +``` + +#### Utilitários + +**addSubviews** + +```swift +func addSubviews(_ subviews: [UIView], translatesAutoresizingMaskIntoConstraints: Bool = false) +``` + +**Uso:** + +```swift +let views = [view1, view2, view3] +parentView.addSubviews(views) +``` + +**subviews(ofType:)** + +```swift +func subviews(ofType _: T.Type) -> [T] +``` + +**Uso:** + +```swift +let buttons = view.subviews(ofType: UIButton.self) +let labels = view.subviews(ofType: UILabel.self) +``` + +#### Exemplo Completo + +```swift +class CustomCardView: UIView { + + override init(frame: CGRect) { + super.init(frame: frame) + setupCard() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupCard() + } + + private func setupCard() { + // Configurar aparência + backgroundColor = .systemBackground + cornerRadius = 12 + borderWidth = 1 + borderColor = .systemGray4 + + // Aplicar sombra + applyShadow(color: .black, offSet: CGSize(width: 0, height: 2), radius: 8, opacity: 0.1) + + // Configurar constraints se necessário + setHeight(size: 200) + } + + func showWithAnimation() { + alpha = 0 + fadeIn(withDuration: 0.3) { finished in + print("Card apareceu!") + } + } + + func hideWithAnimation() { + fadeOut(withDuration: 0.3) { finished in + self.removeFromSuperview() + } + } +} +``` + +
+ +
+UIImage - Extensões para UIImage + +### UIImage Extensions + +Extensões para manipulação de imagens. + +#### Criação + +**init(base64String:scale:)** + +```swift +convenience init?(base64String: String, scale: CGFloat = 1.0) +``` + +**Uso:** + +```swift +let base64String = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" +if let image = UIImage(base64String: base64String) { + imageView.image = image +} +``` + +**init(view:)** + +```swift +@MainActor +convenience init(view: UIView) +``` + +**Uso:** + +```swift +let image = UIImage(view: myView) +``` + +#### Transformações + +**tintImage** + +```swift +func tintImage(color: UIColor) -> UIImage +``` + +**Uso:** + +```swift +let originalImage = UIImage(systemName: "heart.fill") +let tintedImage = originalImage?.tintImage(color: .red) +``` + +**resizeImage** + +```swift +func resizeImage(newWidth: CGFloat) -> UIImage +``` + +**Uso:** + +```swift +let resizedImage = originalImage.resizeImage(newWidth: 200) +``` + +**createThumbnail** + +```swift +func createThumbnail(_ maxPixelSize: UInt) -> UIImage +``` + +**Uso:** + +```swift +let thumbnail = originalImage.createThumbnail(150) +``` + +#### Utilitários + +**isAnimated** + +```swift +func isAnimated() -> Bool +``` + +**Uso:** + +```swift +if image.isAnimated() { + print("É uma imagem animada (GIF)") +} +``` + +**imageWithColor** + +```swift +func imageWithColor(color: UIColor) -> UIImage +``` + +**Uso:** + +```swift +let coloredImage = UIImage().imageWithColor(color: .systemBlue) +``` + +#### Exemplo Completo + +```swift +class ImageProcessor { + + static func processImage(_ image: UIImage) -> UIImage { + // Redimensionar se muito grande + var processedImage = image + if image.size.width > 1000 { + processedImage = image.resizeImage(newWidth: 1000) + } + + // Criar thumbnail + let thumbnail = processedImage.createThumbnail(200) + + return thumbnail + } + + static func createColoredIcon(systemName: String, color: UIColor, size: CGSize) -> UIImage? { + let config = UIImage.SymbolConfiguration(pointSize: size.width, weight: .medium) + let image = UIImage(systemName: systemName, withConfiguration: config) + return image?.tintImage(color: color) + } + + static func convertViewToImage(_ view: UIView) -> UIImage { + return UIImage(view: view) + } +} +``` + +
+ +
+UIColor - Extensões para UIColor + +### UIColor Extensions + +Extensões para manipulação de cores. + +#### Inicialização + +**init(hex:)** + +```swift +convenience init(hex: String) +``` + +**Uso:** + +```swift +let color1 = UIColor(hex: "#FF0000") // Vermelho +let color2 = UIColor(hex: "#00FF00") // Verde +let color3 = UIColor(hex: "#0000FF") // Azul +let color4 = UIColor(hex: "#FF0000FF") // Vermelho com alpha +``` + +#### Propriedades + +**hexString** + +```swift +var hexString: String? +``` + +**Uso:** + +```swift +let color = UIColor.red +if let hex = color.hexString { + print("Hex: \(hex)") // "#FF0000" +} +``` + +**redValue, greenValue, blueValue, alphaValue** + +```swift +var redValue: CGFloat +var greenValue: CGFloat +var blueValue: CGFloat +var alphaValue: CGFloat +``` + +**Uso:** + +```swift +let color = UIColor(red: 0.5, green: 0.3, blue: 0.8, alpha: 1.0) +print("Red: \(color.redValue)") +print("Green: \(color.greenValue)") +print("Blue: \(color.blueValue)") +print("Alpha: \(color.alphaValue)") +``` + +#### Exemplo Completo + +```swift +class ColorUtils { + + static func randomColor() -> UIColor { + let red = CGFloat.random(in: 0...1) + let green = CGFloat.random(in: 0...1) + let blue = CGFloat.random(in: 0...1) + return UIColor(red: red, green: green, blue: blue, alpha: 1.0) + } + + static func hexToColor(_ hex: String) -> UIColor? { + return UIColor(hex: hex) + } + + static func colorToHex(_ color: UIColor) -> String? { + return color.hexString + } + + static func createGradientColors(from startColor: UIColor, to endColor: UIColor, steps: Int) -> [UIColor] { + var colors: [UIColor] = [] + + for i in 0.. + +
+Array - Extensões para Array + +### Array Extensions + +Extensões para manipulação de arrays. + +#### Remoção de Duplicatas + +**unique** + +```swift +var unique: [Element] +``` + +**Uso:** + +```swift +let numbers = [1, 2, 2, 3, 3, 3, 4] +let uniqueNumbers = numbers.unique +print(uniqueNumbers) // [1, 2, 3, 4] +``` + +**removeDuplicates** + +```swift +@discardableResult +mutating func removeDuplicates() -> [Element] +``` + +**Uso:** + +```swift +var numbers = [1, 2, 2, 3, 3, 3, 4] +numbers.removeDuplicates() +print(numbers) // [1, 2, 3, 4] +``` + +**withoutDuplicates** + +```swift +func withoutDuplicates() -> [Element] +``` + +**Uso:** + +```swift +let numbers = [1, 2, 2, 3, 3, 3, 4] +let uniqueNumbers = numbers.withoutDuplicates() +print(uniqueNumbers) // [1, 2, 3, 4] +``` + +#### Remoção de Elementos + +**removeAll** + +```swift +@discardableResult +mutating func removeAll(_ item: Element) -> [Element] +@discardableResult +mutating func removeAll(_ items: [Element]) -> [Element] +``` + +**Uso:** + +```swift +var numbers = [1, 2, 2, 3, 4, 5] +numbers.removeAll(2) +print(numbers) // [1, 3, 4, 5] + +var letters = ["a", "b", "c", "a", "b"] +letters.removeAll(["a", "b"]) +print(letters) // ["c"] +``` + +#### Inserção + +**prepend** + +```swift +mutating func prepend(_ newElement: Element) +``` + +**Uso:** + +```swift +var numbers = [2, 3, 4] +numbers.prepend(1) +print(numbers) // [1, 2, 3, 4] +``` + +#### Troca + +**safeSwap** + +```swift +mutating func safeSwap(from index: Index, to otherIndex: Index) +``` + +**Uso:** + +```swift +var numbers = [1, 2, 3, 4, 5] +numbers.safeSwap(from: 0, to: 4) +print(numbers) // [5, 2, 3, 4, 1] +``` + +#### Exemplo Completo + +```swift +class ArrayUtils { + + static func removeDuplicates(from array: [T]) -> [T] { + return array.unique + } + + static func shuffle(_ array: [T]) -> [T] { + var shuffled = array + for i in 0..(_ array: [T], size: Int) -> [[T]] { + var chunks: [[T]] = [] + for i in stride(from: 0, to: array.count, by: size) { + let chunk = Array(array[i.. + +
+Data - Extensões para Data + +### Data Extensions + +Extensões para manipulação de dados. + +#### Conversões + +**toHexString** + +```swift +var toHexString: String +``` + +**Uso:** + +```swift +let data = "Hello".data(using: .utf8)! +let hex = data.toHexString +print(hex) // "48656c6c6f" +``` + +**init(hexString:)** + +```swift +init?(hexString: String) +``` + +**Uso:** + +```swift +if let data = Data(hexString: "48656c6c6f") { + let string = String(data: data, encoding: .utf8) + print(string) // "Hello" +} +``` + +**bool** + +```swift +var bool: Bool +``` + +**Uso:** + +```swift +let trueData = Data([1]) +let falseData = Data([0]) +print(trueData.bool) // true +print(falseData.bool) // false +``` + +#### Hash e Criptografia + +**SHA256** + +```swift +func SHA256() -> Data +``` + +**Uso:** + +```swift +let data = "Hello World".data(using: .utf8)! +let hash = data.SHA256() +print(hash.toHexString) +``` + +**SHA512** + +```swift +func SHA512() -> Data +``` + +**Uso:** + +```swift +let data = "Hello World".data(using: .utf8)! +let hash = data.SHA512() +print(hash.toHexString) +``` + +**HMACSHA512** + +```swift +@available(iOS 13.0, *) +func HMACSHA512(key: Data) -> Data +``` + +**Uso:** + +```swift +let data = "Hello World".data(using: .utf8)! +let key = "secret".data(using: .utf8)! +let hmac = data.HMACSHA512(key: key) +print(hmac.toHexString) +``` + +**XOR** + +```swift +func XOR(with other: Data) -> Data +``` + +**Uso:** + +```swift +let data1 = "Hello".data(using: .utf8)! +let data2 = "World".data(using: .utf8)! +let xor = data1.XOR(with: data2) +``` + +#### JSON + +**prettyJson** + +```swift +var prettyJson: String? +``` + +**Uso:** + +```swift +let jsonData = """ +{"name": "John", "age": 30, "city": "New York"} +""".data(using: .utf8)! + +if let pretty = jsonData.prettyJson { + print(pretty) + // { + // "name" : "John", + // "age" : 30, + // "city" : "New York" + // } +} +``` + +**toDictionay** + +```swift +var toDictionay: Dictionary? +``` + +**Uso:** + +```swift +let jsonData = """ +{"name": "John", "age": 30} +""".data(using: .utf8)! + +if let dict = jsonData.toDictionay { + print(dict["name"]) // "John" + print(dict["age"]) // 30 +} +``` + +**object** + +```swift +func object() -> T? +``` + +**Uso:** + +```swift +struct User: Codable { + let name: String + let age: Int +} + +let jsonData = """ +{"name": "John", "age": 30} +""".data(using: .utf8)! + +if let user: User = jsonData.object() { + print("Nome: \(user.name), Idade: \(user.age)") +} +``` + +#### Exemplo Completo + +```swift +class DataUtils { + + static func hashString(_ string: String) -> String { + let data = string.data(using: .utf8)! + let hash = data.SHA256() + return hash.toHexString + } + + static func createHMAC(message: String, key: String) -> String { + let messageData = message.data(using: .utf8)! + let keyData = key.data(using: .utf8)! + let hmac = messageData.HMACSHA512(key: keyData) + return hmac.toHexString + } + + static func encryptData(_ data: Data, with key: Data) -> Data { + return data.XOR(with: key) + } + + static func decryptData(_ encryptedData: Data, with key: Data) -> Data { + return encryptedData.XOR(with: key) + } +} +``` + +
+ +
+UIButton - Extensões para UIButton + +### UIButton Extensions + +Extensões para manipulação de botões. + +#### Propriedades por Estado + +**imageForNormal, imageForHighlighted, imageForSelected, imageForDisabled** + +```swift +var imageForNormal: UIImage? { get set } +var imageForHighlighted: UIImage? { get set } +var imageForSelected: UIImage? { get set } +var imageForDisabled: UIImage? { get set } +``` + +**Uso:** + +```swift +let button = UIButton() +button.imageForNormal = UIImage(systemName: "heart") +button.imageForHighlighted = UIImage(systemName: "heart.fill") +button.imageForSelected = UIImage(systemName: "heart.fill") +``` + +**titleForNormal, titleForHighlighted, titleForSelected, titleForDisabled** + +```swift +var titleForNormal: String? { get set } +var titleForHighlighted: String? { get set } +var titleForSelected: String? { get set } +var titleForDisabled: String? { get set } +``` + +**Uso:** + +```swift +button.titleForNormal = "Normal" +button.titleForHighlighted = "Highlighted" +button.titleForSelected = "Selected" +button.titleForDisabled = "Disabled" +``` + +**titleColorForNormal, titleColorForHighlighted, titleColorForSelected, titleColorForDisabled** + +```swift +var titleColorForNormal: UIColor? { get set } +var titleColorForHighlighted: UIColor? { get set } +var titleColorForSelected: UIColor? { get set } +var titleColorForDisabled: UIColor? { get set } +``` + +**Uso:** + +```swift +button.titleColorForNormal = .systemBlue +button.titleColorForHighlighted = .systemRed +button.titleColorForSelected = .systemGreen +button.titleColorForDisabled = .systemGray +``` + +#### Métodos + +**setImageForAllStates** + +```swift +func setImageForAllStates(_ image: UIImage) +``` + +**Uso:** + +```swift +let image = UIImage(systemName: "star.fill") +button.setImageForAllStates(image) +``` + +**setTitleForAllStates** + +```swift +func setTitleForAllStates(_ title: String) +``` + +**Uso:** + +```swift +button.setTitleForAllStates("Botão") +``` + +**setTitleColorForAllStates** + +```swift +func setTitleColorForAllStates(_ color: UIColor) +``` + +**Uso:** + +```swift +button.setTitleColorForAllStates(.white) +``` + +**centerTextAndImage** + +```swift +func centerTextAndImage(spacing: CGFloat) +``` + +**Uso:** + +```swift +button.centerTextAndImage(spacing: 8) +``` + +#### Exemplo Completo + +```swift +class CustomButton: UIButton { + + override init(frame: CGRect) { + super.init(frame: frame) + setupButton() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupButton() + } + + private func setupButton() { + // Configurar imagem para todos os estados + let image = UIImage(systemName: "heart") + setImageForAllStates(image) + + // Configurar título + setTitleForAllStates("Curtir") + + // Configurar cores + titleColorForNormal = .systemBlue + titleColorForHighlighted = .systemRed + titleColorForSelected = .systemGreen + + // Centralizar texto e imagem + centerTextAndImage(spacing: 8) + + // Configurar aparência + backgroundColor = .systemBackground + layer.cornerRadius = 8 + layer.borderWidth = 1 + layer.borderColor = UIColor.systemBlue.cgColor + } + + func setLiked(_ isLiked: Bool) { + isSelected = isLiked + titleForSelected = isLiked ? "Curtido" : "Curtir" + } +} +``` + +
+ +--- + +## 📱 Helpers + +
+Funções de Log + +### Funções de Log + +Sistema completo de logging com diferentes níveis. + +#### printLog +```swift +public func printLog(title: String, msg: Any, prettyPrint: Bool = false) +``` + +**Uso:** + +```swift +printLog(title: "DEBUG", msg: "Mensagem de debug") +printLog(title: "USER", msg: userData, prettyPrint: true) +``` + +#### printInfo +```swift +public func printInfo(title: String, msg: Any, prettyPrint: Bool = false, function: String = #function, file: String = #file, line: Int = #line, column: Int = #column) +``` + +**Uso:** + +```swift +printInfo(title: "API", msg: "Requisição realizada com sucesso") +``` + +#### printWarn +```swift +public func printWarn(title: String, msg: Any, prettyPrint: Bool = false, function: String = #function, file: String = #file, line: Int = #line, column: Int = #column) +``` + +**Uso:** + +```swift +printWarn(title: "DEPRECATED", msg: "Este método será removido na próxima versão") +``` + +#### printError +```swift +public func printError(title: String, msg: Any, prettyPrint: Bool = false, function: String = #function, file: String = #file, line: Int = #line, column: Int = #column) +``` + +**Uso:** + +```swift +printError(title: "NETWORK", msg: "Falha na conexão com o servidor") +``` + +#### Exemplo Completo + +```swift +class NetworkManager { + + func fetchData() { + printInfo(title: "NETWORK", msg: "Iniciando requisição") + + // Simular requisição + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + let success = Bool.random() + + if success { + printLog(title: "SUCCESS", msg: "Dados carregados com sucesso") + } else { + printError(title: "ERROR", msg: "Falha ao carregar dados") + } + } + } +} +``` + +
+ +
+Operador de Potência + +### Operador de Potência + +Operador customizado para cálculos de potência. + +#### Declaração +```swift +precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } +infix operator ^^ : PowerPrecedence +public func ^^ (radix: Float, power: Float) -> Float +``` + +#### Uso +```swift +let result1 = 2.0 ^^ 3.0 // 8.0 (2³) +let result2 = 5.0 ^^ 2.0 // 25.0 (5²) +let result3 = 10.0 ^^ 0.5 // 3.16... (√10) + +print("2³ = \(result1)") +print("5² = \(result2)") +print("√10 = \(result3)") +``` + +
+ +--- + +## 🔐 Criptografia + +
+LCECryptoKitManager - Gerenciador de Criptografia + +### LCECryptoKitManager + +Sistema completo de criptografia usando LCECryptoKit. + +#### Inicialização + +```swift +// Sem chave privada +let cryptoManager = LCECryptoKitManager() + +// Com chave privada +let cryptoManager = LCECryptoKitManager(privateKey: "minha_chave_privada") +``` + +#### Métodos + +**generateKey** + +```swift +public static func generateKey() -> String +``` + +**Uso:** + +```swift +let newKey = LCECryptoKitManager.generateKey() +print("Nova chave: \(newKey)") +``` + +**encodeTP** + +```swift +public func encodeTP(email: String, password: String) -> String? +``` + +**Uso:** + +```swift +if let encoded = cryptoManager.encodeTP(email: "user@exemplo.com", password: "senha123") { + print("Dados codificados: \(encoded)") +} +``` + +**decodeOTP** + +```swift +public func decodeOTP(_ otpHash: String) -> String? +``` + +**Uso:** + +```swift +if let decoded = cryptoManager.decodeOTP("hash_codificado") { + print("Dados decodificados: \(decoded)") +} +``` + +**encodeOTPWithKey** + +```swift +public func encodeOTPWithKey(email: String, password: String) -> String? +``` + +**Uso:** + +```swift +if let encoded = cryptoManager.encodeOTPWithKey(email: "user@exemplo.com", password: "senha123") { + print("Dados codificados com chave: \(encoded)") +} +``` + +**decodeOTPWithKey** + +```swift +public func decodeOTPWithKey(_ otpHash: String) -> Bool +``` + +**Uso:** + +```swift +let isValid = cryptoManager.decodeOTPWithKey("hash_codificado") +if isValid { + print("Hash válido!") +} else { + print("Hash inválido!") +} +``` + +#### Exemplo Completo + +```swift +class CryptoService { + private let cryptoManager: LCECryptoKitManager + + init(privateKey: String? = nil) { + if let key = privateKey { + cryptoManager = LCECryptoKitManager(privateKey: key) + } else { + cryptoManager = LCECryptoKitManager() + } + } + + func encryptUserData(email: String, password: String) -> String? { + return cryptoManager.encodeTP(email: email, password: password) + } + + func decryptUserData(_ hash: String) -> String? { + return cryptoManager.decodeOTP(hash) + } + + func validateHash(_ hash: String) -> Bool { + return cryptoManager.decodeOTPWithKey(hash) + } + + static func generateNewKey() -> String { + return LCECryptoKitManager.generateKey() + } +} +``` + +
+ +--- + +## 🌐 API & Networking + +
+API - Sistema de Requisições HTTP + +### API + +Sistema completo para requisições HTTP com suporte a certificados, cache e retry automático. + +#### Configuração + +```swift +// Configurar parâmetros padrão +API.defaultParams = ["api_key": "sua_chave_aqui"] + +// Configurar certificado cliente +let certData = Data(contentsOf: Bundle.main.url(forResource: "client", withExtension: "p12")!) +API.shared.setupCertificationRequest(certData: certData, password: "senha_do_certificado") +``` + +#### Método Principal + +**request** + +```swift +public func request(url: String, + params: Any? = nil, + method: httpMethod, + headers: [String: String] = [:], + jsonEncoding: Bool = true, + debug: Bool = true, + timeoutInterval: TimeInterval = 30, + networkServiceType: URLRequest.NetworkServiceType = .default, + persistConnection: Bool = false) async throws -> T +``` + +#### Exemplos de Uso + +**GET Request:** + +```swift +struct User: Codable { + let id: Int + let name: String + let email: String +} + +do { + let users: [User] = try await API.shared.request( + url: "https://api.exemplo.com/users", + method: .get, + headers: ["Authorization": "Bearer token_aqui"] + ) + print("Usuários: \(users)") +} catch { + print("Erro: \(error)") +} +``` + +**POST Request:** + +```swift +struct LoginRequest: Codable { + let email: String + let password: String +} + +struct LoginResponse: Codable { + let token: String + let user: User +} + +do { + let loginData = LoginRequest(email: "user@exemplo.com", password: "senha123") + let response: LoginResponse = try await API.shared.request( + url: "https://api.exemplo.com/login", + params: loginData, + method: .post + ) + print("Token: \(response.token)") +} catch { + print("Erro no login: \(error)") +} +``` + +**Upload de Arquivo:** + +```swift +let fileURL = URL(fileURLWithPath: "/path/to/image.jpg") +let params = [ + "title": "Minha Imagem", + "description": "Descrição da imagem", + "file": fileURL.absoluteString +] + +do { + let response: [String: Any] = try await API.shared.request( + url: "https://api.exemplo.com/upload", + params: params, + method: .post + ) + print("Upload realizado: \(response)") +} catch { + print("Erro no upload: \(error)") +} +``` + +**Com Retry Automático:** + +```swift +do { + let data: MyResponse = try await API.shared.request( + url: "https://api.exemplo.com/data", + method: .get, + persistConnection: true // Habilita retry automático + ) +} catch { + print("Erro após tentativas: \(error)") +} +``` + +#### Exemplo Completo + +```swift +class APIService { + + func fetchUsers() async throws -> [User] { + return try await API.shared.request( + url: "https://api.exemplo.com/users", + method: .get, + headers: ["Authorization": "Bearer \(getToken())"] + ) + } + + func createUser(_ user: User) async throws -> User { + return try await API.shared.request( + url: "https://api.exemplo.com/users", + params: user, + method: .post + ) + } + + func uploadImage(_ imageData: Data, filename: String) async throws -> [String: Any] { + let params = [ + "filename": filename, + "data": imageData.base64EncodedString() + ] + + return try await API.shared.request( + url: "https://api.exemplo.com/upload", + params: params, + method: .post + ) + } + + private func getToken() -> String { + // Implementar lógica de obtenção de token + return "seu_token_aqui" + } +} +``` + +
+ +--- + +## 📝 Conclusão + +O **LCEssentials** é uma biblioteca completa e poderosa que oferece: + +- ✅ **50+ Extensões** para tipos fundamentais do Swift/UIKit +- ✅ **Componentes UI** prontos para uso (SnackBar, ImagePicker, ImageZoom) +- ✅ **Suporte SwiftUI** com componentes customizados +- ✅ **Sistema de API** completo com retry automático e certificados +- ✅ **Criptografia** integrada com LCECryptoKit +- ✅ **Helpers** e utilitários para desenvolvimento iOS +- ✅ **Logging** avançado com diferentes níveis +- ✅ **Cache** inteligente para downloads +- ✅ **Validações** para CPF, CNPJ, Email, etc. + +### 🚀 Começando + +1. **Instale** via Swift Package Manager +2. **Importe** `import LCEssentials` +3. **Use** as extensões e componentes conforme necessário + +### 📚 Recursos Adicionais + +- **Documentação completa** com exemplos práticos +- **Suporte** para iOS 13+, macOS 10.15+, tvOS 13+, watchOS 6+ +- **Código aberto** e mantido pela Loverde Co. +- **Atualizações** regulares com novas funcionalidades + +--- + +**Desenvolvido com ❤️ pela [Loverde Co.](https://loverde.com.br)** + +*Para dúvidas ou sugestões, entre em contato: daniel@loverde.com.br* diff --git a/README.md b/README.md index 47163e5..d3c51f6 100644 --- a/README.md +++ b/README.md @@ -28,36 +28,7 @@ http://git.loverde.com.br:3000/git/LCEssentials.git ``` ## Usage example - -* Background Trhead - -```swift -LCEssentials.backgroundThread(delay: 0.6, background: { - //Do something im background - }) { - //When finish, update UI - } -``` -* NavigationController with Completion Handler - -```swift -self.navigationController?.popViewControllerWithHandler(completion: { - //Do some stuff after pop - }) - -//or more simple -self.navigationController?.popViewControllerWithHandler { - //Do some stuff after pop -} -``` -## Another components -> LCESnackBarView - **great way to send feedback to user** - -And then import `LCEssentials ` wherever you import UIKit or SwiftUI - -``` swift -import LCEssentials -``` +[Read full documentation](LCEssentials_DOCUMENTATION.md) Author: ----