Check if device supports UIFeedbackGenerator in iOS 10

8.8k Views Asked by At

In iOS 10, there is a new api which allows developers to make use of the taptic engine, UIFeedbackGenerator.

While this api is available in iOS 10, it only works on the new devices, iPhone 7 and 7 plus. It does not works on older devices including the 6S or 6S Plus, even those have a taptic engine. I guess the taptic engine on the 7 and 7 plus is a different more powerful one.

I can't seem to find a way to see if the device supports using the new api. I would like to replace some vibrate code with taptic code, where it makes sense.

Edit:

Adding the 3 concrete subclasses for search purposes: UIImpactFeedbackGenerator UINotificationFeedbackGenerator UISelectionFeedbackGenerator

Edit 2:

I have a theory but no iPhone 7 device to test it so if you have one, give it a shot. UIFeedbackGenerator has a methods called prepare(). When printing out an instance of UIImpactFeedbackGenerator, I noticed that it printed a property named "prepared" which would show 0. Calling prepare() in simulator or on iPhone 6S and then printing out the instance still shows prepared as 0. Can someone call prepare() on an instance of UIImpactFeedbackGenerator from an iPhone7 and then print the instance to console to see if prepared is set to 1? This value is not exposed but there may be a way to get this info w/o using private apis.

3

There are 3 best solutions below

3
On

Currently, the best way is to check the device's model using:

public extension UIDevice
    public func platform() -> String {
        var sysinfo = utsname()
        uname(&sysinfo) // ignore return value
        return String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters)
    }
}

The platform names for iPhone 7 and 7 plus are: "iPhone9,1", "iPhone9,3", "iPhone9,2", "iPhone9,4"

Source: iOS: How to determine the current iPhone/device model in Swift?

You can create a function:

public extension UIDevice {
    public var hasHapticFeedback: Bool {
        return ["iPhone9,1", "iPhone9,3", "iPhone9,2", "iPhone9,4"].contains(platform())
    } 
}
1
On

I have extended chrisamanse's answer. It extraxts the generation number from the model identifier and checks if it is equal or greater than 9. Should work with future iPhone models unless Apple decides to introduce a new internal naming scheme.

public extension UIDevice {

    var modelIdentifier: String {
        var sysinfo = utsname()
        uname(&sysinfo) // ignore return value
        return String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters)
    }


    var hasHapticFeedback: Bool {

        // assuming that iPads and iPods don't have a Taptic Engine
        if !modelIdentifier.contains("iPhone") {
            return false
        }

        // e.g. will equal to "9,5" for "iPhone9,5"
        let subString = String(modelIdentifier[modelIdentifier.index(modelIdentifier.startIndex, offsetBy: 6)..<modelIdentifier.endIndex])

        // will return true if the generationNumber is equal to or greater than 9
        if let generationNumberString = subString.components(separatedBy: ",").first,
            let generationNumber = Int(generationNumberString),
            generationNumber >= 9 {
            return true
        }

        return false
    }

}

Use it like so:

if UIDevice.current.hasHapticFeedback {
    // work with taptic engine
} else {
    // fallback for older devices
}
3
On

So, apparently this can be done with a private API call.

Objective-C:

[[UIDevice currentDevice] valueForKey:@"_feedbackSupportLevel"];

Swift:

UIDevice.currentDevice().valueForKey("_feedbackSupportLevel");


... These methods seem to return:

  • 0 = Taptic not available
  • 1 = First generation (tested on an iPhone 6s) ... which does NOT support UINotificationFeedbackGenerator, etc.
  • 2 = Second generation (tested on an iPhone 7) ... which does support it.

Unfortunately, there are two caveats here:

  1. Using these could get your app rejected by Apple during the App Store's App Review, but there doesn't seem to be any other way currently.
  2. We don't know what the actual values represent.

Special thanks to Tim Oliver and Steve T-S for helping test this with different devices. https://twitter.com/TimOliverAU/status/778105029643436033