Shared Tools
Common tools, view modifiers, extensions, components, and public functions in EasyAppSwiftUI
Shared Tools
EasyAppSwiftUI provides a rich collection of utility tools, including view modifiers, extension methods, and utility functions to help you quickly build feature-complete SwiftUI applications.
We continuously update our utility toolkit to help you rapidly build fully-featured SwiftUI applications.
Overview
- 12 Custom View Modifiers - For authentication, paywalls, loading states, etc.
- Extension Methods - Enhance SwiftUI views and basic type functionality
- Utility Functions - Validation, haptic feedback, app navigation, and other practical features
- Cross-Version Compatibility - Support for compatibility across different iOS versions
View Modifiers
1. Authentication Modifier
Automatically manages user authentication state, displaying different content based on login status.
ContentView()
.modifier(AuthViewModifier())
// Or use extension method
ContentView()
.showAuthView()Key Features:
- Automatically checks user login status
- Shows login/registration interface for unauthenticated users
- Shows main app interface for authenticated users
- Smooth state transition animations
2. Paywall Modifier
Locks content behind a paywall and manages subscription verification.
// Basic usage
PremiumContentView()
.modifier(RequirePremiumViewModifier())
// Advanced usage
PremiumContentView()
.requirePremium(
subscriptionType: .revenueCat,
sheetOrFullScreenCover: .sheet,
onCancelPaywall: {
print("User cancelled payment")
},
onPaySuccess: {
print("Payment successful")
}
)Core Implementation:
struct RequirePremiumViewModifier: ViewModifier {
@EnvironmentObject private var authManager: AuthManager
@State private var showPaywall = false
let subscriptionType: SubscriptionType
let sheetOrFullScreenCover: SheetOrFullScreenCover
let onCancelPaywall: () -> Void
let onPaySuccess: () -> Void
func body(content: Content) -> some View {
content
.onAppear {
showPaywall = !authManager.subscriptionActive
}
.sheet(isPresented: sheetOrFullScreenCover == .sheet ? $showPaywall : .constant(false)) {
paywallContent()
}
}
}3. Loading State Modifier
Displays full-screen loading indicator with optional message overlay.
@State private var isLoading = false
ContentView()
.modifier(FullScreenToastViewModifier(
isPresenting: $isLoading,
message: "Processing your request...",
backgroundColor: Color.black.opacity(0.7)
))
// Or use extension method
ContentView()
.fullScreenLoading(isPresenting: $isLoading)4. Shimmer Effect Modifier
Adds animated shimmer effect to views, commonly used for loading states.
LoadingView()
.modifier(ShimmerViewModifier(duration: 2.0, autoreverse: true))
// Or use extension method
LoadingView()
.shimmering(enable: true, duration: 1.5)5. Keyboard Management Modifier
Automatically dismisses keyboard when user drags.
FormView()
.modifier(ResignKeyboardOnDragGesture())
// Or use extension method
FormView()
.dismissKeyboard()Extension Methods
Color Extensions
Support for creating colors from hexadecimal strings.
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
}Usage Examples:
let primaryColor = Color(hex: "007AFF") // 6-digit RGB
let redColor = Color(hex: "F00") // 3-digit RGB
let transparentBlue = Color(hex: "80007AFF") // 8-digit ARGBString Extensions
Provides string content validation functionality.
extension String {
var containsLetter: Bool {
let letterRegex = ".*[A-Za-z]+.*"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", letterRegex)
return passwordTest.evaluate(with: self)
}
var containsNumber: Bool {
let numberRegex = ".*[0-9]+.*"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", numberRegex)
return passwordTest.evaluate(with: self)
}
}Usage Examples:
let password = "password123"
let hasLetters = password.containsLetter // true
let hasNumbers = password.containsNumber // trueDate Extensions
Provides user-friendly date formatting.
extension Date {
var friendlyLocalFormat: String {
return self.formatted(date: .abbreviated, time: .omitted)
}
}Usage Examples:
let now = Date()
let formattedDate = now.friendlyLocalFormat // "Jan 15, 2024"View Extensions
Provides rich view enhancement functionality.
Loading States
// Button loading state
Button("Submit") { }
.loading($isLoading)
// Toast notifications
someView.showToast($showToast, message: "Operation successful", isSuccess: true)
// Full-screen loading
someView.fullScreenLoading(isPresenting: $isLoading, message: "Loading...")Styling and Appearance
// Rounded corners
someView.roundedCorners(radius: 12)
someView.cardRoundedCorners() // Predefined card corners
someView.circle() // Circular clipping
// Shadow effects
someView.shadowCompat()
someView.colorSchemeShadow(
light: Color.gray.opacity(0.3),
dark: Color.white.opacity(0.1),
radius: 4
)
// Background handling
someView.viewBackground() // Adaptive system backgroundInteraction Enhancements
// Enhanced tap gesture
CustomView()
.tappable {
// Tap action
}
// Web view presentation
someView.webViewSheet(
isPresented: $showWebView,
url: URL(string: "https://example.com"),
title: "Web Page Title"
)Utility Functions
Haptic Feedback Engine
Provides simple haptic feedback functionality.
enum HapticsEngine {
static func impact(style: UIImpactFeedbackGenerator.FeedbackStyle = .light) {
UIImpactFeedbackGenerator(style: style).impactOccurred()
}
}Usage Examples:
// Light vibration
HapticsEngine.impact(style: .light)
// Medium vibration
HapticsEngine.impact(style: .medium)
// Heavy vibration
HapticsEngine.impact(style: .heavy)Validation Tools
Provides common input validation functionality.
enum Validate {
// Email validation
static func validateEmail(_ email: String) -> Bool {
let emailPattern = #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"#
let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailPattern)
return emailPredicate.evaluate(with: email)
}
// Password length validation
static func validatePasswordLength(_ password: String) -> Bool {
return password.count >= 6 && password.count <= 12 && !password.isEmpty
}
// Password letter validation
static func validatePasswordLetter(_ password: String) -> Bool {
return password.containsLetter && !password.isEmpty
}
// Password number validation
static func validatePasswordNumber(_ password: String) -> Bool {
return password.containsNumber && !password.isEmpty
}
// Complete password validation
static func validatePassword(_ password: String) -> Bool {
return validatePasswordLength(password) &&
validatePasswordLetter(password) &&
validatePasswordNumber(password)
}
}Usage Examples:
let email = "user@example.com"
let password = "password123"
let isValidEmail = Validate.validateEmail(email) // true
let isValidPassword = Validate.validatePassword(password) // trueApp Navigation Tools
Provides in-app and external URL navigation functionality.
enum OpenApp {
// Open URL in external browser
static func openUrlOutsideApp(_ url: String) {
guard let url = URL(string: url) else { return }
UIApplication.shared.open(url)
}
// Open URL within app
static func openUrlInApp(_ url: String) {
guard let url = URL(string: url) else { return }
let safariViewController = SFSafariViewController(url: url)
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController {
rootViewController.present(safariViewController, animated: true)
}
}
}Usage Examples:
// Open in external browser
OpenApp.openUrlOutsideApp("https://www.apple.com")
// Open within app
OpenApp.openUrlInApp("https://developer.apple.com")Compatibility Features
Cross-Version Compatibility
// iOS 17+ compatible onChange modifier
someView.onChangeCompat(of: value) { oldValue, newValue in
// Handle value changes, automatically adapts to iOS 17+ and earlier versions
}
// Haptic feedback compatibility
someView.sensoryFeedbackCompat(trigger: triggerValue)Enum Definitions
// Button style types
enum ButtonStyleType {
case common // Common button style
case text // Text button style
}
// Subscription types
enum SubscriptionType: String, CaseIterable {
case revenueCat // RevenueCat subscription
case storeKit2 // StoreKit2 subscription
}
// Presentation styles
enum SheetOrFullScreenCover: String, CaseIterable {
case sheet // Sheet presentation
case fullScreenCover // Full screen cover
}Dependencies
This module requires the following external libraries:
- JDStatusBarNotification - Status bar notifications
- WhatsNewKit - "What's New" presentation
- AlertToast - Toast notifications
- RevenueCat (optional) - Subscription management
- SafariServices - In-app browser
Best Practices
1. Modular Usage
// Combine multiple modifiers
ContentView()
.cardRoundedCorners()
.cardShadow()
.requirePremium()
.showToast($showToast, message: "Success!", isSuccess: true)2. State Management
@State private var isLoading = false
@State private var showError = false
Button("Submit") {
isLoading = true
// Perform operation
}
.loading($isLoading)
.showToast($showError, message: "Operation failed", isSuccess: false)3. Theme Adaptation
// Automatically adapt to dark/light mode
CardView()
.colorSchemeShadow(
light: .gray.opacity(0.3),
dark: .white.opacity(0.1)
)
.viewBackground()Last updated on