I'm playing around with some code to manipulate UIColors in place, which I understand may be (is) completely unsupported. I was able to quickly write an obj-c version that ignores a const warning, but I've been having a lot of trouble trying to convert it to swift.
I'm not sure exactly how to get unsafe mutable access to a read only property and I would love some help here. The obj-c source is below:
@implementation UIColor (MemoryManipulation)
-(void)replaceWith:(UIColor *)newColor {
size_t dstNum = CGColorGetNumberOfComponents(self.CGColor);
size_t srcNum = CGColorGetNumberOfComponents(newColor.CGColor);
// not true, but fine for now.
NSAssert(dstNum == 4, @"UIColor must have 4 color components");
NSAssert(srcNum == 4, @"UIColor must have 4 color components");
CGFloat *dstComponents = (CGFloat *)CGColorGetComponents(self.CGColor);
CGFloat *srcComponents = (CGFloat *)CGColorGetComponents(newColor.CGColor);
dstComponents[0] = srcComponents[0];
dstComponents[1] = srcComponents[1];
dstComponents[2] = srcComponents[2];
dstComponents[3] = srcComponents[3];
}
@end
I don’t think this is a good idea, you’re trying to edit UIColor in-place. UIColor is immutable so that they can share the same object for the same color. Trying to mutate if could easily lead to unexpected result, and borderline anti-pattern.
Also most of the functions you used are converted safely into Swift, further preventing you from editing them.
I do suggest that you replace UIColor with a new one whenever you want to mutate, compiler should already try to optimize and avoid allocating new instance when possible.
I agree this is a bad idea and won’t be put into production code. However, for the purposes of the test, I explicitly want to manipate the color that multiple uikit objects are referencing so they all change together.
Regardless, the question is more along the lines of: how could you get an UnsafeMutableBufferPointer to a readonly/const ivar.
Your best bet would be to create UnsafeMutablePointer from UnsafePointer using this initializer. But I don’t know how COW would interact in this scenario.
I can't figure out how to correctly create the first UnsafePointer to underlying CGColor.components. It seems like whatever I try, Swift won't return the same pointer as Obj-c, test code below:
Swift
newColor.cgColor.components!.withUnsafeBufferPointer { (src) -> Void in
print("swift-src:", src)
}
let src = UnsafePointer(newColor.cgColor.components!)
print("swift-src:", src)
and even if you do create the UnsafePointer pointing to the same address(unless I'm misinterpreting the prints), somehow the object isn't there anymore
You can see that it creates a new Array, copying the contents. You may want to experiment with the __unsafeComponents API that the implementation uses. As others have said, this isn't something you should do in production.
I didn't think to look at Foundation! Thanks a bunch, I think I'll be able to get the correct pointers now.
I'm just playing around customizing iOS appearance in as many hacky ways as I can. In reality I use NSNotfiication everywhere to get proper appearance changing.
Just to clarify the terminology: the code I quoted is not part of Foundation. It's part of the so-called Swift overlay for Core Graphics. Apple provides "Swifty" APIs for a number of its C and Objective-C frameworks, and these are called "overlays" because they sit on top of the original frameworks.
Unlike the actual framework code, which is closed source, the overlays are part of the Swift project and thus open source. The overlays live at stdlib/public/Darwin in the Swift repo (there's one subfolder per framework, e.g. CoreGraphics).
I find it a bit weird that this code is located inside the "stdlib" folder because it's not part of the Swift standard library, but that's how it is. The actual stdlib is at stdlib/public/core.