Skip to content
All essays
iOSMarch 27, 202511 min

Swift Haptic Feedback: Engaging User Experience

Add haptic feedback to iOS apps. Haptic feedback, UI feedback, and custom haptics.

Ü
Ümit Uz
Mobile & Full Stack Developer

Haptic feedback provides tactile responses that enhance user experience. Learn to implement haptics in iOS apps.

UIKit Haptics

Basic Haptic Feedback

swift
import UIKit

class HapticManager {
    static let shared = HapticManager()

    private init() {}

    func notification(_ type: UINotificationFeedbackGenerator.FeedbackType) {
        let generator = UINotificationFeedbackGenerator()
        generator.prepare()
        generator.notificationOccurred(type)
    }

    func impact(_ style: UIImpactFeedbackGenerator.FeedbackStyle) {
        let generator = UIImpactFeedbackGenerator(style: style)
        generator.prepare()
        generator.impactOccurred()
    }

    func selection() {
        let generator = UISelectionFeedbackGenerator()
        generator.prepare()
        generator.selectionChanged()
    }
}

Feedback Types

swift
struct HapticExamplesView: View {
    var body: some View {
        VStack(spacing: 20) {
            Button("Success") {
                HapticManager.shared.notification(.success)
            }

            Button("Warning") {
                HapticManager.shared.notification(.warning)
            }

            Button("Error") {
                HapticManager.shared.notification(.error)
            }

            Divider()

            Button("Light Impact") {
                HapticManager.shared.impact(.light)
            }

            Button("Medium Impact") {
                HapticManager.shared.impact(.medium)
            }

            Button("Heavy Impact") {
                HapticManager.shared.impact(.heavy)
            }

            Divider()

            Button("Selection") {
                HapticManager.shared.selection()
            }
        }
        .padding()
    }
}

Core Haptics

Custom Haptic Patterns

swift
import CoreHaptics

class AdvancedHapticManager: ObservableObject {
    private var hapticEngine: CHHapticEngine?

    func setupHaptics() {
        guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else {
            print("Haptics not supported")
            return
        }

        do {
            hapticEngine = try CHHapticEngine()
            try hapticEngine?.start()
        } catch {
            print("Haptic engine creation failed: \(error)")
        }
    }

    func playCustomHaptic() {
        guard let engine = hapticEngine else {
            setupHaptics()
            return
        }

        // Create haptic pattern
        let sharpness = CHHapticEventParameter(
            parameterID: .hapticSharpness,
            value: 1.0
        )

        let intensity = CHHapticEventParameter(
            parameterID: .hapticIntensity,
            value: 1.0
        )

        let event = CHHapticEvent(
            eventType: .hapticTransient,
            parameters: [sharpness, intensity],
            relativeTime: 0,
            duration: 0.1
        )

        do {
            let pattern = try CHHapticPattern(events: [event], parameters: [])
            let player = try engine.makePlayer(with: pattern)
            try player.start(atTime: 0)
        } catch {
            print("Failed to play haptic: \(error)")
        }
    }
}

Continuous Haptics

swift
extension AdvancedHapticManager {
    func playContinuousHaptic(duration: TimeInterval) {
        guard let engine = hapticEngine else { return }

        let sharpness = CHHapticEventParameter(
            parameterID: .hapticSharpness,
            value: 0.5
        )

        let intensity = CHHapticEventParameter(
            parameterID: .hapticIntensity,
            value: 0.8
        )

        let event = CHHapticEvent(
            eventType: .hapticContinuous,
            parameters: [sharpness, intensity],
            relativeTime: 0,
            duration: duration
        )

        do {
            let pattern = try CHHapticPattern(events: [event], parameters: [])
            let player = try engine.makePlayer(with: pattern)
            try player.start(atTime: 0)
        } catch {
            print("Failed to play continuous haptic: \(error)")
        }
    }
}

Haptic Patterns

Predefined Patterns

swift
struct HapticPatternManager {
    func playHeartbeat() {
        let pattern = [
            (delay: 0.0, intensity: 1.0, duration: 0.1),
            (delay: 0.15, intensity: 0.8, duration: 0.1),
            (delay: 0.4, intensity: 1.0, duration: 0.1),
            (delay: 0.15, intensity: 0.8, duration: 0.1)
        ]

        for (delay, intensity, duration) in pattern {
            DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
                let generator = UIImpactFeedbackGenerator(style: .medium)
                generator.impactOccurred()
            }
        }
    }

    func playNotificationPattern() {
        // Double tap pattern
        HapticManager.shared.impact(.light)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            HapticManager.shared.impact(.light)
        }
    }
}

SwiftUI Integration

Haptic Button

swift
struct HapticButton: View {
    let title: String
    let action: () -> Void
    var hapticStyle: UIImpactFeedbackGenerator.FeedbackStyle = .medium

    var body: some View {
        Button(action: {
            HapticManager.shared.impact(hapticStyle)
            action()
        }) {
            Text(title)
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
        }
    }
}

struct HapticButtonsView: View {
    var body: some View {
        VStack(spacing: 20) {
            HapticButton(title: "Like", hapticStyle: .light) {
                print("Liked")
            }

            HapticButton(title: "Share", hapticStyle: .medium) {
                print("Shared")
            }

            HapticButton(title: "Delete", hapticStyle: .heavy) {
                print("Deleted")
            }
        }
    }
}

Haptic Toggle

swift
struct HapticToggle: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Enable", isOn: $isOn)
            .onChange(of: isOn) { _, newValue in
                if newValue {
                    HapticManager.shared.notification(.success)
                } else {
                    HapticManager.shared.notification(.warning)
                }
            }
    }
}

Context-Aware Haptics

Smart Haptic Feedback

swift
class ContextAwareHaptics {
    func provideFeedback(for action: UserAction) {
        switch action {
        case .success:
            HapticManager.shared.notification(.success)
        case .failure:
            HapticManager.shared.notification(.error)
        case .warning:
            HapticManager.shared.notification(.warning)
        case .tap:
            HapticManager.shared.selection()
        case .scroll:
            HapticManager.shared.impact(.light)
        }
    }

    enum UserAction {
        case success
        case failure
        case warning
        case tap
        case scroll
    }
}

Haptic Customization

Intensity Control

swift
class CustomHaptics {
    func playScaledHaptic(intensity: Float) {
        let generator = UIImpactFeedbackGenerator(style: .medium)
        generator.prepare()

        // Scale impact based on intensity
        switch intensity {
        case 0.0..<0.3:
            generator.impactOccurred(intensity: 0.3)
        case 0.3..<0.7:
            generator.impactOccurred(intensity: intensity)
        default:
            generator.impactOccurred(intensity: 1.0)
        }
    }
}

Best Practices

  1. 1Restraint: Use haptics sparingly
  2. 2Context: Provide context-relevant feedback
  3. 3Intensity: Match intensity to action importance
  4. 4Duration: Keep haptics brief
  5. 5Testing: Test on real devices
  6. 6Preferences: Respect user preferences
  7. 7Battery: Consider battery impact
  8. 8Accessibility: Provide visual alternatives

Haptic feedback creates engaging, tactile experiences!

Related essays

Next essay
Swift iCloud Sync: CloudKit Integration