{"id":389,"date":"2022-01-31T11:18:51","date_gmt":"2022-01-31T11:18:51","guid":{"rendered":"https:\/\/blog.mozilla.org\/data\/?p=389"},"modified":"2022-01-31T11:18:51","modified_gmt":"2022-01-31T11:18:51","slug":"this-week-in-glean-building-and-deploying-a-rust-library-on-ios","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/data\/2022\/01\/31\/this-week-in-glean-building-and-deploying-a-rust-library-on-ios\/","title":{"rendered":"This Week in Glean: Building and Deploying a Rust library on iOS"},"content":{"rendered":"<p>(\u201cThis Week in Glean\u201d is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release notes, documentation, hopes, dreams, or whatever: so long as it is inspired by Glean.) All \u201cThis Week in Glean\u201d blog posts are listed in the <a href=\"https:\/\/mozilla.github.io\/glean\/book\/appendix\/twig.html\">TWiG index<\/a> (and on the <a href=\"https:\/\/blog.mozilla.org\/data\/category\/glean\/\">Mozilla Data blog<\/a>).<\/p>\n<hr \/>\n<p>We ship the Glean SDK for multiple platforms, one of them being iOS applications. Previously I talked about <a href=\"https:\/\/blog.mozilla.org\/data\/2021\/04\/16\/this-week-in-glean-rustc-ios-and-an-m1\/\">how we got it to build on the Apple ARM machines<\/a>. Today we will take a closer look at how to bundle a Rust static library into an iOS application.<\/p>\n<p>The Glean SDK project was set up in 2019 and we have evolved its project configuration over time. A lot has changed in Xcode since then, so for this article we\u2019re starting with a fresh Xcode project, a fresh Rust library and put it all together step by step.<br \/>\nThis is essentially an update to the <a href=\"https:\/\/mozilla.github.io\/firefox-browser-architecture\/experiments\/2017-09-06-rust-on-ios.html\">Building and Deploying a Rust library on iOS<\/a> article from 2017.<\/p>\n<p>For future readers: This was done using Xcode 13.2.1 and rustc 1.58.1.<br \/>\n<em>One note: I learned iOS development to the extent required to ship Glean iOS. I\u2019ve never written a full iOS application and lack a lot of experience with Xcode.<\/em><\/p>\n<h2 id=\"the-application\">The application<\/h2>\n<p>The premise of our application is easy:<\/p>\n<blockquote>\n<p>Show a non-interactive message to the user with data from a Rust library.<\/p>\n<\/blockquote>\n<p>Let\u2019s get started on that.<\/p>\n<h2 id=\"the-project\">The project<\/h2>\n<p>We start with a fresh iOS project. Go to File -&gt; New -&gt; Project, then choose the iOS App template, give it a name such as <code>ShippingRust<\/code>, select where to store it and finally create it. You\u2019re greeted with <code>ContentView.swift<\/code> and the following code:<\/p>\n<pre class=\"swift\"><code>import SwiftUI\r\n\r\nstruct ContentView: View {\r\n    var body: some View {\r\n        Text(&quot;Hello, world!&quot;)\r\n            .padding()\r\n    }\r\n}<\/code><\/pre>\n<p>You can build and run it now. This will open the Simulator and display \u201cHello, world!\u201d. We\u2019ll get back to the Swift application later.<\/p>\n<h2 id=\"the-rust-parts\">The Rust parts<\/h2>\n<p>First we set up the Rust library.<\/p>\n<p>In a terminal navigate to your <code>ShippingRust<\/code> project directory. In there create a new Rust crate:<\/p>\n<div class=\"sourceCode\" id=\"cb2\">\n<pre class=\"sourceCode sh\"><code class=\"sourceCode bash\"><span id=\"cb2-1\"><a href=\"#cb2-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"ex\">cargo<\/span> new <span class=\"at\">--lib<\/span> shipping-rust-ffi<\/span><\/code><\/pre>\n<\/div>\n<p>We will need a static library, so we change the crate type in the generated <code>shipping-rust-ffi\/Cargo.toml<\/code>. Add the following below the package configuration:<\/p>\n<div class=\"sourceCode\" id=\"cb3\">\n<pre class=\"sourceCode toml\"><code class=\"sourceCode toml\"><span id=\"cb3-1\"><a href=\"#cb3-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"kw\">[<\/span><span class=\"dt\">lib<\/span><span class=\"kw\">]<\/span><\/span>\r\n<span id=\"cb3-2\"><a href=\"#cb3-2\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"dt\">crate-type<\/span> <span class=\"op\">=<\/span> <span class=\"op\">[<\/span><span class=\"st\">&quot;staticlib&quot;<\/span><span class=\"op\">]<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>Let\u2019s also turn the project into a Cargo workspace. Create a new top-level <code>Cargo.toml<\/code> with the content:<\/p>\n<div class=\"sourceCode\" id=\"cb4\">\n<pre class=\"sourceCode toml\"><code class=\"sourceCode toml\"><span id=\"cb4-1\"><a href=\"#cb4-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"kw\">[<\/span><span class=\"dt\">workspace<\/span><span class=\"kw\">]<\/span><\/span>\r\n<span id=\"cb4-2\"><a href=\"#cb4-2\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"dt\">members<\/span> <span class=\"op\">=<\/span> <span class=\"op\">[<\/span><\/span>\r\n<span id=\"cb4-3\"><a href=\"#cb4-3\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"st\">&quot;shipping-rust-ffi&quot;<\/span><\/span>\r\n<span id=\"cb4-4\"><a href=\"#cb4-4\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"op\">]<\/span><\/span><\/code><\/pre>\n<\/div>\n<p><code>cargo build<\/code> in the project directory should work now and create a new static library.<\/p>\n<div class=\"sourceCode\" id=\"cb5\">\n<pre class=\"sourceCode sh\"><code class=\"sourceCode bash\"><span id=\"cb5-1\"><a href=\"#cb5-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"ex\">$<\/span> ls <span class=\"at\">-l<\/span> target\/debug\/libshipping_rust_ffi.a<\/span>\r\n<span id=\"cb5-2\"><a href=\"#cb5-2\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"ex\">-rw-r--r--<\/span> 2 jer staff 16061952 Jan 28 13:09 target\/debug\/libshipping_rust_ffi.a<\/span><\/code><\/pre>\n<\/div>\n<p>Let\u2019s add some code to <code>shipping-rust-ffi\/src\/lib.rs<\/code> next. Nothing fancy, a simple function taking some arguments and returning the sum:<\/p>\n<div class=\"sourceCode\" id=\"cb6\">\n<pre class=\"sourceCode rust\"><code class=\"sourceCode rust\"><span id=\"cb6-1\"><a href=\"#cb6-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"kw\">use<\/span> <span class=\"pp\">std::os::raw::<\/span><span class=\"dt\">c_int<\/span><span class=\"op\">;<\/span><\/span>\r\n<span id=\"cb6-2\"><a href=\"#cb6-2\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb6-3\"><a href=\"#cb6-3\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"at\">#[<\/span>no_mangle<span class=\"at\">]<\/span><\/span>\r\n<span id=\"cb6-4\"><a href=\"#cb6-4\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"kw\">pub<\/span> <span class=\"kw\">extern<\/span> <span class=\"st\">&quot;C&quot;<\/span> <span class=\"kw\">fn<\/span> shipping_rust_addition(a<span class=\"op\">:<\/span> <span class=\"dt\">c_int<\/span><span class=\"op\">,<\/span> b<span class=\"op\">:<\/span> <span class=\"dt\">c_int<\/span>) <span class=\"op\">-&gt;<\/span> <span class=\"dt\">c_int<\/span> <span class=\"op\">{<\/span><\/span>\r\n<span id=\"cb6-5\"><a href=\"#cb6-5\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>    a <span class=\"op\">+<\/span> b<\/span>\r\n<span id=\"cb6-6\"><a href=\"#cb6-6\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"op\">}<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>The <code>no_mangle<\/code> ensures the name lands in the compiled library as-is and the <code>extern \"C\"<\/code> makes sure it uses the right ABI.<\/p>\n<p>We now have a Rust library exporting a C-ABI compatible interface. We can now consume this in our iOS application.<\/p>\n<h2 id=\"the-xcode-parts\">The Xcode parts<\/h2>\n<p>Before we can use the code we need a bit more setup. Strap in, there\u2019s a lot of fiddly manual steps now<a href=\"#fn1\" class=\"footnote-ref\" id=\"fnref1\" role=\"doc-noteref\"><sup>1<\/sup><\/a>.<\/p>\n<p>We start by linking against the <code>libshipping_rust_ffi.a<\/code> library. In your Xcode project open your target configuration<a href=\"#fn2\" class=\"footnote-ref\" id=\"fnref2\" role=\"doc-noteref\"><sup>2<\/sup><\/a>, go to \u201cBuild Phases\u201d, then look for \u201cLink Binary with Libraries\u201d. Add a new one, in the popup select \u201cAdd files\u201d on the bottom left and look for the <code>target\/debug\/libshipping_rust_ffi.a<\/code> file. Yes, that\u2019s actually for the wrong target. This is just for the name, we\u2019ll fix up the path next. Go to \u201cBuild Settings\u201d and search for \u201cLibrary Search Paths\u201d. It probably has the path to file in there right now for both <code>Debug<\/code> and <code>Release<\/code> builds. Remove that one for <code>Debug<\/code>, then add a new row by clicking the small <code>+<\/code> symbol. Select the <code>Any Driverkit<\/code> matcher. It doesn\u2019t matter which matcher you choose or what value you give it, but when we overwrite this manually in the next step I\u2019ll assume you chose <code>Any Driverkit<\/code>. Do the same for the <code>Release<\/code> configuration.<\/p>\n<p>Once that\u2019s done, save your project and go back to your project directory. We will modify the project configuration to have Xcode look for the library based on the target it is building for<a href=\"#fn3\" class=\"footnote-ref\" id=\"fnref3\" role=\"doc-noteref\"><sup>3<\/sup><\/a>. Open up <code>ShippingRust.xcodeproj\/project.pbxproj<\/code> in a text editor, then search for the first line with <code>\"LIBRARY_SEARCH_PATHS[sdk=driverkit*]\"<\/code>. It should be in a section saying <code>\/* Debug *\/<\/code>. Remove the <code>LIBRARY_SEARCH_PATHS<\/code> line and add 3 new ones:<\/p>\n<pre><code>&quot;LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]&quot; = &quot;$(PROJECT_DIR)\/target\/aarch64-apple-ios\/debug&quot;;\r\n&quot;LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]&quot; = &quot;$(PROJECT_DIR)\/target\/aarch64-apple-ios-sim\/debug&quot;;\r\n&quot;LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]&quot; = &quot;$(PROJECT_DIR)\/target\/x86_64-apple-ios\/debug&quot;;<\/code><\/pre>\n<p>Look for the next line with <code>\"LIBRARY_SEARCH_PATHS[sdk=driverkit*]\"<\/code>, now in a <code>\/* Release *\/<\/code> section and replace it with:<\/p>\n<pre><code>&quot;LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]&quot; = &quot;$(PROJECT_DIR)\/target\/aarch64-apple-ios\/release&quot;;\r\n&quot;LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]&quot; = &quot;$(PROJECT_DIR)\/target\/aarch64-apple-ios-sim\/release&quot;;\r\n&quot;LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]&quot; = &quot;$(PROJECT_DIR)\/target\/x86_64-apple-ios\/release&quot;;<\/code><\/pre>\n<p>Save the file and return focus back to Xcode. If you didn\u2019t make any typos Xcode should still have your project open. In the settings you will find the library search paths as we\u2019ve just defined them. If you messed something up Xcode will complain that it cannot read the project file if you try to go to the settings.<\/p>\n<p>Next we need to teach Xcode how to compile Rust code. Once again go to your target settings, selecting the \u201cBuild Phases\u201d tab again.<\/p>\n<p>There add a new \u201cRun Script\u201d phase, give it the name \u201cBuild Rust library\u201d (double-click the \u201cRun Script\u201d section header), and set the command to:<\/p>\n<div class=\"sourceCode\" id=\"cb9\">\n<pre class=\"sourceCode sh\"><code class=\"sourceCode bash\"><span id=\"cb9-1\"><a href=\"#cb9-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"fu\">bash<\/span> <span class=\"va\">${PROJECT_DIR}<\/span>\/bin\/compile-library.sh shipping-rust-ffi <span class=\"va\">$buildvariant<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>The <code>compile-library.sh<\/code> script is going to do the heavy lifting. The first argument is the crate name we want to compile, the second is the build variant to select. This is not yet defined, so let\u2019s do it first.<\/p>\n<p>Go to the \u201cBuild Settings\u201d tab and click the <code>+<\/code> button to add a new \u201cUser-Defined Setting\u201d. Give it the name <code>buildvariant<\/code> and choose a value based on the build variant: <code>debug<\/code> for <code>Debug<\/code> and <code>release<\/code> for <code>Release<\/code>.<\/p>\n<p>Now we need the actual script to build the Rust library for the right targets. It\u2019s a bit long to write out, but the logic is not too complex: First we select the Cargo profile to use based on our <code>buildvariant<\/code> (that is whether to pass <code>--release<\/code> or not), then we set up <code>LIBRARY_PATH<\/code> if necessary and finally compile the Rust library for the selected target. Xcode passes the architectures to build in <code>ARCHS<\/code>. It\u2019s either <code>x86_64<\/code> for simulator builds on Intel Mac hardware or <code>arm64<\/code>. If it\u2019s <code>arm64<\/code> it can be either the simulator or an actual hardware target. Those differ, but we can know which is which from what\u2019s in <code>LLVM_TARGET_TRIPLE_SUFFIX<\/code> and select the right Rust target.<\/p>\n<p>Let\u2019s put all of that into a <code>compile-library.sh<\/code> script. Create a new directory <code>bin<\/code> in your project directory. In there create the file with the following content:<\/p>\n<div class=\"sourceCode\" id=\"cb10\">\n<pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb10-1\"><a href=\"#cb10-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"co\">#!\/usr\/bin\/env bash<\/span><\/span>\r\n<span id=\"cb10-2\"><a href=\"#cb10-2\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-3\"><a href=\"#cb10-3\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">if<\/span> <span class=\"bu\">[<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$#<\/span><span class=\"st\">&quot;<\/span> <span class=\"ot\">-ne<\/span> 2 <span class=\"bu\">]<\/span><\/span>\r\n<span id=\"cb10-4\"><a href=\"#cb10-4\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">then<\/span><\/span>\r\n<span id=\"cb10-5\"><a href=\"#cb10-5\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>    <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Usage (note: only call inside xcode!):&quot;<\/span><\/span>\r\n<span id=\"cb10-6\"><a href=\"#cb10-6\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>    <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;compile-library.sh &lt;FFI_TARGET&gt; &lt;buildvariant&gt;&quot;<\/span><\/span>\r\n<span id=\"cb10-7\"><a href=\"#cb10-7\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>    <span class=\"bu\">exit<\/span> 1<\/span>\r\n<span id=\"cb10-8\"><a href=\"#cb10-8\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">fi<\/span><\/span>\r\n<span id=\"cb10-9\"><a href=\"#cb10-9\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-10\"><a href=\"#cb10-10\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"co\"># what to pass to cargo build -p, e.g. your_lib_ffi<\/span><\/span>\r\n<span id=\"cb10-11\"><a href=\"#cb10-11\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"va\">FFI_TARGET=$1<\/span><\/span>\r\n<span id=\"cb10-12\"><a href=\"#cb10-12\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"co\"># buildvariant from our xcconfigs<\/span><\/span>\r\n<span id=\"cb10-13\"><a href=\"#cb10-13\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"va\">BUILDVARIANT=$2<\/span><\/span>\r\n<span id=\"cb10-14\"><a href=\"#cb10-14\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-15\"><a href=\"#cb10-15\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"va\">RELFLAG=<\/span><\/span>\r\n<span id=\"cb10-16\"><a href=\"#cb10-16\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">if<\/span> <span class=\"kw\">[[<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$BUILDVARIANT<\/span><span class=\"st\">&quot;<\/span> <span class=\"ot\">!=<\/span> <span class=\"st\">&quot;debug&quot;<\/span> <span class=\"kw\">]];<\/span> <span class=\"cf\">then<\/span><\/span>\r\n<span id=\"cb10-17\"><a href=\"#cb10-17\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"va\">RELFLAG=<\/span>--release<\/span>\r\n<span id=\"cb10-18\"><a href=\"#cb10-18\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">fi<\/span><\/span>\r\n<span id=\"cb10-19\"><a href=\"#cb10-19\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-20\"><a href=\"#cb10-20\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"bu\">set<\/span> <span class=\"at\">-euvx<\/span><\/span>\r\n<span id=\"cb10-21\"><a href=\"#cb10-21\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-22\"><a href=\"#cb10-22\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">if<\/span> <span class=\"kw\">[[<\/span> <span class=\"ot\">-n<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">${DEVELOPER_SDK_DIR<\/span><span class=\"op\">:-<\/span><span class=\"va\">}<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">]];<\/span> <span class=\"cf\">then<\/span><\/span>\r\n<span id=\"cb10-23\"><a href=\"#cb10-23\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"co\"># Assume we&#39;re in Xcode, which means we&#39;re probably cross-compiling.<\/span><\/span>\r\n<span id=\"cb10-24\"><a href=\"#cb10-24\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"co\"># In this case, we need to add an extra library search path for build scripts and proc-macros,<\/span><\/span>\r\n<span id=\"cb10-25\"><a href=\"#cb10-25\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"co\"># which run on the host instead of the target.<\/span><\/span>\r\n<span id=\"cb10-26\"><a href=\"#cb10-26\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"co\"># (macOS Big Sur does not have linkable libraries in \/usr\/lib\/.)<\/span><\/span>\r\n<span id=\"cb10-27\"><a href=\"#cb10-27\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"bu\">export<\/span> <span class=\"va\">LIBRARY_PATH=<\/span><span class=\"st\">&quot;<\/span><span class=\"va\">${DEVELOPER_SDK_DIR}<\/span><span class=\"st\">\/MacOSX.sdk\/usr\/lib:<\/span><span class=\"va\">${LIBRARY_PATH<\/span><span class=\"op\">:-<\/span><span class=\"va\">}<\/span><span class=\"st\">&quot;<\/span><\/span>\r\n<span id=\"cb10-28\"><a href=\"#cb10-28\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">fi<\/span><\/span>\r\n<span id=\"cb10-29\"><a href=\"#cb10-29\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-30\"><a href=\"#cb10-30\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"va\">IS_SIMULATOR=<\/span>0<\/span>\r\n<span id=\"cb10-31\"><a href=\"#cb10-31\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">if<\/span> <span class=\"bu\">[<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">${LLVM_TARGET_TRIPLE_SUFFIX<\/span><span class=\"er\">-<\/span><span class=\"va\">}<\/span><span class=\"st\">&quot;<\/span> <span class=\"ot\">=<\/span> <span class=\"st\">&quot;-simulator&quot;<\/span> <span class=\"bu\">]<\/span><span class=\"kw\">;<\/span> <span class=\"cf\">then<\/span><\/span>\r\n<span id=\"cb10-32\"><a href=\"#cb10-32\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"va\">IS_SIMULATOR=<\/span>1<\/span>\r\n<span id=\"cb10-33\"><a href=\"#cb10-33\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">fi<\/span><\/span>\r\n<span id=\"cb10-34\"><a href=\"#cb10-34\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-35\"><a href=\"#cb10-35\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">for<\/span> arch <span class=\"kw\">in<\/span> <span class=\"va\">$ARCHS<\/span><span class=\"kw\">;<\/span> <span class=\"cf\">do<\/span><\/span>\r\n<span id=\"cb10-36\"><a href=\"#cb10-36\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"cf\">case<\/span> <span class=\"st\">&quot;<\/span><span class=\"va\">$arch<\/span><span class=\"st\">&quot;<\/span> <span class=\"kw\">in<\/span><\/span>\r\n<span id=\"cb10-37\"><a href=\"#cb10-37\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>    x86_64<span class=\"kw\">)<\/span><\/span>\r\n<span id=\"cb10-38\"><a href=\"#cb10-38\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"cf\">if<\/span> <span class=\"bu\">[<\/span> <span class=\"va\">$IS_SIMULATOR<\/span> <span class=\"ot\">-eq<\/span> 0 <span class=\"bu\">]<\/span><span class=\"kw\">;<\/span> <span class=\"cf\">then<\/span><\/span>\r\n<span id=\"cb10-39\"><a href=\"#cb10-39\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>        <span class=\"bu\">echo<\/span> <span class=\"st\">&quot;Building for x86_64, but not a simulator build. What&#39;s going on?&quot;<\/span> <span class=\"op\">&gt;&amp;<\/span><span class=\"dv\">2<\/span><\/span>\r\n<span id=\"cb10-40\"><a href=\"#cb10-40\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>        <span class=\"bu\">exit<\/span> 2<\/span>\r\n<span id=\"cb10-41\"><a href=\"#cb10-41\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"cf\">fi<\/span><\/span>\r\n<span id=\"cb10-42\"><a href=\"#cb10-42\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-43\"><a href=\"#cb10-43\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"co\"># Intel iOS simulator<\/span><\/span>\r\n<span id=\"cb10-44\"><a href=\"#cb10-44\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"bu\">export<\/span> <span class=\"va\">CFLAGS_x86_64_apple_ios=<\/span><span class=\"st\">&quot;-target x86_64-apple-ios&quot;<\/span><\/span>\r\n<span id=\"cb10-45\"><a href=\"#cb10-45\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"va\">$HOME<\/span><span class=\"ex\">\/.cargo\/bin\/cargo<\/span> build <span class=\"at\">-p<\/span> <span class=\"va\">$FFI_TARGET<\/span> <span class=\"at\">--lib<\/span> <span class=\"va\">$RELFLAG<\/span> <span class=\"at\">--target<\/span> x86_64-apple-ios<\/span>\r\n<span id=\"cb10-46\"><a href=\"#cb10-46\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"cf\">;;<\/span><\/span>\r\n<span id=\"cb10-47\"><a href=\"#cb10-47\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb10-48\"><a href=\"#cb10-48\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>    arm64<span class=\"kw\">)<\/span><\/span>\r\n<span id=\"cb10-49\"><a href=\"#cb10-49\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"cf\">if<\/span> <span class=\"bu\">[<\/span> <span class=\"va\">$IS_SIMULATOR<\/span> <span class=\"ot\">-eq<\/span> 0 <span class=\"bu\">]<\/span><span class=\"kw\">;<\/span> <span class=\"cf\">then<\/span><\/span>\r\n<span id=\"cb10-50\"><a href=\"#cb10-50\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>        <span class=\"co\"># Hardware iOS targets<\/span><\/span>\r\n<span id=\"cb10-51\"><a href=\"#cb10-51\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>        <span class=\"va\">$HOME<\/span><span class=\"ex\">\/.cargo\/bin\/cargo<\/span> build <span class=\"at\">-p<\/span> <span class=\"va\">$FFI_TARGET<\/span> <span class=\"at\">--lib<\/span> <span class=\"va\">$RELFLAG<\/span> <span class=\"at\">--target<\/span> aarch64-apple-ios<\/span>\r\n<span id=\"cb10-52\"><a href=\"#cb10-52\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"cf\">else<\/span><\/span>\r\n<span id=\"cb10-53\"><a href=\"#cb10-53\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>        <span class=\"va\">$HOME<\/span><span class=\"ex\">\/.cargo\/bin\/cargo<\/span> build <span class=\"at\">-p<\/span> <span class=\"va\">$FFI_TARGET<\/span> <span class=\"at\">--lib<\/span> <span class=\"va\">$RELFLAG<\/span> <span class=\"at\">--target<\/span> aarch64-apple-ios-sim<\/span>\r\n<span id=\"cb10-54\"><a href=\"#cb10-54\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>      <span class=\"cf\">fi<\/span><\/span>\r\n<span id=\"cb10-55\"><a href=\"#cb10-55\" aria-hidden=\"true\" tabindex=\"-1\"><\/a>  <span class=\"cf\">esac<\/span><\/span>\r\n<span id=\"cb10-56\"><a href=\"#cb10-56\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"cf\">done<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>And now we\u2019re done with the setup for compiling the Rust library automatically as part of the Xcode project build.<\/p>\n<h2 id=\"the-code-parts\">The code parts<\/h2>\n<p>We now have an Xcode project that builds our Rust library and links against it. We now need to use this library!<\/p>\n<p>Swift speaks Objective-C, which is an extension to C, but we need to tell it about the things available. In C land that\u2019s done with a header. Let\u2019s create a new file, select the \u201cHeader File\u201d template and name it <code>FfiBridge.h<\/code>. This will create a new file with this content:<\/p>\n<div class=\"sourceCode\" id=\"cb11\">\n<pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"pp\">#ifndef FfiBridge_h<\/span><\/span>\r\n<span id=\"cb11-2\"><a href=\"#cb11-2\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"pp\">#define FfiBridge_h<\/span><\/span>\r\n<span id=\"cb11-3\"><a href=\"#cb11-3\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb11-4\"><a href=\"#cb11-4\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><\/span>\r\n<span id=\"cb11-5\"><a href=\"#cb11-5\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"pp\">#endif <\/span><span class=\"co\">\/* FfiBridge_h *\/<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>Here we need to add the definition of our function. As a reminder this is its definition in Rust:<\/p>\n<div class=\"sourceCode\" id=\"cb12\">\n<pre class=\"sourceCode rust\"><code class=\"sourceCode rust\"><span id=\"cb12-1\"><a href=\"#cb12-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"kw\">extern<\/span> <span class=\"st\">&quot;C&quot;<\/span> <span class=\"kw\">fn<\/span> shipping_rust_addition(a<span class=\"op\">:<\/span> <span class=\"dt\">c_int<\/span><span class=\"op\">,<\/span> b<span class=\"op\">:<\/span> <span class=\"dt\">c_int<\/span>) <span class=\"op\">-&gt;<\/span> <span class=\"dt\">c_int<\/span><span class=\"op\">;<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>This translates to the following in C:<\/p>\n<div class=\"sourceCode\" id=\"cb13\">\n<pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb13-1\"><a href=\"#cb13-1\" aria-hidden=\"true\" tabindex=\"-1\"><\/a><span class=\"dt\">int<\/span> shipping_rust_addition(<span class=\"dt\">int<\/span> a, <span class=\"dt\">int<\/span> b);<\/span><\/code><\/pre>\n<\/div>\n<p>Add that line between the <code>#define<\/code> and <code>#endif<\/code> lines. Xcode doesn\u2019t know about that file yet, so once more into the <code>Build Settings<\/code> of the target. Search for <code>Objective-C Bridging Header<\/code> and set it to <code>$(PROJECT_DIR)\/ShippingRust\/FfiBridge.h<\/code>. In <code>Build Phases<\/code> add a new <code>Header Phase<\/code>. There you add the <code>FfiBridge.h<\/code> as well.<\/p>\n<p>If it now all compiles we\u2019re finally ready to use our Rust library.<\/p>\n<p>Open up <code>ContentView.swift<\/code> and change the code to call your Rust library:<\/p>\n<pre class=\"swift\"><code>struct ContentView: View {\r\n    var body: some View {\r\n        Text(&quot;Hello, world! \\(shipping_rust_addition(30, 1))&quot;)\r\n            .padding()\r\n    }\r\n}<\/code><\/pre>\n<p>We simply interpolate the result of <code>shipping_rust_addition(30, 1)<\/code> into the string displayed.<\/p>\n<p>Once we compile and run it in the simulator we see we\u2019ve succeeded at satisfying our premise:<\/p>\n<blockquote>\n<p>Show a non-interactive message to the user with data from a Rust library.<\/p>\n<\/blockquote>\n<figure>\n<img decoding=\"async\" src=\"https:\/\/tmp.fnordig.de\/blog\/2022\/ios-simulator-helloworld31.png\" alt=\"iOS simulator running our application showing \u201cHello, world! 31\u201d\" \/><figcaption aria-hidden=\"true\">iOS simulator running our application showing \u201cHello, world! 31\u201d<\/figcaption><\/figure>\n<p>Compiling for any iOS device should work just as well.<\/p>\n<h2 id=\"the-next-steps\">The next steps<\/h2>\n<p>This was a lot of setup for calling one simple function. Luckily this is a one-time setup. From here on you can extend your Rust library, define them in the header file and call them from Swift. If you go that route you should really start using <a href=\"https:\/\/github.com\/eqrion\/cbindgen\">cbindgen<\/a> to generate that header file automatically for you.<\/p>\n<p>This time we looked at building an iOS application directly calling a Rust library. That\u2019s not actually how Glean works. The Glean Swift SDK itself wraps the Rust library and exposes a Swift library. In a next blog post I\u2019ll showcase how we ship that as a Swift package.<\/p>\n<p>For Glean we\u2019re stepping away from manually writing our FFI functions. We\u2019re instead migrating our code base to use <a href=\"https:\/\/github.com\/mozilla\/uniffi-rs\/\">UniFFI<\/a>. UniFFI will generate the C API from an API definitions file and also comes with a bit of runtime code to handle conversion between Rust, C and Swift data types for us. We\u2019re not there yet for Glean, but you can try it on your own. <a href=\"https:\/\/mozilla.github.io\/uniffi-rs\/\">Read the UniFFI documentation<\/a> and integrate it into your project. It should be possible to extent the setup we done her to also run the necessary steps for UniFFI. Eventually I\u2019ll document how we did it as well.<\/p>\n<hr \/>\n<p><em>Footnotes:<\/em><\/p>\n<section class=\"footnotes\" role=\"doc-endnotes\">\n<hr \/>\n<ol>\n<li id=\"fn1\" role=\"doc-endnote\">\n<p>And most of these steps are user-interface-dependent and might be different in future Xcode version. \ud83d\ude41<a href=\"#fnref1\" class=\"footnote-back\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn2\" role=\"doc-endnote\">\n<p>Click your project name in the tree view on the left. This gets you to the project configuration (backed by the <code>ShippingRust.xcodeproj\/project.pbxproj<\/code> file). You should then see the Targets, including your <code>ShippingRust<\/code> target and probably <code>ShippingRustTests<\/code> as well. We need the former.<a href=\"#fnref2\" class=\"footnote-back\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn3\" role=\"doc-endnote\">\n<p>Previously we would have built a universal library containing the library of multiple targets. That doesn\u2019t work anymore now that <code>arm64<\/code> can stand for both the simulator and hardware targets. Thus linking to the individual libraries is the way to go, as the now-deprecated <a href=\"https:\/\/github.com\/TimNN\/cargo-lipo\"><code>cargo-lipo<\/code><\/a> also points out.<a href=\"#fnref3\" class=\"footnote-back\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<\/ol>\n<\/section>\n","protected":false},"excerpt":{"rendered":"<p>(\u201cThis Week in Glean\u201d is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/data\/2022\/01\/31\/this-week-in-glean-building-and-deploying-a-rust-library-on-ios\/\">Read more<\/a><\/p>\n","protected":false},"author":1756,"featured_media":197,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[448297],"tags":[],"coauthors":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts\/389"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/users\/1756"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/comments?post=389"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/posts\/389\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/media\/197"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/media?parent=389"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/categories?post=389"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/tags?post=389"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/data\/wp-json\/wp\/v2\/coauthors?post=389"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}