Skip to content

Instantly share code, notes, and snippets.

@ollieatkinson
Last active April 2, 2025 06:43
Show Gist options
  • Save ollieatkinson/eb87a82fcb5500d5561fed8b0900a9f7 to your computer and use it in GitHub Desktop.
Save ollieatkinson/eb87a82fcb5500d5561fed8b0900a9f7 to your computer and use it in GitHub Desktop.
Utilise the private CoreSVG framework in Swift
import Darwin
import Foundation
import UIKit
// https://github.com/xybp888/iOS-SDKs/blob/master/iPhoneOS17.1.sdk/System/Library/PrivateFrameworks/CoreSVG.framework/CoreSVG.tbd
// https://developer.limneos.net/index.php?ios=17.1&framework=UIKitCore.framework&header=UIImage.h
@objc
class CGSVGDocument: NSObject { }
var CGSVGDocumentRetain: (@convention(c) (CGSVGDocument?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentRetain")
var CGSVGDocumentRelease: (@convention(c) (CGSVGDocument?) -> Void) = load("CGSVGDocumentRelease")
var CGSVGDocumentCreateFromData: (@convention(c) (CFData?, CFDictionary?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentCreateFromData")
var CGContextDrawSVGDocument: (@convention(c) (CGContext?, CGSVGDocument?) -> Void) = load("CGContextDrawSVGDocument")
var CGSVGDocumentGetCanvasSize: (@convention(c) (CGSVGDocument?) -> CGSize) = load("CGSVGDocumentGetCanvasSize")
typealias ImageWithCGSVGDocument = @convention(c) (AnyObject, Selector, CGSVGDocument) -> UIImage
var ImageWithCGSVGDocumentSEL: Selector = NSSelectorFromString("_imageWithCGSVGDocument:")
let CoreSVG = dlopen("/System/Library/PrivateFrameworks/CoreSVG.framework/CoreSVG", RTLD_NOW)
func load<T>(_ name: String) -> T {
unsafeBitCast(dlsym(CoreSVG, name), to: T.self)
}
public class SVG {
deinit { CGSVGDocumentRelease(document) }
let document: CGSVGDocument
public convenience init?(_ value: String) {
guard let data = value.data(using: .utf8) else { return nil }
self.init(data)
}
public init?(_ data: Data) {
guard let document = CGSVGDocumentCreateFromData(data as CFData, nil)?.takeUnretainedValue() else { return nil }
guard CGSVGDocumentGetCanvasSize(document) != .zero else { return nil }
self.document = document
}
public var size: CGSize {
CGSVGDocumentGetCanvasSize(document)
}
public func image() -> UIImage? {
let ImageWithCGSVGDocument = unsafeBitCast(UIImage.self.method(for: ImageWithCGSVGDocumentSEL), to: ImageWithCGSVGDocument.self)
let image = ImageWithCGSVGDocument(UIImage.self, ImageWithCGSVGDocumentSEL, document)
return image
}
public func draw(in context: CGContext) {
draw(in: context, size: size)
}
public func draw(in context: CGContext, size target: CGSize) {
var target = target
let ratio = (
x: target.width / size.width,
y: target.height / size.height
)
let rect = (
document: CGRect(origin: .zero, size: size), ()
)
let scale: (x: CGFloat, y: CGFloat)
if target.width <= 0 {
scale = (ratio.y, ratio.y)
target.width = size.width * scale.x
} else if target.height <= 0 {
scale = (ratio.x, ratio.x)
target.width = size.width * scale.y
} else {
let min = min(ratio.x, ratio.y)
scale = (min, min)
target.width = size.width * scale.x
target.height = size.height * scale.y
}
let transform = (
scale: CGAffineTransform(scaleX: scale.x, y: scale.y),
aspect: CGAffineTransform(translationX: (target.width / scale.x - rect.document.width) / 2, y: (target.height / scale.y - rect.document.height) / 2)
)
context.translateBy(x: 0, y: target.height)
context.scaleBy(x: 1, y: -1)
context.concatenate(transform.scale)
context.concatenate(transform.aspect)
CGContextDrawSVGDocument(context, document)
}
}
@ningkaiqiang
Copy link

Wow, This is cool, think you for doing this @ollieatkinson, Can I display animated svg using CoreSVG?

@thomasalbert1993
Copy link

Which iOS is it available from?

@ollieatkinson
Copy link
Author

Which iOS is it available from?

iOS 13 / macOS 10.15

@ollieatkinson
Copy link
Author

Wow, This is cool, think you for doing this @ollieatkinson, Can I display animated svg using CoreSVG?

No, animated SVG does not work unfortunately

@thomasalbert1993
Copy link

Do you know why applying .withRenderingMode(.alwaysTemplate) to rendered image (mySVG.image()) does not work?
I'm not able to tint my SVG icons this way, whereas it works when I import them in a .xcassets file 🤔

@ollieatkinson
Copy link
Author

template mode only works from assets taken from xcasset file, unsure of the exact reason - possibly due to how they are loaded and can be masked. To work around this you can mask a color with the SVG.

@ollieatkinson
Copy link
Author

Personally, I use the svg.draw(in: context, size: rect.size) method so I can directly apply fill mode in CGContext.

@thomasalbert1993
Copy link

Thanks for your answer! Yes I managed to re-color the SVG but I wanted to take advantage of UIImageView.tintColor which grays icon when view is not in focus, but it seems not to be doable this way with SVGs. Too bad..

@ollieatkinson
Copy link
Author

it's possible if you use this and apply the tint inside CGContext

@Eggers-CGI
Copy link

Hey, nice gist :-) I see for each SVG rendered a "CGGStackRestore: CG GState restored too many times" - do you experience the same? Am i doing something wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment