How to build VCX iOS library 
Tags: ios, vcx, indy
Published: 2020-01-31 21:50:49

---
title: How to build VCX iOS library 
published: true
description: 
tags: #ios #vcx #indy
---

VCX is part of Hyperledger’s [Indy](https://www.hyperledger.org/projects/hyperledger-indy) and [Aries](https://www.hyperledger.org/projects/aries) projects. It’s a library for exchange of verifiable credentials written in Rust. It has also c-callable API with wrappers for iOS, Java, Node and other languages.
Our team needed to build the iOS static library from scratch and I decided to write down some notes from the process. The description of the build process is not only a good help for those who need the same, but it’s also good for understanding the build process itself which can be useful for any other library.

I won’t go into details describing all commands, I just want to provide a high-level description. If you want to see specifics, go to this repo [jakubkoci/indy-vcx-build](https://github.com/jakubkoci/indy-vcx-build) with the final Bash script or look at these two links which were my inspiration (thank you to all authors for their contribution):
* [indy-sdk/README.txt at master · hyperledger/indy-sdk · GitHub](https://github.com/hyperledger/indy-sdk/blob/master/vcx/libvcx/build_scripts/ios/mac/README.txt)
* [indy-sdk-android/android.build.sh at master · faisal00813/indy-sdk-android · GitHub](https://github.com/faisal00813/indy-sdk-android/blob/master/android.build.sh)

## Prerequisities
You need to run a build script on Mac OS with Rust and some other libraries installed. You will find everything described in the [setup.sh](https://github.com/jakubkoci/indy-vcx-build/blob/master/setup.sh) script.

## Goal
At the end of the process, we want to have a `vcx.framework` folder containing a binary file compiled for all required architectures together. This binary file will be combined from `libvcx` library and all its dependencies `libindy`, `libssl`, `libcrypto`, `libsodium` and `libzmq`.

## Architectures
Whenever you want to build a native library, you need to select the architecture a.k.a type of processor of a device where your app, and therefore library, will be running. There are usually following options for iOS:

* `arm64` (`aarch64-apple-ios`): 64-bit ARM processor in iPhone 5s, iPad Air and newer.
* `armv7s` (`armv7s-apple-ios`): 32-bit ARM processor in iPhone 5.
* `armv7` (`armv7-apple-ios`): 32-bit ARM processor in iPhone 3GS, 4, 4S.
* `x86_64` (`x86_64-apple-ios`): 64-bit simulator.
* `i386` (`i386-apple-ios`): 32-bit simulator.

In my case, OpenSSL library supports only two architectures, `arm64` for devices and `x86_64` for a simulator, so I need to build libraries only for these two.

You can find more about architectures here:
* [Blake's iOS Device Specification Grid](https://blakespot.com/ios_device_specifications_grid.html)
* [About ARM architectures for iOS development – Stanley SIU’s Blog](https://ktatsiu.wordpress.com/2016/11/15/about-arm-architectures-for-ios-development/)
* [CPU Architectures](https://docs.elementscompiler.com/Platforms/Cocoa/CpuArchitectures/)

## Build Native Libraries
For every 3rd party library I checkout particular repo and run a build script within it:

### OpenSSL
* clone https://github.com/x2on/OpenSSL-for-iPhone
* run `./build-libssl.sh` which produces both `libssl.a` and `libcrypto.a`

### Libsodium
* clone https://github.com/evernym/libsodium-ios 
* run `./libsodium.rb` which produces `libsodium.a`

### ZMQ
* clone https://github.com/evernym/libzmq-ios 
* run `./libzmq.rb` -> which produces `libzmq.a`

### Libindy
I take similar steps with `libindy`, but on top of cloning the repo, I also checkout the required version by tag:
```shell
$ git clone git clone https://github.com/hyperledger/indy-sdk
$ cd indy-sdk/libindy
$ git checkout $INDY_VERSION
$ cargo lipo —release —targets="aarch64-apple-ios,x86_64-apple-ios”
```

You can spot that instead of architectures `arm64` or `x86_64` I use `aarch64-apple-ios, x86_64-apple-ios`. I call them triplets. Some libraries use the first architecture format, some use triplets, but you can see the mapping between architectures and triplets in the Architectures section above. 

You can find more about architectures here:
* [Blake's iOS Device Specification Grid](https://blakespot.com/ios_device_specifications_grid.html)
* [About ARM architectures for iOS development – Stanley SIU’s Blog](https://ktatsiu.wordpress.com/2016/11/15/about-arm-architectures-for-ios-development/)
* [CPU Architectures](https://docs.elementscompiler.com/Platforms/Cocoa/CpuArchitectures/)

### Lipo
[Lipo](https://ss64.com/osx/lipo.html) is a command-line tool that allows us to create universal binary with more architectures in one file (called Universal Static Library, or just “fat file”). We can also use it for extracting one universal file into more files according to given architecture (“non-fat file”).

If you check the output library with `lipo` you will see whether it’s a fat-file or not, and what architectures it contains.

For example, in our case:
```shell
$ lipo -info output/libsodium-ios/dist/ios/lib/libsodium.a
Architectures in the fat file: output/libsodium-ios/dist/ios/lib/libsodium.a are: armv7 armv7s i386 x86_64 arm64 
```

or, for `libindy`:
```shell
$ lipo -info output/indy-sdk/libindy/target/aarch64-apple-ios/release/libindy.a
Non-fat file: output/indy-sdk/libindy/target/aarch64-apple-ios/release/libindy.a is architecture: arm64
```

If you run this command for every library you’ll see that all 3rd party libraries are fat files with all architectures, but  `libindy` has been built as non-fat files. We need to extract architectures from fat-files to non-fat files:
```shell
$ lipo -extract ${arch} ../$FILE_PATH -o ${arch}/$FILE_NAME-fat.a
$ lipo ${arch}/$FILE_NAME-fat.a -thin $arch -output ${arch}/$FILE_NAME.a
```

You may be confused why there are two steps (extracting — keep output as a fat file with given architecture only, and thinning — create a non-fat file with given architecture). That’s because you need to extract architecture before you thin the file. If you try to thin the file directly, you will get the following error:
```
fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: input file (arm64/libzmq.a) must be a fat file when the -thin option is specified
```

### Libvcx
I also copied `libindy` from `indy-sdk` to a different directory. Extracting and copying helps us set environment variables which are required to build `libvcx` in the following way:
```shell
export OPENSSL_LIB_DIR=$WORK_DIR/libs/openssl/${ARCH}
export IOS_SODIUM_LIB=$WORK_DIR/libs/sodium/${ARCH}
export IOS_ZMQ_LIB=$WORK_DIR/libs/zmq/${ARCH}
export LIBINDY_DIR=$WORK_DIR/libs/indy/${ARCH}

cargo build —target “${TRIPLET}” --release --no-default-features —features “ci”
```

## Combine It All
Now, we have all libraries `libssl`, `libcrypto`, `libsodium`, `libzmq`, `libindy` and `libvcx` separated by architecture. What we want is one file with all libraries for all architectures. This part is too code heavy to describe it here. In general, we first combine all libraries for given platform and then create a fat file with all architectures with `lipo`. I prepared a simple drawing describing it:

![Alt Process of extracting and combining native libraries](https://thepracticaldev.s3.amazonaws.com/i/9xusrdcss3ulqaeoooej.png)

This still not over. To call output `libvcxall.a` library from our code we need to wrap it with Objective-C and c-callable headers. First, we copy the `libvcxall.a` to `indy-sdk/vcx/wrappers/ios/vcx` which contains these headers and then we use `xcodebuild` and `lipo` utility to create our final `vcx.framework`.

Yes, we’re finally done. I hope you find it helpful even if you don’t need to build the library by yourself, but were curious about the process.