A beautiful, customizable OTP (One-Time Password) input component for SwiftUI with an animated 3D particle background.
📱 SMS Auto-Read - Automatically detects OTP codes from SMS (no setup needed!)
✨ Highly Customizable - Configure colors, fonts, animations, and more
🎨 Beautiful Animations - Smooth transitions and 3D particle effects
📱 iOS & macOS Support - Works on both platforms
🎯 Easy Integration - Simple API with sensible defaults
⚡ Lightweight - No external dependencies
🔒 Secure Input - Number-only keyboard with validation
SMS Auto-Read is now built-in! 🎉 OTP codes are automatically detected from SMS messages on iOS - no configuration needed. Just use the component and it works!
Add ModernOTP to your project using Xcode:
- File → Add Package Dependencies
- Enter package URL:
https://github.com/MahmoudAlaa92/Modern-OTP.git - Select version/branch
- Add to your target
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/MahmoudAlaa92/Modern-OTP.git", from: "1.0.2")
]import SwiftUI
import Modern_OTP
struct ContentView: View {
@State private var isSuccess = false
var body: some View {
OTPContainerView(
expectedCode: "1234",
isSuccess: $isSuccess
)
}
}If you want just the OTP input without the container:
import SwiftUI
import Modern_OTP
struct ContentView: View {
@State private var isSuccess = false
var body: some View {
ModernOTPView(
digitCount: 6,
expectedCode: "123456",
isSuccess: $isSuccess
)
.padding()
}
}let otpConfig = OTPConfiguration(
cellWidth: 60,
cellHeight: 60,
cellCornerRadius: 16,
cellSpacing: 16,
activeBorderColor: .blue,
successBorderColor: .green,
errorBorderColor: .red,
textColor: .primary,
digitFont: .system(size: 28, weight: .bold),
successIconName: "checkmark.circle.fill",
successIconColor: .green
)
OTPContainerView(
expectedCode: "1234",
isSuccess: $isSuccess,
otpConfig: otpConfig
)let containerConfig = OTPContainerConfiguration(
backgroundColor: .black,
surfaceColor: Color(.systemGray6),
containerCornerRadius: 32,
borderColor: .blue,
titleText: "Verify Your Identity",
titleFont: .largeTitle.bold(),
subtitleText: "Enter the 6-digit code we sent to your device.",
showAnimatedBackground: true
)
OTPContainerView(
expectedCode: "123456",
digitCount: 6,
isSuccess: $isSuccess,
containerConfig: containerConfig
)let animationConfig = AnimatedBackgroundConfiguration(
particleCount: 300,
lightModeColor: .orange,
darkModeColor: .orange,
particleOpacity: 0.7,
rotationSpeed: 1.0,
useNumbers: false,
customCharacters: ["★", "◆", "●", "■", "▲"]
)
OTPContainerView(
expectedCode: "1234",
isSuccess: $isSuccess,
animationConfig: animationConfig
)OTPContainerView(
expectedCode: "1234",
isSuccess: $isSuccess,
onSuccess: {
print("OTP verified successfully!")
// Navigate to next screen, etc.
},
onError: {
print("Invalid OTP entered")
// Show error message, haptic feedback, etc.
}
)| Property | Type | Default | Description |
|---|---|---|---|
cellWidth |
CGFloat |
55 |
Width of each OTP cell |
cellHeight |
CGFloat |
55 |
Height of each OTP cell |
cellCornerRadius |
CGFloat |
12 |
Corner radius of cells |
cellSpacing |
CGFloat |
12 |
Spacing between cells |
normalBorderColor |
Color |
.gray.opacity(0.3) |
Border color for inactive cells |
activeBorderColor |
Color |
.orange |
Border color for active cell |
successBorderColor |
Color |
.orange |
Border color on success |
errorBorderColor |
Color |
.red |
Border color on error |
textColor |
Color |
.gray |
Color of entered digits |
caretColor |
Color |
.primary |
Color of blinking caret |
digitFont |
Font |
.title |
Font for digits |
successIconName |
String |
"checkmark" |
SF Symbol for success |
successIconColor |
Color |
.orange |
Color of success icon |
successIconFont |
Font |
.system(size: 30) |
Font for success icon |
errorFlashDuration |
Double |
1.0 |
Duration of error flash |
| Property | Type | Default | Description |
|---|---|---|---|
backgroundColor |
Color |
.white |
Screen background color |
surfaceColor |
Color |
.gray.opacity(0.1) |
Container card color |
containerCornerRadius |
CGFloat |
24 |
Container corner radius |
borderColor |
Color |
.orange |
Container border color |
showBorder |
Bool |
true |
Show container border |
titleText |
String |
"Enter the code" |
Main title text |
subtitleText |
String |
"We sent you a code..." |
Description text |
titleFont |
Font |
.title |
Title font |
titleColor |
Color |
.orange |
Title color |
subtitleFont |
Font |
.body |
Subtitle font |
subtitleColor |
Color |
.gray |
Subtitle color |
showAnimatedBackground |
Bool |
true |
Show particle animation |
| Property | Type | Default | Description |
|---|---|---|---|
particleCount |
Int |
200 |
Number of particles |
lightModeColor |
Color |
.orange |
Particle color (light) |
darkModeColor |
Color |
.orange |
Particle color (dark) |
particleOpacity |
Double |
0.85 |
Particle opacity |
rotationSpeed |
Double |
0.5 |
Rotation speed |
useNumbers |
Bool |
true |
Use numbers (0-9) |
customCharacters |
[String] |
[] |
Custom characters |
struct CustomOTPView: View {
@State private var isSuccess = false
var body: some View {
let otpConfig = OTPConfiguration(
cellWidth: 65,
cellHeight: 65,
cellCornerRadius: 20,
activeBorderColor: .purple,
successBorderColor: .mint,
errorBorderColor: .pink,
digitFont: .system(size: 32, weight: .heavy, design: .rounded),
successIconName: "star.fill"
)
let containerConfig = OTPContainerConfiguration(
backgroundColor: Color(.systemIndigo),
surfaceColor: Color(.systemBackground),
borderColor: .purple,
titleText: "🔐 Secure Access",
titleFont: .system(size: 34, weight: .black, design: .rounded),
subtitleText: "Please enter your verification code"
)
let animationConfig = AnimatedBackgroundConfiguration(
particleCount: 150,
lightModeColor: .purple.opacity(0.3),
darkModeColor: .purple.opacity(0.6),
rotationSpeed: 0.3
)
return OTPContainerView(
expectedCode: "9876",
digitCount: 4,
isSuccess: $isSuccess,
containerConfig: containerConfig,
otpConfig: otpConfig,
animationConfig: animationConfig,
onSuccess: {
print("Access granted!")
}
)
}
}let otpConfig = OTPConfiguration(
normalBorderColor: .gray.opacity(0.2),
activeBorderColor: .black,
successBorderColor: .black,
errorBorderColor: .black,
textColor: .black,
digitFont: .system(size: 24, weight: .medium, design: .monospaced)
)
let containerConfig = OTPContainerConfiguration(
backgroundColor: .white,
surfaceColor: .white,
showBorder: false,
titleFont: .system(size: 28, weight: .semibold),
subtitleFont: .system(size: 16),
showAnimatedBackground: false
)
OTPContainerView(
expectedCode: "1234",
isSuccess: $isSuccess,
containerConfig: containerConfig,
otpConfig: otpConfig
)Complete OTP verification UI with animated background, title, subtitle, and input.
Standalone OTP input component without container or background.
3D particle animation that can be used independently.
- iOS 17.0+ / macOS 14.0+
- Swift 6.0+
- Xcode 16.0+
ModernOTP is available under the MIT license. See LICENSE for details.
Mahmoud Alaa
GitHub: @MahmoudAlaa92
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
If you have any questions or issues, please open an issue on GitHub.
⭐ If you find this package useful, please star the repository!
Made with ❤️ by Mahmoud Alaa

