[HN Gopher] Native Mac APIs for Go
       ___________________________________________________________________
        
       Native Mac APIs for Go
        
       Author : maydemir
       Score  : 307 points
       Date   : 2021-02-04 16:33 UTC (6 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | Klonoar wrote:
       | This is seriously cool. I feel slightly validated that I've also
       | been wrapping these by hand, since it's kind of quirky to try and
       | auto-generate these while keeping the same usability and such as
       | their original incarnations.
       | 
       | For those interested in a Rust variant, I've been hacking on one
       | for awhile: https://github.com/ryanmcgrath/cacao
       | 
       | I wouldn't say it's yet ready for production use, but so far it's
       | working pretty well for me. Been dogfooding it by building an app
       | I've wanted for a bit - a proper magic-wormhole macOS app.
       | 
       | One of the things I've really wanted to keep to is the delegate
       | pattern, since I think it actually works really well for Rust's
       | model. A fun example I finished yesterday is ListView cell reuse:
       | 
       | https://twitter.com/ryanmcgrath/status/1357097991081844737/p...
       | 
       | Ultimately I view this as one of the last pieces needed for a
       | cross-platform Rust UI framework to actually work.
        
         | [deleted]
        
       | dvt wrote:
       | Great work, OP.
       | 
       | However, I think it's problematic that modern languages (Go,
       | Rust) don't provide these kinds of bindings out of the box. I
       | honestly think it's probably starting to be "in-scope" for
       | standard libraries. I'm working on a side-project now which needs
       | a desktop app, and I'm actually using Fyne[1] because it makes
       | cross-platform development semi-easy (even though it doesn't look
       | native, nor particularly great). The alternative is using either
       | Electron or using something like this for OSX, using something
       | else for Windows, and using something else for Linux.
       | 
       | Window/widget/notification/taskbar APIs are stable for all major
       | operating systems, and it seems like we keep reinventing the
       | wheel here.
       | 
       | [1] https://fyne.io/
        
       | jfb wrote:
       | We used a lot of PyObjC BITD at Apple to explore frameworks that
       | we couldn't get documentation for. That was simultaneously
       | awesome and so so so broken, from an organizational perspective.
        
       | revskill wrote:
       | So we could use Go to develop iPhone apps ?
        
         | gspq wrote:
         | You can use Gomobile for that.
        
       | tectonic wrote:
       | Most of Jeff Lindsay's stuff is worth checking out. He also
       | started dokku and coined the term webhook.
        
         | 52-6F-62 wrote:
         | We use Dokku on the regular for a number of small services at
         | work. It was a little strange being new to it, but it's been
         | great. Kudos to him.
        
           | asaddhamani wrote:
           | I know it's kind of going against the idea of Dokku, but can
           | it be made to scale across servers? I love the simplicity of
           | Heroku but the cost can be a barrier for some use cases. But
           | k8s is too complicated. Anything like Dokku that scales
           | horizontally?
        
             | cweagans wrote:
             | The scheduler in Dokku is pluggable. You can use Kubernetes
             | or Nomad: http://dokku.viewdocs.io/dokku/advanced-
             | usage/schedulers/alt...
        
             | phamilton wrote:
             | If I were avoiding heroku because of cost and had a need to
             | scale horizontally, I'd use Fargate with spot instances.
             | 
             | Manage it with CDK or terraform. As long as you have enough
             | containers running, spot ends up being a non issue.
        
               | qbasic_forever wrote:
               | It's even simpler now, lambda supports running docker
               | images as of a couple months ago:
               | https://aws.amazon.com/blogs/aws/new-for-aws-lambda-
               | containe...
        
               | jamesmishra wrote:
               | I also recommend this path, but I found it really hard to
               | write all of the required Terraform for the VPC, security
               | groups, load balancers, and Fargate configuration.
               | 
               | So I put together an open-source Terraform super-module
               | to automatically set all of that up in a few lines of
               | code.
               | 
               | https://provose.com/
        
             | ipmb wrote:
             | shameless plug... you might be interested in
             | https://apppack.io. I built it for very similar reasons.
        
             | qbasic_forever wrote:
             | Look at a hosted knative solution, like Google Cloud run or
             | one of many others: https://knative.dev/docs/knative-
             | offerings/ You don't have to know or care about anything
             | Kubernetes with it, but you get ease of just throwing
             | containers at something and having it run them for web-
             | scenarios. They scale up and down to zero so you aren't
             | paying a cost when nothing is happening. And when/if you
             | need it, you have the full power of a kubernetes cluster at
             | your fingertips too.
             | 
             | AWS lambda can run plain old docker containers now too.
             | Check out something like the serverless framework to make
             | it easy to define a bunch of web services or APIs and
             | deploy to lambda, knative, etc.
        
             | joshmanders wrote:
             | If you're looking for a cost-effective alternative to
             | Heroku, might I suggest signing up for my product
             | https://primcloud.com. We're pushing towards public
             | availability by end of February early March at the absolute
             | latest.
             | 
             | I'm a former lover of Heroku and maintainer of Dokku.
        
             | jtsiskin wrote:
             | This is the same concern I had. I settled on Caprover
             | (https://caprover.com/), seems to exactly fit the bill.
        
             | turtlebits wrote:
             | flynn.io was the product I used found when I wanted a
             | little better scalability than Dokku. This was a few years
             | ago when I tried it, so unsure of the current landscape.
        
               | asaddhamani wrote:
               | I had tried flynn in the past (a few years ago just as
               | you) but couldn't get it to work, but that was with
               | Meteor which has very specific deployment requirements.
               | I'll try out flynn when I need to scale beyond Dokku.
        
               | goliatone wrote:
               | For my hobby servers I have tried dokku, Flynn and
               | countless other similar solutions. Settled with
               | caprover[1] and very happy with it
               | 
               | [1] https://caprover.com
        
         | dimitrios1 wrote:
         | also wrote registrator which helped pioneer the technique of
         | service discovery we see in so many cloud tools and platforms
         | today.
        
           | monadic3 wrote:
           | Old is new again, apparently. :)
        
           | progrium wrote:
           | also helped design docker (the good parts) and a bunch of
           | other stuff
        
       | lunixbochs wrote:
       | I don't see an @autoreleasepool anywhere in the source, which I
       | believe means calling some AppKit APIs from Go-managed threads
       | will silently leak memory (due to memory allocated internally by
       | the APIs that will never get released).
       | 
       | In my own projects, I had to wrap calls from foreign threads into
       | AppKit APIs with an @autoreleasepool {} block or my app would
       | leak memory. [1]
       | 
       | [1]
       | https://developer.apple.com/library/archive/documentation/Co...
        
         | progrium wrote:
         | I thought I wrapped NSAutoreleasePool but maybe I just used it
         | directly dynamically. If something is not wrapped in the source
         | that doesn't mean you can't use it.
         | objc.Get("NSAutoreleasePool").Alloc().Init()
         | 
         | Unfortunately I can afford some leaks at the moment, so if
         | that's critical to anybody else and I'm doing something wrong
         | just submit a PR
        
           | lunixbochs wrote:
           | Ah, I don't just mean the class, I mean you need to have an
           | autoreleasepool block active before calling into AppKit
           | otherwise you can leak memory on every call. It doesn't look
           | like you're using pools yet, or documenting that users of
           | your library should use them.
           | 
           | See here: https://developer.apple.com/library/archive/documen
           | tation/Co...
           | 
           | > Cocoa always expects code to be executed within an
           | autorelease pool block, otherwise autoreleased objects do not
           | get released and your application leaks memory
        
             | progrium wrote:
             | I guess I will look into this as that really sounds like
             | syntactic sugar for something more basic. Like using the
             | class.
             | 
             | I have a hard time keeping up with their changes but you
             | might be right: https://developer.apple.com/documentation/f
             | oundation/nsautor...
             | 
             | Oddly it says you cannot use them directly, but later
             | implies maybe they are just less efficient. It would be
             | nice if somebody made an issue for this.
        
               | lunixbochs wrote:
               | This answer says the block is more efficient than
               | manually managing NSAutoreleasePool objects:
               | https://stackoverflow.com/a/12448176
               | 
               | This answer looks like a better overview of what the
               | runtime is doing: https://stackoverflow.com/a/21010442
               | 
               | The @autoreleasepool block seems equivalent to this:
               | ctx = _objc_autoreleasePoolPush()         defer
               | _objc_autoreleasePoolPop(ctx)
               | 
               | You could maybe provide sugar for it like this:
               | https://play.golang.org/p/dljXN3BdEGr
        
               | progrium wrote:
               | Awesome, can you throw into an issue?
        
               | lunixbochs wrote:
               | Done https://github.com/progrium/macdriver/issues/12
        
               | progrium wrote:
               | wow, thanks!
        
               | pjscott wrote:
               | The page you linked is not actually ambiguous, though
               | perhaps a bit tricky to read. It says:
               | 
               | 1. If you're compiling Objective C in ARC mode, you can't
               | use NSAutoreleasePool directly, and must instead use
               | @autoreleasepool.
               | 
               | 2. In manual reference counting mode you can use either
               | NSAutoreleasePool or @autoreleasepool, but the latter has
               | lower overhead. (This may matter if e.g. you're draining
               | the autorelease pool on every iteration of a loop to
               | reduce memory spikes.)
               | 
               | Under the hood -- at least on the version I disassembled
               | -- NSAutoreleasePool's -init and -release methods wrap
               | the CoreFoundation CFAutoreleasePoolPush and
               | CFAutoreleasePoolPop functions, which in turn call the
               | runtime's objc_autoreleasePoolPush and
               | objc_autoreleasePoolPop functions, which are the things
               | that @autoreleasepool will cause the compiler to emit
               | directly.
        
       | JacobCarlborg wrote:
       | D has native support for calling Objective-C. No need to
       | integrate with the Objective-C runtime directly. Everything is
       | written using familiar D syntax. No extra overhead compared to
       | Objective-C. The generated D code is the same as the Objective-C
       | compiler would generate.
       | 
       | https://dlang.org/spec/objc_interface.html
        
         | hu3 wrote:
         | Although it's a bit out of the etiquete to post tangents like
         | this, I find it really cool that D has such native support and
         | will be reading more about it because you shared. So thanks.
        
       | w0mbat wrote:
       | I am freaked out by the line, "Retain and Release methods for
       | working with Objective-C garbage collection".
       | 
       | Do they mean the abandoned GC system Apple tried, or the obsolete
       | manual pool-based system Apple used to use?
       | 
       | Either way, I want ARC instead.
        
         | progrium wrote:
         | sorry I meant memory management not garbage collection. again
         | they're just convenience methods wrapping those exact methods
         | on NSObject
        
         | hctaw wrote:
         | Last I tried to bind to ObjC APIs through C you needed to use
         | NSAutoReleasePool at some point, and to interact with the GC as
         | the APIs expect.
         | 
         | There was no option to use anything else. Has that changed?
        
           | w0mbat wrote:
           | ARC is built on top of the retain/release pool system, but it
           | automates everything from the programmer's point of view so
           | you almost never have to explicitly interact with it.
        
             | hctaw wrote:
             | This works across FFI? How?
        
         | JonathonW wrote:
         | Retain/Release methods would be for manual reference counting,
         | as is traditional in Obj-C/Cocoa.
         | 
         | ARC is a compiler trick in the clang objective-c and swift
         | compilers-- essentially injecting the appropriate
         | retain/release calls for you. You won't get that for free in Go
         | (because, again, it's a compiler feature-- compiled ObjC/Swift
         | code is still calling retain/release just like you would
         | manually), but I'd expect some integration with Go's memory
         | management system so you're not having to make those calls
         | manually. (It looks like that is not here in this particular
         | release, though-- they have retain/release calls in some of
         | their examples.)
         | 
         | Objective-C garbage collection is not a thing any more, and
         | hasn't been for quite some time. Deprecated back in macOS 10.8,
         | and outright removed in more recent versions of the runtime (it
         | was never available on iOS, and was taken out sometime around
         | macOS 10.12 on desktop).
        
           | progrium wrote:
           | age old problem, i should definitely warn people about memory
           | management implications. what would you put in the readme for
           | this?
        
             | lunixbochs wrote:
             | Do the BridgeSupport files annotate whether you own
             | returned objects and need to release them? Sprinkling in
             | runtime.SetFinalizer calls based on ownership would be
             | slightly nicer than exposing release/retain.
        
       | gspq wrote:
       | Looks cool. What about Swift support?
        
       | casey wrote:
       | This is great, I've done a few one-off go wrappers of objective C
       | libraries [1][2] for an OSX menuapp framework I built [3].
       | 
       | This seems like a much more general and useful solution, excited
       | to switch some things over to it!
       | 
       | [1] https://github.com/caseymrm/go-pmset
       | 
       | [2] https://github.com/caseymrm/go-smc
       | 
       | [3] https://github.com/caseymrm/menuet
        
       | coetry wrote:
       | This looks really cool! Is there any overhead with using these
       | bindings as opposed to using Swift and calling the APIs directly?
        
         | axaxs wrote:
         | > Is there any overhead with using these bindings
         | 
         | Not the author, but yes. Go calling C is expensive, and that's
         | precisely how this works. Whether or not that matters for your
         | use case is another topic.
        
           | lsllc wrote:
           | There's a bit of a write-up here:
           | 
           | https://www.cockroachlabs.com/blog/the-cost-and-
           | complexity-o...
           | 
           | In their benchmark, calling a `func() {}` in Go vs a `void
           | foo() {}` in C (via CGo) is almost 100x faster.
           | $ go test -bench . -gcflags '-l'    # disable inlining for
           | fairness       BenchmarkCGO-8  10000000              171
           | ns/op       BenchmarkGo-8   2000000000           1.83 ns/op
           | 
           | EDIT: And then you still have the extra overhead when using
           | `C.CString` and `C.GoBytes` if you're passing those sorts of
           | arguments to C.
        
             | _ph_ wrote:
             | But if the called function takes more than 200ns to
             | execute, the overhead is less than 50%. Which probably is
             | true for most relevant functions in an API
        
           | _ph_ wrote:
           | Go calling C is expensive compared to the normal function
           | call overhead, but if the called function does significant
           | work, should not introduce too much overhead.
        
           | MaxBarraclough wrote:
           | > Go calling C is expensive
           | 
           | Is this true even if only primitives are passed and returned?
           | If so, why? For comparison, Java is fast at that these days,
           | if I understand correctly.
           | 
           | (Interesting related reading regarding Java:
           | https://web.archive.org/web/20160304055443/http://nerds-
           | cent... )
        
         | progrium wrote:
         | This is a good question that should be added in the readme.
         | Would love an issue for it to also collect data
        
       ___________________________________________________________________
       (page generated 2021-02-04 23:00 UTC)