Skip to content

Instantly share code, notes, and snippets.

@kitasuke
Last active December 27, 2016 04:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kitasuke/83cb153dd11d4b5727dae3d10142d590 to your computer and use it in GitHub Desktop.
Save kitasuke/83cb153dd11d4b5727dae3d10142d590 to your computer and use it in GitHub Desktop.

Type-safe API call with Protocol Buffers in Swift

Apple recently open sourced swift-protobuf which is a plugin of Protocol Buffers for swift language. Protocol Buffers in Swift enables us to have type safety, make API faster and unify schema documentation of structured data. I had a chance to use swift-protobuf in my project and thought that there are many benefits for us, so I would like to share my thoughts.

I also created a repository which has sample server/client app with Protocol Buffers. Please take a look here if you're interested in what implementation looks like.

What's Protocol Buffers?

Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

https://developers.google.com/protocol-buffers/

Protocol Buffers is developed by Google in 2008 and it's been used internally, but they released it as open source project.

https://en.wikipedia.org/wiki/Protocol_Buffers

It's also used in gRPC, it generates Objective-C source code though.

http://www.grpc.io/docs/quickstart/objective-c.html

Languages

Most of major languages are supported. For example C++, Go, Java, Python, Ruby, C#, Objective-C, Javascript, PHP and of course Swift. You can see more details here.

How it works

Schema of data structure should be defined with message type in .proto file, so that swift-protobuf can generate swift source code.
Here is an example of token.proto file.

message Token {
  string accessToken = 1;
}

// GET - Response
message GetTokenResponse {
  Token token = 1;
}

// POST - Request
message PostTokenRequest {
  string accessToken = 1;
}

// POST - Response
message PostTokenResponse {
  Token token = 1;
}

If you run protoc command, it compilies token.proto file and generates token.pb.swift file and you will see struct of Token which has all of properties in Token message you defined in .proto file. The Token conforms to SwiftProtobuf.Message protocol. It has serializeProtobuf(), serializeJSON(), init(protobuf:), init(json:) methods which allows you to serialize/deserialize value between Data and protobuf type.
Here is an example of token.pb.swift file.

struct  Token: SwiftProtobuf.Message {
    var accessToken: String = ""

    init() {}
}

struct  GetTokenResponse: SwiftProtobuf.Message {
    init() {}

    var token:  Token {...}
}

struct  PostTokenRequest: SwiftProtobuf.Message {
    var accessToken: String = ""

    init() {}
}

struct  PostTokenResponse: SwiftProtobuf.Message {
    init() {}

    var token:  Token {...}
}

Also there are HTTP request and response type for API call along with Token sturct. You can simply serialize PostTokenRequest type by serializeProtobuf() method and set it to request body or deserialize GetTokenResponse or PostTokenResponse by init(protobuf:) method. Every request or response has their type. It's type safety works well in Swift!

Basic Type

Proto type Swift type
int32 Int32
sint32 Int32
sfixed32 Int32
uint32 UInt32
fixed32 UInt32
int64 Int64
sint64 Int64
sfixed64 Int64
uint64 UInt64
fixed64 UInt64
bool Bool
float Float
double Double
string String
bytes Data

See more detailed info here

Customization

You can set namespace with package specifier, set access control of struct and change property name for specific language.

Pros

Unified definition

The only definition you'll need is .proto file. It generates same output for each language, so it won't happen that iOS and Android unexpectedly has type mismatch of property. There also won't be any confusion from undocumented information of schema.

If you use protoc-gen-doc plugin, you can generate HTML or Markdown documentations from your comments in .proto file.

Type safety

You can write type-safe implementation for API call since every single request and response has their type. You can also express state elegantly with enum. Type safety enables us to type check at compile time so that it can save some amount of time to solve unexpected issues during runtime.

Serializing and deserializing methods are already implemented in swift-protobuf since they know what binary format is, so mapping your struct between data and protobuf type works like a charm.

To call API with Protocol Buffers, you set application/protbuf to Content-Type and Accept in HTTP headers and set serialized data to HTTP body. That's it! It's much simpler with Protocol Buffers. You don't need object mapping library or network layer library any more.

Faster API

Binary data size gets smaller with awesome binary format mechanism so that API request and response gets faster. Encoding and Decoding are also lightweight compare to JSON.

Please see more detailed info here

Versioning

Versioning handles backward compatibility well. If you add new field in server side, it will be treated as optional value in client side. However, if you change type of existing field or numbered field, it'll crash your app. Make sure that you should be careful about them.

Cons

Human readability

Binary data is not key value like JSON, so it's hard to understand at a glance. For that case, you can print log as String by using String.init(data:encoding:), or accept JSON instead for debug usage only.

Stability

swift-protobuf is still pre-released version, so it has breaking changes a lot. Make sure that you watch some activities on their repository or you can contribute to swift-protobuf since it's open source.

Plugin's Swift

It's likely to happen compile errors because of version conflict of Swift between your app and swift-protobuf plugin. It's good to know during compile at least, but you have to choose appropriate version of plugin in terms of Swift usage. This issue is not specific one for swift-protobuf. It's more like a general issue for auto-generate tool, but you should pay extra attention.

Web browser

Javascript seems like that it's not good at handling binary data, so it might be better to use JSON for some case.

Summary

I guess it's too early to evaluate swift-protobuf yet, but I believe that it has so many benefits and potentials, especially type safety. You can prevent any future mistakes at compile time, not runtime. It will be best fit to Swift. It's definitely worth a try!

If you're interested in more details, take a look at sample app here as well.

Reference

https://developers.google.com/protocol-buffers/
https://github.com/apple/swift-protobuf
https://github.com/google/protobuf
http://www.grpc.io/docs/
https://github.com/estan/protoc-gen-doc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment