SVSwiper library uses core UIKit components and provides custom onBoarding screens with many customisation options. SVSwiper provides various delegate methods to customise the navigation according the needs of application. Any contributions towards this library are welcome.
✨ Easy Integration - Simple API with just a few lines of code
🎨 Customizable - Customize colors, fonts, and content
📱 Modern Design - Beautiful horizontal scrolling onboarding screens
🔒 Type Safe - Fully written in Swift with value types
🧪 Tested - Comprehensive unit test coverage
🎯 Delegate Pattern - Modern coordinator delegate for better control
🦋 SwiftUI Support - Native SwiftUI views for iOS 14+ with result builders
🔐 Privacy Manifest - Includes PrivacyInfo.xcprivacy for App Store compliance
- iOS 14.0+
- Swift 5.3+
- Xcode 12.0+
SVSwiper is available through Swift Package Manager.
To add SVSwiper as a dependency to your Xcode project, select File > Swift Packages > Add Package Dependency and enter the repository URL:
https://github.com/sareenv/SVSwiper.git
Alternatively, you can add it to your Package.swift file:
dependencies: [
.package(url: "https://github.com/sareenv/SVSwiper.git", from: "2.1.0")
]Then add SVSwiper to your target dependencies:
targets: [
.target(
name: "YourTarget",
dependencies: ["SVSwiper"]
)
]SVSwiper is also available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'SVSwiper'For modern SwiftUI apps, use the native SVSwiperView with ultra-simple syntax:
import SwiftUI
import SVSwiper
struct ContentView: View {
@State private var showOnboarding = !UserDefaults.standard.bool(forKey: "isOpenedBefore")
var body: some View {
if showOnboarding {
// Just pass your content array! ✨
SVSwiperView([
SVContent(title: "Welcome", description: "Get started", image: UIImage(named: "onboarding1")),
SVContent(title: "Discover", description: "Explore features", image: UIImage(named: "onboarding2")),
SVContent(title: "Get Started", description: "You're all set!", image: UIImage(named: "onboarding3"))
]) {
showOnboarding = false
}
} else {
MainAppView()
}
}
}For a more declarative approach, use the result builder:
struct ContentView: View {
@State private var showOnboarding = true
var body: some View {
if showOnboarding {
SVSwiperView {
SVContent(
title: "Welcome",
description: "Get started with our amazing app",
image: UIImage(named: "onboarding1")
)
SVContent(
title: "Discover",
description: "Explore all the features",
image: UIImage(named: "onboarding2")
)
SVContent(
title: "Get Started",
description: "You're all set!",
image: UIImage(named: "onboarding3")
)
} onComplete: {
withAnimation {
showOnboarding = false
}
}
} else {
MainAppView()
}
}
}struct ContentView: View {
@State private var showOnboarding = true
let contents = [
SVContent(title: "Maximise Profits", description: "Gain with our internal tools", image: UIImage(named: "onboarding1")),
SVContent(title: "Invest in Stocks", description: "We provide great insights", image: UIImage(named: "onboarding2")),
SVContent(title: "Develop Connections", description: "Connect with experts", image: UIImage(named: "onboarding3"))
]
var body: some View {
if showOnboarding {
SVSwiperView(
contents,
backgroundColor: Color(uiColor: .systemBackground),
titleFont: .custom("YourFont-Bold", size: 28),
descriptionFont: .custom("YourFont-Regular", size: 17),
buttonColor: .blue,
buttonTitle: "Let's Go!"
) {
withAnimation {
showOnboarding = false
}
}
.transition(.move(edge: .leading))
} else {
TabView {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
}
}
}
}// 1. Simplest - Just the array
SVSwiperView(contents) { }
// 2. With customization
SVSwiperView(contents, backgroundColor: .blue, buttonColor: .green) { }
// 3. Result builder (DSL style)
SVSwiperView {
SVContent(title: "Screen 1", description: "Description", image: nil)
SVContent(title: "Screen 2", description: "Description", image: nil)
} onComplete: { }
// 4. Full customization with result builder
SVSwiperView(
backgroundColor: .white,
titleFont: .largeTitle,
descriptionFont: .body,
buttonColor: .purple,
buttonTitle: "Start Now"
) {
SVContent(title: "Welcome", description: "Let's begin", image: nil)
SVContent(title: "Features", description: "Discover more", image: nil)
} onComplete: {
print("Completed!")
}The modern approach uses a coordinator delegate pattern for better control over navigation:
import UIKit
import SVSwiper
class AppCoordinator: SVSwiperCoordinatorDelegate {
private let window: UIWindow
init(window: UIWindow) {
self.window = window
}
func start() {
let onboardingController = createOnboardingController()
if onboardingController.shouldShowOnboarding() {
window.rootViewController = onboardingController
} else {
showMainApp()
}
window.makeKeyAndVisible()
}
private func createOnboardingController() -> SVSwiperController {
let data: [SVContent] = [
SVContent(
title: "Maximise Profits",
description: "Gain with our internal tools, we are here to support your company at every stage",
image: UIImage(named: "onboarding1")
),
SVContent(
title: "Invest in Stocks",
description: "We provide great insights, on which stock to buy and sell at any moment of time",
image: UIImage(named: "onboarding2")
),
SVContent(
title: "Develop Connections",
description: "Connect with community of experts in the field of stock market",
image: UIImage(named: "onboarding3")
)
]
let controller = SVSwiperController(
backgroundColor: .white,
details: data,
titleFont: .boldSystemFont(ofSize: 24),
descriptionFont: .systemFont(ofSize: 16)
)
controller.coordinatorDelegate = self
return controller
}
// MARK: - SVSwiperCoordinatorDelegate
func swiperDidComplete(_ controller: SVSwiperController) {
showMainApp()
}
func swiperShouldShow(_ controller: SVSwiperController) -> Bool {
return controller.shouldShowOnboarding()
}
func swiper(_ controller: SVSwiperController, didScrollToPage page: Int) {
print("User scrolled to page: \(page)")
}
private func showMainApp() {
let mainViewController = MainViewController()
window.rootViewController = UINavigationController(rootViewController: mainViewController)
}
}// In SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
self.window = window
let coordinator = AppCoordinator(window: window)
coordinator.start()
}The library still supports the legacy approach for backward compatibility:
import UIKit
import SVSwiper
class ViewController: UIViewController {
private let data: [SVContent] = [
SVContent(title: "Maximise Profits", description: "Gain with our internal tools", image: UIImage(named: "onboarding1")),
SVContent(title: "Invest in Stocks", description: "We provide great insights", image: UIImage(named: "onboarding2")),
SVContent(title: "Develop Connections", description: "Connect with community of experts", image: UIImage(named: "onboarding3"))
]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupSVSwiper()
}
private func setupSVSwiper() {
let controller = SVSwiperController(
backgroundColor: .white,
details: data,
descriptionFont: .systemFont(ofSize: 13)
)
let parentController = MainViewController()
controller.configureSlider(parentViewController: parentController, isNavigationSupported: true)
}
}SVSwiper includes a PrivacyInfo.xcprivacy file that declares:
- No Tracking: The library does not track users
- No Data Collection: No personal data is collected
- UserDefaults API Usage: Documents the use of UserDefaults for storing onboarding completion status (Reason: CA92.1 - Storing user preferences)
This ensures compliance with Apple's App Privacy requirements for iOS 17+ and App Store submissions.
NSPrivacyAccessedAPITypes:
- NSPrivacyAccessedAPICategoryUserDefaults
Reasons: CA92.1 (Accessing user defaults to read/write onboarding completion status)Create content for each onboarding screen:
let content = SVContent(
title: "Your Title",
description: "Your description text",
image: UIImage(named: "yourImage") // Optional
)Customize the appearance:
let controller = SVSwiperController(
backgroundColor: .systemBackground, // Background color
details: contentArray, // Array of SVContent
titleFont: .boldSystemFont(ofSize: 24), // Title font
descriptionFont: .systemFont(ofSize: 16) // Description font
)Customize the SwiftUI view with multiple initialization options:
// Simple - just pass the array
SVSwiperView(contentArray) { }
// With customization
SVSwiperView(
contentArray,
backgroundColor: .white,
titleFont: .system(size: 24, weight: .bold),
descriptionFont: .system(size: 16),
buttonColor: .purple,
buttonTitle: "Get Started"
) {
// Completion handler
}
// Result builder syntax
SVSwiperView {
SVContent(title: "Screen 1", description: "Text", image: nil)
SVContent(title: "Screen 2", description: "Text", image: nil)
} onComplete: {
// Completion handler
}To reset the onboarding status (useful for testing):
// UIKit
let controller = SVSwiperController(backgroundColor: .white, details: data)
controller.resetOnboardingStatus()
// SwiftUI
UserDefaults.standard.removeObject(forKey: "isOpenedBefore")To check if onboarding should be shown:
// UIKit
if controller.shouldShowOnboarding() {
// Show onboarding
} else {
// Show main app
}
// SwiftUI
@State private var showOnboarding = !UserDefaults.standard.bool(forKey: "isOpenedBefore")-
SVContent is now a struct (was a class)
- This improves thread safety and follows Swift best practices
- No code changes required unless you were subclassing (not recommended)
-
Property names updated
svTitle→titlesvDescription→descriptionsvImage→image- Old property names still work but are deprecated
- SwiftUI Support - Native SwiftUI views with result builders for iOS 13+
- Privacy Manifest - Includes PrivacyInfo.xcprivacy for App Store compliance
- Modern Coordinator Pattern - Better separation of concerns
- Result Builder API - SwiftUI-native DSL for declaring content
- Use coordinator delegate pattern instead of
configureSlider:
// Old (still works, but deprecated)
controller.configureSlider(parentViewController: mainVC, isNavigationSupported: true)
// New (recommended)
controller.coordinatorDelegate = self
// Implement SVSwiperCoordinatorDelegate- Update property access:
// Old
let content = SVContent(title: "Title", description: "Desc")
print(content.svTitle)
// New
let content = SVContent(title: "Title", description: "Desc")
print(content.title)- Consider SwiftUI for new projects:
// New SwiftUI approach (ultra-simple!)
SVSwiperView(contents) { }
// Or with result builder
SVSwiperView {
SVContent(title: "Welcome", description: "Let's begin", image: nil)
SVContent(title: "Features", description: "Discover", image: nil)
} onComplete: { }init(
_ details: [SVContent],
backgroundColor: Color = .white,
titleFont: Font = .system(size: 24, weight: .bold),
descriptionFont: Font = .system(size: 16),
buttonColor: Color = .purple,
buttonTitle: String = "Get Started",
onComplete: @escaping () -> Void = {}
)init(
backgroundColor: Color = .white,
titleFont: Font = .system(size: 24, weight: .bold),
descriptionFont: Font = .system(size: 16),
buttonColor: Color = .purple,
buttonTitle: String = "Get Started",
onComplete: @escaping () -> Void = {},
@SVContentBuilder content: () -> [SVContent]
)coordinatorDelegate: SVSwiperCoordinatorDelegate?- Delegate for lifecycle events
shouldShowOnboarding() -> Bool- Check if onboarding should be shownmarkOnboardingCompleted()- Mark onboarding as completedresetOnboardingStatus()- Reset completion status (for testing)configureSlider(parentViewController:isNavigationSupported:)- Legacy configuration method (deprecated)
swiperDidComplete(_ controller: SVSwiperController)- Called when onboarding completesswiperShouldShow(_ controller: SVSwiperController) -> Bool- Determine if onboarding should show
swiper(_ controller: SVSwiperController, didScrollToPage page: Int)- Called on page changes
title: String- The title textdescription: String- The description textimage: UIImage?- Optional image
A SwiftUI-style result builder for declaratively constructing content arrays:
SVSwiperView {
SVContent(title: "Screen 1", description: "Description", image: nil)
SVContent(title: "Screen 2", description: "Description", image: nil)
// ... more content
}To run the example project, clone the repo, and run pod install from the Example directory first.
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
Vinayak Sareen is the author for this repository. If you have any suggestion / recommendations please feel free to contact me at contact@sareenv.com
SVSwiper is available under the MIT license. See the LICENSE file for more info.
Copyright (c) 2019 Vinayak Sareen contact@sareenv.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
