I want to load and manipulate SKUIImageColorAnalyzer
and
SKUIAnalyzedImageColors
objects from the private StoreKitUI.framework
.
First, I attempt to load the framework at runtime:
guard case let libHandle = dlopen("/System/Library/PrivateFrameworks/StoreKitUI.framework/StoreKitUI", RTLD_NOW) where libHandle != nil else {
fatalError("StoreKitUI not found")
}
Then, I verify that the SKUIImageColorAnalyzer
class can be found:
guard let analyzerClass: AnyClass = NSClassFromString("SKUIImageColorAnalyzer") else {
fatalError("SKUIImageColorAnalyzer lookup failed")
}
I want to use the analyzeImage:
class method on SKUIImageColorAnalyzer
, which takes in a UIImage
for analysis and returns an SKUIAnalyzedImageColors
object. I do this by verifying the analyzeImage:
selector exists on the SKUIImageColorAnalyzer
object, and recreate the function:
let selector: Selector = "analyzeImage:"
guard case let method = class_getClassMethod(analyzerClass, selector) where method != nil else {
fatalError("failed to look up \(selector)")
}
// recreate the method's implementation function
typealias Prototype = @convention(c) (AnyClass, Selector, UIImage) -> AnyObject? // returns an SKUIAnalyzedImageColors object
let opaqueIMP = method_getImplementation(method)
let function = unsafeBitCast(opaqueIMP, Prototype.self)
Now, I can get a UIImage
object and pass that in as the argument to the function:
let img = UIImage(named: "someImage.jpg")!
let analyzedImageColors = function(analyzerClass, selector, img) // <SKUIAnalyzedImageColors: 0x7f90d3408eb0>
I know that analyzedImageColors
is of type SKUIAnalyzedImageColors
, but the compiler still thinks its type is AnyObject
based on the way I declared Prototype
above. Now I want to access the properties of an SKUIAnalyzedImageColors
object.
From the header, I can see that there are properties such as backgroundColor
, textPrimaryColor
, and textSecondaryColor
on the object. I can access these properties using valueForKey
, but I'd like to expose a public interface on SKUIAnalyzedImageColors
so I can access these properties.
My first attempt was something like this:
// Create a "forward declaration" of the class
class SKUIAnalyzedImageColors: NSObject { }
// Create convenience extensions for accessing properties
extension SKUIAnalyzedImageColors {
func backgroundColor() -> UIColor {
return self.valueForKey("_backgroundColor") as! UIColor
}
func textPrimaryColor() -> UIColor {
return self.valueForKey("_textPrimaryColor") as! UIColor
}
func textSecondaryColor() -> UIColor {
return self.valueForKey("_textSecondaryColor") as! UIColor
}
}
// ...
// modify the prototype to return an SKUIAnalyzedImageColors object
typealias Prototype = @convention(c) (AnyClass, Selector, UIImage) -> SKUIAnalyzedImageColors?
// ...
// access the properties from the class extension
analyzedImageColors?.backgroundColor() // Optional(UIDeviceRGBColorSpace 0.262745 0.231373 0.337255 1)
This still requires me to use valueForKey
. Is there a way to expose a public interface on a class from a framework loaded at runtime?