swhitty / SwiftDraw

Swift library and command line tool to convert SVGs into SFSymbol, PNG, PDF and Swift source code.
zlib License
326 stars 41 forks source link

Usable from Objective-C? #4

Closed samizdatco closed 1 year ago

samizdatco commented 1 year ago

Thanks so much for this terrific library. I've been looking into getting it working with an old objc-based app of mine and am a little confused by the interface in the generated headers. I can see in Image.swift that the init method can handle urls, bundle names, as well as data. But the SwiftDraw-Swift.h file that's generated via swift build only contains argument-less stubs:

SWIFT_CLASS_NAMED("Image")
@interface SVGImage : NSObject
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end

Shouldn't the various other initializers also be present (or am I missing something obvious here)?

swhitty commented 1 year ago

Hi @samizdatco, thanks for the feedback. 🙏🏻

Most of the functionality within SwiftDraw is only available from Swift. Thanks for pointing out the shortcoming, the latest version 0.9.2 now allows you to create images from svgs within Objective-C;

UIKit

imageView.image = [UIImage svgNamed: @"sample.svg"];

macOS

imageView.image = [NSImage svgNamed: @"sample.svg"];

I do not plan on exposing much more to ObjectiveC. It is possible to write your own extensions and expose additional functionality as you require.

Cheers

samizdatco commented 1 year ago

I do not plan on exposing much more to ObjectiveC. It is possible to write your own extensions and expose additional functionality as you require.

That seems totally reasonable and thanks a ton for adding the bundle-loading method!

The thing that would be especially handy for me is being able to create NSImages from NSData though. Would you consider adding bindings for the data & url initializers too? Maybe something along the lines of this copy-and-paste job (though surely there's a way to cut down on the redundancy...):

public extension NSImage {

  convenience init?(svgNamed name: String, in bundle: Bundle = Bundle.main) {
    guard let image = Image(named: name, in: bundle) else { return nil }

    self.init(size: image.size, flipped: true) { rect in
      guard let ctx = NSGraphicsContext.current?.cgContext else { return false }
      ctx.draw(image, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
      return true
    }
  }

  convenience init?(svgData data: Data) {
    guard let image = Image(data: data) else { return nil }

    self.init(size: image.size, flipped: true) { rect in
      guard let ctx = NSGraphicsContext.current?.cgContext else { return false }
      ctx.draw(image, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
      return true
    }
  }

  convenience init?(svgFileURL url: URL) {
    guard let image = Image(fileURL: url) else { return nil }

    self.init(size: image.size, flipped: true) { rect in
      guard let ctx = NSGraphicsContext.current?.cgContext else { return false }
      ctx.draw(image, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
      return true
    }
  }

  @objc
  static func svgNamed(_ name: String, inBundle: Bundle) -> NSImage? {
    NSImage(svgNamed: name, in: inBundle)
  }

  @objc
  static func svgNamed(_ name: String) -> NSImage? {
      NSImage(svgNamed: name, in: .main)
  }

  @objc
  static func svgFromData(_ data: Data) -> NSImage? {
      NSImage(svgData: data)
  }

  @objc
  static func svgFromURL(_ url: URL) -> NSImage? {
      NSImage(svgFileURL: url)
  }
}
swhitty commented 1 year ago

Hi @samizdatco

Release 0.9.4 adds 2 more convenience initialisers to Swift and ObjectiveC. They have been named in line with the existing platform initialisers

UIImage(svgData: Data)
UIImage(contentsOfSVGFile path: String)

NSImage(svgData: Data)
NSImage(contentsOfSVGFile path: String)

And exports to Objective-C

- (instancetype) initWithSVGData: (NSData *)
- (instancetype) initWithContentsOfSVGFile: (NSString *)

While AppKit does include NSImage(contentsOf url: URL) I have not implemented an SVG variant as it is trivial for consumers to write using Data(contentsOf url: URL).

samizdatco commented 1 year ago

Fantastic! Thanks again for making these changes.