Where it's difficult or impossible to "chain" together multiple CIFilter calls to achieve the desired effect - maybe due to a class that has a single property, one way to overcome this is to do the following:
- Subclass 
CIFilter, overriding everything you need to. This may include attributes, setValue(forKey:), and most importantly, outputImage. 
- Subclass 
CIFilterConstructor, and create a registerFilter() method. 
For example, let's say you wish to combine a gaussian blur and then add a monochrome red tone to an image. At it's most basic you can do this:
class BlurThenColor:CIFilter {
    let blurFilter = CIFilter(name: "CIGaussianBlur")
    override public var attributes: [String : Any] {
        return [
            kCIAttributeFilterDisplayName: "Blur then Color",
            "inputImage": [kCIAttributeIdentity: 0,
                           kCIAttributeClass: "CIImage",
                           kCIAttributeDisplayName: "Image",
                           kCIAttributeType: kCIAttributeTypeImage]
        ]
    }
    override init() {
        super.init()
    }
    @available(*, unavailable) required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override public func setValue(_ value: Any?, forKey key: String) {
        switch key {
        case "inputImage":
            blurFilter?.setValue(inputImage, forKey: "inputImage")
        default:
            break
        }
    }
    override public var  outputImage: CIImage {
        return (blurFilter?.outputImage)! .applyingFilter("CIColorMonochrome", parameters: ["inputColor": CIColor(red: 1.0, green: 0.0, blue: 0.0)])
    }
}
If you wish to expose more attributes, you can simply add them to the attributes and setValue(forKey:) overrides along wist adding variables and setDefaults. Here I'm simply using the defaults.
Now that you've chained your effect together into one custom filter, you can register it and use it:
let CustomFilterCategory = "CustomFilter"
public class CustomFilterConstructor: NSObject, CIFilterConstructor {
    static public func registerFilter() {
        CIFilter.registerName(
            "BlurThenColor",
            constructor: CustomFilterConstructor(),
            classAttributes: [
                kCIAttributeFilterCategories: [CustomFilterCategory]
            ])
    }
    public func filter(withName name: String) -> CIFilter? {
        switch name {
        case "BlurThenColor":
            return BlurThenColor()
        default:
            return nil
        }
    }
}
To use this, just be sure to register the filter (I tend to put mine in AppDelegate if possible):
CustomFilterConstructor.registerFilter()
From there, you can use BlurThenColor just like any other CIFilter. Instantiate it, use setValue, and call outputImage.
Please note, this code will crash because of force-unwrapping of inputImage and/or typos. I'm sure you can make this more safe - but rest assured that I've tested this and it works. (I created this custom filter and replaced it in an app where the force-unwraps don't happen.)