objective-cmacos3dtouchforce-touch

Find out if Mac is Force Touch - capable


Is it possible to find out if a Mac is Force Touch capable - either via a built-in Trackpad, like the new MacBook, or a Bluetooth device like the Magic Trackpad 2?

I'd like to present preferences specific to Force Touch if the Mac is Force Touch capable, but not display (or disable) those preferences if Force Touch is not available.

In the portion after the separator, you see the options I have in mind in the pic linked here. (sorry, embedding the pic itself didn't work).

preferences

So, not showing the preferences wouldn't restrict users who don't have force touch, it would just let users who have it configure how it should work, and those settings would be useless to users who don't have it.

Is there a way to achieve this?

Thank you and kind regards, Matt

Edit: It's in Objective-C.


Solution

  • I figured it out:

    + (BOOL)isForceTouchCapable
    {
        if (![[self class] isAtLeastElCapitan])
            return NO;
    
        io_iterator_t iterator;
    
        //get default HIDDevice dictionary
        CFMutableDictionaryRef mDict = IOServiceMatching(kIOHIDDeviceKey);
    
        //add manufacturer "Apple Inc." to dict
        CFDictionaryAddValue(mDict, CFSTR(kIOHIDManufacturerKey), CFSTR("Apple Inc."));
    
        //get matching services, depending on dict
        IOReturn ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, mDict, &iterator);
    
        BOOL result = YES;
        if (ioReturnValue != kIOReturnSuccess)
            NSLog(@"error getting matching services for force touch devices");
        else
        {
            //recursively go through each device found and its children and grandchildren, etc.
            result = [[self class] _containsForceTouchDevice:iterator];
            IOObjectRelease(iterator);
        }
    
        return result;
    }
    
    + (BOOL)_containsForceTouchDevice:(io_iterator_t)iterator
    {
        io_object_t object = 0;
        BOOL success = NO;
        while ((object = IOIteratorNext(iterator)))
        {
            CFMutableDictionaryRef result = NULL;
            kern_return_t state = IORegistryEntryCreateCFProperties(object, &result, kCFAllocatorDefault, 0);
            if (state == KERN_SUCCESS && result != NULL)
            {
                if (CFDictionaryContainsKey(result, CFSTR("DefaultMultitouchProperties")))
                {
                    CFDictionaryRef dict = CFDictionaryGetValue(result, CFSTR("DefaultMultitouchProperties"));
                    CFTypeRef val = NULL;
                    if (CFDictionaryGetValueIfPresent(dict, CFSTR("ForceSupported"), &val))
                    {
                        Boolean aBool = CFBooleanGetValue(val);
                        if (aBool) //supported
                            success = YES;
                    }
                }
            }
    
            if (result != NULL)
                CFRelease(result);
    
            if (success)
            {
                IOObjectRelease(object);
                break;
            } else
            {
                io_iterator_t childIterator = 0;
                kern_return_t err = IORegistryEntryGetChildIterator(object, kIOServicePlane, &childIterator);
                if (!err)
                {
                    success = [[self class] _containsForceTouchDevice:childIterator];
                    IOObjectRelease(childIterator);
                } else
                    success = NO;
    
                IOObjectRelease(object);
            }
        }
    
        return success;
    }
    

    Just call + (BOOL)isForceTouchCapable and it will return YES if a Force Touch device is available (a Magic Trackpad 2 or a built in force-touch-trackpad) or NO if there isn't.

    For those interested in how this came to be, I wrote about it on my blog with an example project.