ProtocolBuffers-Swift 4.0.6

ProtocolBuffers-Swift 4.0.6

测试已测试
语言语言 SwiftSwift
许可证 Apache-2.0
发布上一个版本2018年9月
SPM支持 SPM

Alexey Khokhlov 维护。



  • 作者
  • Alexey Khokhlov

Protocol Buffers for Swift

Build Status Carthage compatible Version Platform

Swift 中的 Protocol Buffers 实现。

Protocol Buffers 是一种以高效且可扩展的格式编码结构化数据的方式。该项目基于 Google 中的 Protocol Buffers 实现。有关更多信息,请参阅 Google protobuf 项目

需要 Protocol Buffers 3.0

如何在 Linux(Ubuntu 14.04)上安装 Protobuf 编译器

1.wget https://github.com/google/protobuf/archive/v3.2.0.tar.gz

tar xzf v3.2.0.tar.gz

cd protobuf-3.2.0/

sudo apt-get install autoreconf automake libtool make

./autogen.sh

./configure CXXFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib

sudo make && sudo make install

cd .. && wget https://github.com/alexeyxo/protobuf-swift/archive/3.0.9.tar.gz && tar xzf 3.0.9.tar.gz && cd protobuf-swift-3.0.9

./script/build.sh && swift build

如何在 Homebrew 中安装 Protobuf 编译器

1.ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2.brew install protobuf-swift

如何安装 Protobuf 编译器

1.ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2.brew install automake

3.brew install libtool

4.brew install protobuf

5.git clone [email protected]:alexeyxo/protobuf-swift.git

6../scripts/build.sh

在您的项目中添加 ./src/ProtocolBuffers/ProtocolBuffers.xcodeproj

CocoaPods

Podfile

use_frameworks!
pod 'ProtocolBuffers-Swift'

通过 Carthage 安装

Cartfile

github "alexeyxo/protobuf-swift"

编译 ".proto" 文件。

protoc  person.proto --swift_out="./"

序列化

syntax = "proto2";
message Person {
    required int32 id = 1;
    required string name = 2;
    optional string email = 3;
}
let personBuilder = Person.Builder()
personBuilder.id = 123
personBuilder.name = "Bob"
personBuilder.email = "[email protected]"
let person = try! personBuilder.build()
print(person)

person.data() //return NSData

链式操作

syntax = "proto2";
message Perfomance
{
  required int32 ints = 1;
  required int64 ints64 = 2;
  required double doubles = 3;
  required float floats  = 4;
  optional string str  = 5;
  optional bytes bytes  = 6;
  optional string description = 7;
}
var originalBuilder = ProtoPerfomance.Builder()
originalBuilder.setInts(Int32(32))
               .setInts64(Int64(64))
               .setDoubles(Double(12.12))
               .setFloats(Float(123.123))
               .setStr("string")
let original = originalBuilder.build()

子构建器

syntax = "proto2";
message Foo {
  optional int32 val = 1;
  // some other fields.
}

message Bar {
  optional Foo foo = 1;
  // some other fields.
}

message Baz {
  optional Bar bar = 1;
  // some other fields.
}
var builder = baz.toBuilder()
builder.getBarBuilder().getFooBuilder().setVal(10)
baz = builder.build()

地图(ProtocolBuffers 3.0)

syntax = "proto3";
message MapMessageValue
{
    int32 valueInMapMessage = 1;
}

message MessageContainsMap
{

  enum EnumMapValue
  {
    FirstValueEnum = 0;
    SecondValueEnum = 1;
  }

  map<int32,int32> map_int32_int32= 1;
  map<int64,int64> map_int64_int64= 2;
  map<string,string> map_string_string = 3;
  map<string,bytes> map_string_bytes = 4;
  map<string,MapMessageValue> map_string_message = 5;
  map<int32,EnumMapValue> map_int32_enum = 6;

}
final internal class MessageContainsMap : GeneratedMessage, GeneratedMessageProtocol, Hashable {
    ...
    private(set) var mapInt32Int32:Dictionary<Int32,Int32> = Dictionary<Int32,Int32>()
    private(set) var mapInt64Int64:Dictionary<Int64,Int64> = Dictionary<Int64,Int64>()

    private(set) var mapStringString:Dictionary<String,String> = Dictionary<String,String>()
    private(set) var mapStringBytes:Dictionary<String,NSData> = Dictionary<String,NSData>()
    private(set) var mapInt32Enum:Dictionary<Int32,MessageContainsMap.EnumMapValue> = Dictionary<Int32,MessageContainsMap.EnumMapValue>()
    ...
}

JSON(proto3)

let personBuilder = Person.builder()
personBuilder.id = 123
personBuilder.name = "Bob"
personBuilder.email = "[email protected]"
let person = personBuilder.build()
let jsonData = person.toJSON() //return NSData
let jsonDictionaryObject:Dictionary<String,AnyObject> = person.encode()
let personFromJson = Person.fromJSON(jsonData) //Person

反序列化

var person = Person.parseFromData(bytes) // from NSData

使用oneof

syntax = "proto3";
message SubMessage {
    string str = 1;
}

message SampleMessage {
  oneof test_oneof {
     string name = 4;
     int32 id = 5;
     SubMessage mes = 6;
  }
}
var sm = SampleMessage.Builder()
sm.name = "Alex"
sm.id = 123
println(ss.build()) //->  id: 123

嵌套类型

syntax = "proto3";
message SearchResponse {
    message Result {
        string url = 1;
        string title = 2;
        repeated string snippets = 3;
    }
    repeated Result result = 1;
}
var builderResult = SearchResponse.Result.Builder()
builderResult.url = "http://protobuf.axo.io"
builderResult.title = "Protocol Bufers Apple Swift"
var searchRespons = SearchResponse.builder()
searchRespons.result += [builderResult.build()]
println(searchRespons.build())

syntax = "proto2";
package FooBar;
message Perfomance
{
  required int32 ints = 1;
  required int64 ints64 = 2;
  required double doubles = 3;
  required float floats  = 4;
  optional string str  = 5;
  optional bytes bytes  = 6;
  optional string description = 7;
}
public extension FooBar {
  ...
  final public class Perfomance : GeneratedMessage, GeneratedMessageProtocol {
    ...
  }

}

自定义选项

import "google/protobuf/descriptor.proto";

package google.protobuf;

enum AccessControl {
  InternalEntities = 0;
  PublicEntities = 1;
}
message SwiftFileOptions {

  optional string class_prefix = 1;
  optional AccessControl entities_access_control = 2 [default = PublicEntities];
  optional bool compile_for_framework = 3 [default = true];
}

message SwiftMessageOptions {
  optional bool generate_error_type = 1 [default = false];
}

message SwiftEnumOptions {
  optional bool generate_error_type = 1 [default = false];
}

extend google.protobuf.FileOptions {
  optional SwiftFileOptions swift_file_options = 5092014;
}

extend google.protobuf.MessageOptions {
  optional SwiftMessageOptions swift_message_options = 5092014;
}

extend google.protobuf.EnumOptions {
  optional SwiftEnumOptions swift_enum_options = 5092015;
}

option (.google.protobuf.swift_file_options).compile_for_framework = false;
option (.google.protobuf.swift_file_options).entities_access_control = PublicEntities;

目前protobuf-swift的编译器支持自定义选项。

  1. 类前缀
  2. 访问控制
  3. 错误类型
  4. 编译为框架

如果您使用了自定义选项,您需要在您的

import 'google/protobuf/swift-descriptor.proto';

.proto 文件中添加

类前缀

此选项需要具有前缀的类名称。

示例

import 'google/protobuf/swift-descriptor.proto';

option (.google.protobuf.swift_file_options).class_prefix = "Proto";

message NameWithPrefix
{
  optional string str = 1;
}

生成的类具有名称

final internal class ProtoNameWithPrefix : GeneratedMessage

访问控制

option (.google.protobuf.swift_file_options).entities_access_control = PublicEntities;

默认情况下,所有生成的类标记为 internal。如果您要标记为 public,可以使用 entities_access_control 选项。

option (.google.protobuf.swift_file_options).entities_access_control = PublicEntities;

message MessageWithCustomOption
{
  optional string str = 1;
}

生成的类和所有字段都标记为 public

final public class MessageWithCustomOption : GeneratedMessage

生成符合“Error”协议的 enum/message

option (.google.protobuf.swift_enum_options).generate_error_type = true;

示例

import 'google/protobuf/swift-descriptor.proto';

enum ServiceError {
  option (.google.protobuf.swift_enum_options).generate_error_type = true;
  BadRequest = 0;
  InternalServerError = 1;
}

message UserProfile {
    message Request {
        required string userId = 1;
    }
    message Response {
        optional UserProfile profile = 1;
        optional ServiceError error = 2;
        optional Exception exception = 3;
    }

     message Exception {
        option (.google.protobuf.swift_message_options).generate_error_type = true;
        required int32 errorCode = 1;
        required string errorDescription = 2;
    }
    
    optional string firstName = 1;
    optional string lastName = 2;
    optional string avatarUrl = 3;
}
public enum ServiceError:Error, RawRepresentable, CustomDebugStringConvertible, CustomStringConvertible {
  public typealias RawValue = Int32

  case badRequest
  case internalServerError

  public init?(rawValue: RawValue) {
    switch rawValue {
    case 0: self = .badRequest
    case 1: self = .internalServerError
    default: return nil
    }
  }

  public var rawValue: RawValue {
    switch self {
    case .badRequest: return 0
    case .internalServerError: return 1
    }
  }

  public func throwException() throws {
    throw self
  }

  public var debugDescription:String { return getDescription() }
  public var description:String { return getDescription() }
  private func getDescription() -> String { 
    switch self {
    case .badRequest: return ".badRequest"
    case .internalServerError: return ".internalServerError"
    }
  }
}
func generateException()throws {
    let user = UserProfile.Response.Builder()
    user.error = .internalServerError
    let data = try user.build().data()
    let userError = try UserProfile.Response.parseFrom(data:data)
    if userError.hasError {
        throw userError.error //userError.error.throwException()
    }
}

do {
    try generateException()
} catch let err as ServiceError where err == .internalServerError {
    XCTAssertTrue(true)
} catch {
    XCTAssertTrue(false)
}

func throwExceptionMessage() throws {
  let exception = UserProfile.Exception.Builder()
  exception.errorCode = 403
  exception.errorDescription = "Bad Request"
  let exc = try exception.build()
  let data = try UserProfile.Response.Builder().setException(exc).build().data()
  let userError = try UserProfile.Response.parseFrom(data:data)
  if userError.hasException {
      throw userError.exception
  }
}

do {
    try throwExceptionMessage()
} catch let err as UserProfile.Exception {
    print(err)
    XCTAssertTrue(true)
} catch {
    XCTAssertTrue(false)
}
  

编译框架

option (.google.protobuf.swift_file_options).compile_for_framework = false;

此选项会删除生成文件中的字符串 import ProtocolBuffers

如果您需要其他选项,请告诉我。我将添加它们。

实用工具(ProtocolBuffers 3.0)

添加了已知类型 protos(any.proto、empty.proto、timestamp.proto、duration.proto 等)。用户可以像使用常规 proto 文件一样导入和使用这些 proto。未来版本将会为他们添加额外的运行时支持(以实用助手函数的形式,或通过在生成代码中将它们替换为特定语言的类型)。

Any

message Any {
  // A URL/resource name whose content describes the type of the
  // serialized message.
  //
  // For URLs which use the schema `http`, `https`, or no schema, the
  // following restrictions and interpretations apply:
  //
  // * If no schema is provided, `https` is assumed.
  // * The last segment of the URL's path must represent the fully
  //   qualified name of the type (as in `path/google.protobuf.Duration`).
  // * An HTTP GET on the URL must yield a [google.protobuf.Type][google.protobuf.Type]
  //   value in binary format, or produce an error.
  // * Applications are allowed to cache lookup results based on the
  //   URL, or have them precompiled into a binary to avoid any
  //   lookup. Therefore, binary compatibility needs to be preserved
  //   on changes to types. (Use versioned type names to manage
  //   breaking changes.)
  //
  // Schemas other than `http`, `https` (or the empty schema) might be
  // used with implementation specific semantics.
  //
  // Types originating from the `google.*` package
  // namespace should use `type.googleapis.com/full.type.name` (without
  // schema and path). A type service will eventually become available which
  // serves those URLs (projected Q2/15).
  string type_url = 1;

  // Must be valid serialized data of the above specified type.
  bytes value = 2;
}
Google.Protobuf.Any()

API

message Api {
  // The fully qualified name of this api, including package name
  // followed by the api's simple name.
  string name = 1;

  // The methods of this api, in unspecified order.
  repeated Method methods = 2;

  // Any metadata attached to the API.
  repeated Option options = 3;

  // A version string for this api. If specified, must have the form
  // `major-version.minor-version`, as in `1.10`. If the minor version
  // is omitted, it defaults to zero. If the entire version field is
  // empty, the major version is derived from the package name, as
  // outlined below. If the field is not empty, the version in the
  // package name will be verified to be consistent with what is
  // provided here.
  //
  // The versioning schema uses [semantic
  // versioning](http://semver.org) where the major version number
  // indicates a breaking change and the minor version an additive,
  // non-breaking change. Both version numbers are signals to users
  // what to expect from different versions, and should be carefully
  // chosen based on the product plan.
  //
  // The major version is also reflected in the package name of the
  // API, which must end in `v<major-version>`, as in
  // `google.feature.v1`. For major versions 0 and 1, the suffix can
  // be omitted. Zero major versions must only be used for
  // experimental, none-GA apis.
  //
  // See also: [design doc](http://go/api-versioning).
  //
  //
  string version = 4;

  // Source context for the protocol buffer service represented by this
  // message.
  SourceContext source_context = 5;
}

// Method represents a method of an api.
message Method {
  // The simple name of this method.
  string name = 1;

  // A URL of the input message type.
  string request_type_url = 2;

  // If true, the request is streamed.
  bool request_streaming = 3;

  // The URL of the output message type.
  string response_type_url = 4;

  // If true, the response is streamed.
  bool response_streaming = 5;

  // Any metadata attached to the method.
  repeated Option options = 6;
}
Google.Protobuf.Api()

Duration

message Duration {
  // Signed seconds of the span of time. Must be from -315,576,000,000
  // to +315,576,000,000 inclusive.
  int64 seconds = 1;

  // Signed fractions of a second at nanosecond resolution of the span
  // of time. Durations less than one second are represented with a 0
  // `seconds` field and a positive or negative `nanos` field. For durations
  // of one second or more, a non-zero value for the `nanos` field must be
  // of the same sign as the `seconds` field. Must be from -999,999,999
  // to +999,999,999 inclusive.
  int32 nanos = 2;
}
Google.Protobuf.Duration()

空内容

message Empty {
}
Google.Protobuf.Empty()

字段掩码

message FieldMask {
  // The set of field mask paths.
  repeated string paths = 1;
}
Google.Protobuf.FieldMask()

源上下文

message SourceContext {
  // The path-qualified name of the .proto file that contained the associated
  // protobuf element.  For example: `"google/protobuf/source.proto"`.
  string file_name = 1;
}
Google.Protobuf.SourceContext()

结构体

message Struct {
  // Map of dynamically typed values.
  map<string, Value> fields = 1;
}

// `Value` represents a dynamically typed value which can be either
// null, a number, a string, a boolean, a recursive struct value, or a
// list of values. A producer of value is expected to set one of that
// variants, absence of any variant indicates an error.
message Value {
  oneof kind {
    // Represents a null value.
    NullValue null_value = 1;

    // Represents a double value.
    double number_value = 2;

    // Represents a string value.
    string string_value = 3;

    // Represents a boolean value.
    bool bool_value = 4;

    // Represents a structured value.
    Struct struct_value = 5;

    // Represents a repeated `Value`.
    ListValue list_value = 6;
  }
}

// `ListValue` is a wrapper around a repeated field of values.
message ListValue {
  // Repeated field of dynamically typed values.
  repeated Value values = 1;
}

// `NullValue` is a singleton enumeration to represent the null
// value for the `Value` type union.
enum NullValue {
  // Null value.
  NULL_VALUE = 0;
}
Google.Protobuf.Struct()

时间戳

message Timestamp {
  // Represents seconds of UTC time since Unix epoch
  // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
  // 9999-12-31T23:59:59Z inclusive.
  int64 seconds = 1;

  // Non-negative fractions of a second at nanosecond resolution. Negative
  // second values with fractions must still have non-negative nanos values
  // that count forward in time. Must be from 0 to 999,999,999
  // inclusive.
  int32 nanos = 2;
}
Google.Protobuf.Timestamp()

类型

message Type {
  // The fully qualified message name.
  string name = 1;

  // The list of fields.
  repeated Field fields = 2;

  // The list of oneof definitions.
  // The list of oneofs declared in this Type
  repeated string oneofs = 3;

  // The proto options.
  repeated Option options = 4;

  // The source context.
  SourceContext source_context = 5;
}

// Field represents a single field of a message type.
message Field {
  // Kind represents a basic field type.
  enum Kind {
    // Field type unknown.
    TYPE_UNKNOWN = 0;

    // Field type double.
    TYPE_DOUBLE = 1;

    // Field type float.
    TYPE_FLOAT = 2;

    // Field type int64.
    TYPE_INT64 = 3;

    // Field type uint64.
    TYPE_UINT64 = 4;

    // Field type int32.
    TYPE_INT32 = 5;

    // Field type fixed64.
    TYPE_FIXED64 = 6;

    // Field type fixed32.
    TYPE_FIXED32 = 7;

    // Field type bool.
    TYPE_BOOL = 8;

    // Field type string.
    TYPE_STRING = 9;

    // Field type message.
    TYPE_MESSAGE = 11;

    // Field type bytes.
    TYPE_BYTES = 12;

    // Field type uint32.
    TYPE_UINT32 = 13;

    // Field type enum.
    TYPE_ENUM = 14;

    // Field type sfixed32.
    TYPE_SFIXED32 = 15;

    // Field type sfixed64.
    TYPE_SFIXED64 = 16;

    // Field type sint32.
    TYPE_SINT32 = 17;

    // Field type sint64.
    TYPE_SINT64 = 18;
  }

  // Cardinality represents whether a field is optional, required, or
  // repeated.
  enum Cardinality {
    // The field cardinality is unknown. Typically an error condition.
    CARDINALITY_UNKNOWN = 0;

    // For optional fields.
    CARDINALITY_OPTIONAL = 1;

    // For required fields. Not used for proto3.
    CARDINALITY_REQUIRED = 2;

    // For repeated fields.
    CARDINALITY_REPEATED = 3;
  }

  // The field kind.
  Kind kind = 1;

  // The field cardinality, i.e. optional/required/repeated.
  Cardinality cardinality = 2;

  // The proto field number.
  int32 number = 3;

  // The field name.
  string name = 4;

  // The type URL (without the scheme) when the type is MESSAGE or ENUM,
  // such as `type.googleapis.com/google.protobuf.Empty`.
  string type_url = 6;

  // Index in Type.oneofs. Starts at 1. Zero means no oneof mapping.
  int32 oneof_index = 7;

  // Whether to use alternative packed wire representation.
  bool packed = 8;

  // The proto options.
  repeated Option options = 9;
}

// Enum type definition.
message Enum {
  // Enum type name.
  string name = 1;

  // Enum value definitions.
  repeated EnumValue enumvalue = 2;

  // Proto options for the enum type.
  repeated Option options = 3;

  // The source context.
  SourceContext source_context = 4;
}

// Enum value definition.
message EnumValue {
  // Enum value name.
  string name = 1;

  // Enum value number.
  int32 number = 2;

  // Proto options for the enum value.
  repeated Option options = 3;
}

// Proto option attached to messages/fields/enums etc.
message Option {
  // Proto option name.
  string name = 1;

  // Proto option value.
  Any value = 2;
}
Google.Protobuf.Type()
...

包装器

// Wrapper message for double.
message DoubleValue {
  // The double value.
  double value = 1;
}

// Wrapper message for float.
message FloatValue {
  // The float value.
  float value = 1;
}

// Wrapper message for int64.
message Int64Value {
  // The int64 value.
  int64 value = 1;
}

// Wrapper message for uint64.
message UInt64Value {
  // The uint64 value.
  uint64 value = 1;
}

// Wrapper message for int32.
message Int32Value {
  // The int32 value.
  int32 value = 1;
}

// Wrapper message for uint32.
message UInt32Value {
  // The uint32 value.
  uint32 value = 1;
}

// Wrapper message for bool.
message BoolValue {
  // The bool value.
  bool value = 1;
}

// Wrapper message for string.
message StringValue {
  // The string value.
  string value = 1;
}

// Wrapper message for bytes.
message BytesValue {
  // The bytes value.
  bytes value = 1;
}
Google.Protobuf.StringValue()

致谢

开发者 - Alexey Khokhlov

Google Protocol Buffers - Cyrus Najmabadi, Sergey Martynov, Kenton Varda, Sanjay Ghemawat, Jeff Dean, 以及其他人员