
Building native macOS applications with Rust - ingve
https://blog.bugsnag.com/building-macos-apps-with-rust/
======
robohamburger
Given my experience with rust I imagine a rust specific framework would
probably be the best. I imagine something like react/flux would play nicely
with ownership and borrowing.

Cool to see its possible to do objective c without everything being unsafe
though!

------
amelius
How easy is it to write closures for event handling in Rust?

Is it anywhere near as easy as doing this in Javascript? Or does it require
such things as manual memory management?

~~~
careersuicide
It's not JavaScript easy, but it's not too hard once you wrap your head around
Cell, RefCell, Rc, and friends.

Here's an example using GTK that does something vaguely useful, I have no idea
if it's considered idiomatic, but it works as intended:
[https://github.com/seaneshbaugh/image_viewer/blob/master/src...](https://github.com/seaneshbaugh/image_viewer/blob/master/src/main.rs)

The closures themselves are trivial, it's manipulating the stuff outside the
closure that's tricky (for a Rust neophyte like me).

The relevant definitions are

    
    
      let area = Rc::new(RefCell::new(DrawingArea::new()));
    

and

    
    
      let scale = Rc::new(Cell::new(1.0));
    

Rather than just define things as usual they need to be wrapped in a Cell (for
value types) or RefCell (for reference types) and then wrapped in an Rc so we
can keep track of reference counts at run time.

    
    
      {
          let scale = scale.clone();
    
          let area = area.clone();
    
          window.connect_key_release_event(move |_, key_event| {
              let mut s = scale.get();
    
              let key = key_event.get_keyval() as i32;
    
              if key == gdk_sys::GDK_KEY_plus {
                  s += 0.1;
              } else {
                  if key == gdk_sys::GDK_KEY_minus {
                      s -= 0.1;
                  }
              }
    
              scale.set(s);
    
              area.borrow().queue_draw();
    
              Inhibit(false)
          });
      }
    

Inside the new scope (which is very important) scale and area are cloned which
increments their count, then inside the close scale.get() is used to get a
copy of scale (which is possible since it's a float) and then after doing
stuff to it based on the key that is pressed it's set using scale.set(). For
area it's a bit trickier, it has to first be borrow()'ed before anything can
be done with it.

~~~
legulere
A bit of bikeshedding on the code you linked:

Why are you commenting out code? If you delete it, it will still be in your
git history. If you have some reason to leave it in, document why and wrap it
with a `if false {}` or `if debug {}` instead. That way autoformatters will
not destroy your code and the compiler makes sure your code keeps compiling.

Leaving a line empty between statements helps structuring code. Putting an
empty line after each statement makes the code as unstructured as without.

~~~
Frondo
My personal reason for commenting out code for a few revisions, instead of
deleting right away, is to keep a reminder of the work in progress. When I'm
satisfied that the old code's really done for, I delete it and leave it to the
history.

Until then, it's easier to review the commented-out bits than to go back
through version history. (And plenty easy to delete it when it's ready to be
deleted!)

~~~
problems
Your VCS should really make it quite easy to go back through version history.
If it's not doing that, that's a separate problem.

Maybe it's just me, but I find it an eyesore to have large blobs of commented
out code laying around even for a short period (not to mention the odds of me
remembering to delete them are close to nil).

~~~
kough
How do you remember which commit to diff against? How do you easily pick out
the relevant parts of that diff once many changes have occurred - months or
even years later?

~~~
saghm
> How do you remember which commit to diff against?

Ideally your commit message would indicate this. If you're always consistent
in the language you use to describe similar changes, you can just search
through your git log to find it.

> How do you easily pick out the relevant parts of that diff once many changes
> have occurred - months or even years later?

You can use `git show` to just look at that one commit where you deleted the
code

------
Demcox
I see Rust mentioned exponentially more here on HN lately and it has
definitely peaked my interest as a 2nd year CS student. As soon as I get some
free time, I'll give rust a try!

------
jaegerpicker
I'm a little torn by this, I love Rust and think it has the potential to be a
great language. So in that regard this is really cool and neat. On the other
hand I also really like swift and I have a hard time understand what benefit
Rust would give over swift since Apple had put a large amount of work into the
overall safety of swift interop with Obj-C, though it's far from perfect.

~~~
wyager
> I have a hard time understand what benefit Rust would give over swift

Rust is a drastically better-designed language, with substantially improved
safety mechanisms, more advanced memory management (via lifetimes), generally
better performance, and more powerful (generic) ADTs, to name a few things.
That said, I'm not convinced it's better for the UI-centric development that
Swift is targeting.

~~~
bluejekyll
While I agree with you, to be fair to Swift it has other goals, and one
primary one is to seamlessly work with existing objc, which it's excelled at.

~~~
paulddraper
> to be fair to Swift it has other goals

Exactly. If those are goals you share use Swift. If not, it may not be a good
match.

~~~
benliong78
So the question is valid: what benefit does one get using Rush over Swift on
developing for Mac? Rust may be a better language overall, while still be an
inferior choice in this context of Mac OS X Native app development.

~~~
wyager
I listed some of the benefits earlier in the thread. What I obviously can't do
is prove it's better for native app development, because that depends on the
developer.

------
masklinn
Wouldn't it make sense for the magical macro to RC-ify the objects, and handle
retain/release calls that way?

------
rhodysurf
I assume they are using XCode to package the bundle because of signing so the
app is able to be packaged for the app store?

If you just need to create an .app bundle you can manually create the folder
structure and .plist files which allows you to run the rust binary directly
while looking the same to a user as an XCode bundled app.

~~~
mwcampbell
You don't even need to use Xcode proper to code-sign a Mac app. Just use the
codesign command-line utility. I've developed a Mac app using only the
command-line tools that come with Xcode (via a build system, of course), not
Xcode itself. To submit to the Mac App Store, I use the Application Loader app
that comes with Xcode.

Edit: But I do use Xcode to build, run, and submit iOS apps. Never tried to do
it any other way for that platform.

~~~
rhodysurf
Thats really good to know, and quite useful. Thanks!

------
smallduck

        /// Designate this instance as suitable for being released
        /// once it is out of scope
        fn autorelease(&self) -> Self;
    

Note, this isn't quite what autorelease means. Often the time between the
instance going out of scope and when it's actually released is significant.

------
st3v3r
What about UI? Are you forced to do layout in code, or are you able to load in
Xibs or Storyboards?

~~~
kattrali
You can take either approach. I opted to do it in code[0] as a personal
preference since it reduced the amount of time spent jumping between Xcode and
Vim, but creating a xib or storyboard and referencing it from Rust using
NSBundle resources is just as possible. Maybe I should add examples of using
resources to the code samples in the blog post. Thanks!

0:
[https://github.com/kattrali/webkitten/blob/6ac4ced187b44fe91...](https://github.com/kattrali/webkitten/blob/6ac4ced187b44fe913ed59db6dd632762b3e5fdd/webkitten-
cocoa/src/ui/window.rs#L300-L305)

~~~
st3v3r
That would be nice. I myself am someone who can't stand doing UI work in code,
and prefer to lay it out visually if possible.

------
Sidnicious
How do you subclass an ObjC class from Rust — say, to create a delegate for a
UI element?

How is type safety handled? It looks like msg_send itself isn't type safe, but
there are hand-written bindings… is that right?

~~~
kattrali
> How do you subclass an ObjC class from Rust — say, to create a delegate for
> a UI element?

The objc::declare module includes ClassDecl, which enables registering a new
Objective-C class.

objc::declare documentation: [http://sasheldon.com/rust-
objc/objc/declare/index.html](http://sasheldon.com/rust-
objc/objc/declare/index.html)

Example usage: [https://github.com/kattrali/rust-mac-app-
examples/blob/maste...](https://github.com/kattrali/rust-mac-app-
examples/blob/master/5-declaring-new-objc-class/src/main.rs#L56)

> How is type safety handled? It looks like msg_send itself isn't type safe,
> but there are hand-written bindings… is that right?

Correct. msg_send operates on objc::runtime::Object references. The
handwritten bindings use the impl_objc_class macro referenced in the post to
define separate types for different Objective-C classes, and make messaging
the wrong type of object a compile-time rather than a runtime error.

The impl_objc_class macro implements the ObjCClass and PartialEq traits for a
new type: [https://github.com/kattrali/rust-mac-app-
examples/blob/maste...](https://github.com/kattrali/rust-mac-app-
examples/blob/master/5-declaring-new-objc-class/src/objc_class.rs#L34)

