OLD | NEW |
(Empty) | |
| 1 # Protocol Buffers in Swift |
| 2 |
| 3 ## Objective |
| 4 |
| 5 This document describes the user-facing API and internal implementation of |
| 6 proto2 and proto3 messages in Apple’s Swift programming language. |
| 7 |
| 8 One of the key goals of protobufs is to provide idiomatic APIs for each |
| 9 language. In that vein, **interoperability with Objective-C is a non-goal of |
| 10 this proposal.** Protobuf users who need to pass messages between Objective-C |
| 11 and Swift code in the same application should use the existing Objective-C proto |
| 12 library. The goal of the effort described here is to provide an API for protobuf |
| 13 messages that uses features specific to Swift—optional types, algebraic |
| 14 enumerated types, value types, and so forth—in a natural way that will delight, |
| 15 rather than surprise, users of the language. |
| 16 |
| 17 ## Naming |
| 18 |
| 19 * By convention, both typical protobuf message names and Swift structs/classes |
| 20 are `UpperCamelCase`, so for most messages, the name of a message can be the |
| 21 same as the name of its generated type. (However, see the discussion below |
| 22 about prefixes under [Packages](#packages).) |
| 23 |
| 24 * Enum cases in protobufs typically are `UPPERCASE_WITH_UNDERSCORES`, whereas |
| 25 in Swift they are `lowerCamelCase` (as of the Swift 3 API design |
| 26 guidelines). We will transform the names to match Swift convention, using |
| 27 a whitelist similar to the Objective-C compiler plugin to handle commonly |
| 28 used acronyms. |
| 29 |
| 30 * Typical fields in proto messages are `lowercase_with_underscores`, while in |
| 31 Swift they are `lowerCamelCase`. We will transform the names to match |
| 32 Swift convention by removing the underscores and uppercasing the subsequent |
| 33 letter. |
| 34 |
| 35 ## Swift reserved words |
| 36 |
| 37 Swift has a large set of reserved words—some always reserved and some |
| 38 contextually reserved (that is, they can be used as identifiers in contexts |
| 39 where they would not be confused). As of Swift 2.2, the set of always-reserved |
| 40 words is: |
| 41 |
| 42 ``` |
| 43 _, #available, #column, #else, #elseif, #endif, #file, #function, #if, #line, |
| 44 #selector, as, associatedtype, break, case, catch, class, continue, default, |
| 45 defer, deinit, do, dynamicType, else, enum, extension, fallthrough, false, for, |
| 46 func, guard, if, import, in, init, inout, internal, is, let, nil, operator, |
| 47 private, protocol, public, repeat, rethrows, return, self, Self, static, |
| 48 struct, subscript, super, switch, throw, throws, true, try, typealias, var, |
| 49 where, while |
| 50 ``` |
| 51 |
| 52 The set of contextually reserved words is: |
| 53 |
| 54 ``` |
| 55 associativity, convenience, dynamic, didSet, final, get, infix, indirect, |
| 56 lazy, left, mutating, none, nonmutating, optional, override, postfix, |
| 57 precedence, prefix, Protocol, required, right, set, Type, unowned, weak, |
| 58 willSet |
| 59 ``` |
| 60 |
| 61 It is possible to use any reserved word as an identifier by escaping it with |
| 62 backticks (for example, ``let `class` = 5``). Other name-mangling schemes would |
| 63 require us to transform the names themselves (for example, by appending an |
| 64 underscore), which requires us to then ensure that the new name does not collide |
| 65 with something else in the same namespace. |
| 66 |
| 67 While the backtick feature may not be widely known by all Swift developers, a |
| 68 small amount of user education can address this and it seems like the best |
| 69 approach. We can unconditionally surround all property names with backticks to |
| 70 simplify generation. |
| 71 |
| 72 Some remapping will still be required, though, to avoid collisions between |
| 73 generated properties and the names of methods and properties defined in the base |
| 74 protocol/implementation of messages. |
| 75 |
| 76 # Features of Protocol Buffers |
| 77 |
| 78 This section describes how the features of the protocol buffer syntaxes (proto2 |
| 79 and proto3) map to features in Swift—what the code generated from a proto will |
| 80 look like, and how it will be implemented in the underlying library. |
| 81 |
| 82 ## Packages |
| 83 |
| 84 Modules are the main form of namespacing in Swift, but they are not declared |
| 85 using syntactic constructs like namespaces in C++ or packages in Java. Instead, |
| 86 they are tied to build targets in Xcode (or, in the future with open-source |
| 87 Swift, declarations in a Swift Package Manager manifest). They also do not |
| 88 easily support nesting submodules (Clang module maps support this, but pure |
| 89 Swift does not yet provide a way to define submodules). |
| 90 |
| 91 We will generate types with fully-qualified underscore-delimited names. For |
| 92 example, a message `Baz` in package `foo.bar` would generate a struct named |
| 93 `Foo_Bar_Baz`. For each fully-qualified proto message, there will be exactly one |
| 94 unique type symbol emitted in the generated binary. |
| 95 |
| 96 Users are likely to balk at the ugliness of underscore-delimited names for every |
| 97 generated type. To improve upon this situation, we will add a new string file |
| 98 level option, `swift_package_typealias`, that can be added to `.proto` files. |
| 99 When present, this will cause `typealias`es to be added to the generated Swift |
| 100 messages that replace the package name prefix with the provided string. For |
| 101 example, the following `.proto` file: |
| 102 |
| 103 ```protobuf |
| 104 option swift_package_typealias = "FBP"; |
| 105 package foo.bar; |
| 106 |
| 107 message Baz { |
| 108 // Message fields |
| 109 } |
| 110 ``` |
| 111 |
| 112 would generate the following Swift source: |
| 113 |
| 114 ```swift |
| 115 public struct Foo_Bar_Baz { |
| 116 // Message fields and other methods |
| 117 } |
| 118 |
| 119 typealias FBPBaz = Foo_Bar_Baz |
| 120 ``` |
| 121 |
| 122 It should be noted that this type alias is recorded in the generated |
| 123 `.swiftmodule` so that code importing the module can refer to it, but it does |
| 124 not cause a new symbol to be generated in the compiled binary (i.e., we do not |
| 125 risk compiled size bloat by adding `typealias`es for every type). |
| 126 |
| 127 Other strategies to handle packages that were considered and rejected can be |
| 128 found in [Appendix A](#appendix-a-rejected-strategies-to-handle-packages). |
| 129 |
| 130 ## Messages |
| 131 |
| 132 Proto messages are natural value types and we will generate messages as structs |
| 133 instead of classes. Users will benefit from Swift’s built-in behavior with |
| 134 regard to mutability. We will define a `ProtoMessage` protocol that defines the |
| 135 common methods and properties for all messages (such as serialization) and also |
| 136 lets users treat messages polymorphically. Any shared method implementations |
| 137 that do not differ between individual messages can be implemented in a protocol |
| 138 extension. |
| 139 |
| 140 The backing storage itself for fields of a message will be managed by a |
| 141 `ProtoFieldStorage` type that uses an internal dictionary keyed by field number, |
| 142 and whose values are the value of the field with that number (up-cast to Swift’s |
| 143 `Any` type). This class will provide type-safe getters and setters so that |
| 144 generated messages can manipulate this storage, and core serialization logic |
| 145 will live here as well. Furthermore, factoring the storage out into a separate |
| 146 type, rather than inlining the fields as stored properties in the message |
| 147 itself, lets us implement copy-on-write efficiently to support passing around |
| 148 large messages. (Furthermore, because the messages themselves are value types, |
| 149 inlining fields is not possible if the fields are submessages of the same type, |
| 150 or a type that eventually includes a submessage of the same type.) |
| 151 |
| 152 ### Required fields (proto2 only) |
| 153 |
| 154 Required fields in proto2 messages seem like they could be naturally represented |
| 155 by non-optional properties in Swift, but this presents some problems/concerns. |
| 156 |
| 157 Serialization APIs permit partial serialization, which allows required fields to |
| 158 remain unset. Furthermore, other language APIs still provide `has*` and `clear*` |
| 159 methods for required fields, and knowing whether a property has a value when the |
| 160 message is in memory is still useful. |
| 161 |
| 162 For example, an e-mail draft message may have the “to” address required on the |
| 163 wire, but when the user constructs it in memory, it doesn’t make sense to force |
| 164 a value until they provide one. We only want to force a value to be present when |
| 165 the message is serialized to the wire. Using non-optional properties prevents |
| 166 this use case, and makes client usage awkward because the user would be forced |
| 167 to select a sentinel or placeholder value for any required fields at the time |
| 168 the message was created. |
| 169 |
| 170 ### Default values |
| 171 |
| 172 In proto2, fields can have a default value specified that may be a value other |
| 173 than the default value for its corresponding language type (for example, a |
| 174 default value of 5 instead of 0 for an integer). When reading a field that is |
| 175 not explicitly set, the user expects to get that value. This makes Swift |
| 176 optionals (i.e., `Foo?`) unsuitable for fields in general. Unfortunately, we |
| 177 cannot implement our own “enhanced optional” type without severely complicating |
| 178 usage (Swift’s use of type inference and its lack of implicit conversions would |
| 179 require manual unwrapping of every property value). |
| 180 |
| 181 Instead, we can use **implicitly unwrapped optionals.** For example, a property |
| 182 generated for a field of type `int32` would have Swift type `Int32!`. These |
| 183 properties would behave with the following characteristics, which mirror the |
| 184 nil-resettable properties used elsewhere in Apple’s SDKs (for example, |
| 185 `UIView.tintColor`): |
| 186 |
| 187 * Assigning a non-nil value to a property sets the field to that value. |
| 188 * Assigning nil to a property clears the field (its internal representation is |
| 189 nilled out). |
| 190 * Reading the value of a property returns its value if it is set, or returns |
| 191 its default value if it is not set. Reading a property never returns nil. |
| 192 |
| 193 The final point in the list above implies that the optional cannot be checked to |
| 194 determine if the field is set to a value other than its default: it will never |
| 195 be nil. Instead, we must provide `has*` methods for each field to allow the user |
| 196 to check this. These methods will be public in proto2. In proto3, these methods |
| 197 will be private (if generated at all), since the user can test the returned |
| 198 value against the zero value for that type. |
| 199 |
| 200 ### Autocreation of nested messages |
| 201 |
| 202 For convenience, dotting into an unset field representing a nested message will |
| 203 return an instance of that message with default values. As in the Objective-C |
| 204 implementation, this does not actually cause the field to be set until the |
| 205 returned message is mutated. Fortunately, thanks to the way mutability of value |
| 206 types is implemented in Swift, the language automatically handles the |
| 207 reassignment-on-mutation for us. A static singleton instance containing default |
| 208 values can be associated with each message that can be returned when reading, so |
| 209 copies are only made by the Swift runtime when mutation occurs. For example, |
| 210 given the following proto: |
| 211 |
| 212 ```protobuf |
| 213 message Node { |
| 214 Node child = 1; |
| 215 string value = 2 [default = "foo"]; |
| 216 } |
| 217 ``` |
| 218 |
| 219 The following Swift code would act as commented, where setting deeply nested |
| 220 properties causes the copies and mutations to occur as the assignment statement |
| 221 is unwound: |
| 222 |
| 223 ```swift |
| 224 var node = Node() |
| 225 |
| 226 let s = node.child.child.value |
| 227 // 1. node.child returns the "default Node". |
| 228 // 2. Reading .child on the result of (1) returns the same default Node. |
| 229 // 3. Reading .value on the result of (2) returns the default value "foo". |
| 230 |
| 231 node.child.child.value = "bar" |
| 232 // 4. Setting .value on the default Node causes a copy to be made and sets |
| 233 // the property on that copy. Subsequently, the language updates the |
| 234 // value of "node.child.child" to point to that copy. |
| 235 // 5. Updating "node.child.child" in (4) requires another copy, because |
| 236 // "node.child" was also the instance of the default node. The copy is |
| 237 // assigned back to "node.child". |
| 238 // 6. Setting "node.child" in (5) is a simple value reassignment, since |
| 239 // "node" is a mutable var. |
| 240 ``` |
| 241 |
| 242 In other words, the generated messages do not internally have to manage parental |
| 243 relationships to backfill the appropriate properties on mutation. Swift provides |
| 244 this for free. |
| 245 |
| 246 ## Scalar value fields |
| 247 |
| 248 Proto scalar value fields will map to Swift types in the following way: |
| 249 |
| 250 .proto Type | Swift Type |
| 251 ----------- | ------------------- |
| 252 `double` | `Double` |
| 253 `float` | `Float` |
| 254 `int32` | `Int32` |
| 255 `int64` | `Int64` |
| 256 `uint32` | `UInt32` |
| 257 `uint64` | `UInt64` |
| 258 `sint32` | `Int32` |
| 259 `sint64` | `Int64` |
| 260 `fixed32` | `UInt32` |
| 261 `fixed64` | `UInt64` |
| 262 `sfixed32` | `Int32` |
| 263 `sfixed64` | `Int64` |
| 264 `bool` | `Bool` |
| 265 `string` | `String` |
| 266 `bytes` | `Foundation.NSData` |
| 267 |
| 268 The proto spec defines a number of integral types that map to the same Swift |
| 269 type; for example, `intXX`, `sintXX`, and `sfixedXX` are all signed integers, |
| 270 and `uintXX` and `fixedXX` are both unsigned integers. No other language |
| 271 implementation distinguishes these further, so we do not do so either. The |
| 272 rationale is that the various types only serve to distinguish how the value is |
| 273 **encoded on the wire**; once loaded in memory, the user is not concerned about |
| 274 these variations. |
| 275 |
| 276 Swift’s lack of implicit conversions among types will make it slightly annoying |
| 277 to use these types in a context expecting an `Int`, or vice-versa, but since |
| 278 this is a data-interchange format with explicitly-sized fields, we should not |
| 279 hide that information from the user. Users will have to explicitly write |
| 280 `Int(message.myField)`, for example. |
| 281 |
| 282 ## Embedded message fields |
| 283 |
| 284 Embedded message fields can be represented using an optional variable of the |
| 285 generated message type. Thus, the message |
| 286 |
| 287 ```protobuf |
| 288 message Foo { |
| 289 Bar bar = 1; |
| 290 } |
| 291 ``` |
| 292 |
| 293 would be represented in Swift as |
| 294 |
| 295 ```swift |
| 296 public struct Foo: ProtoMessage { |
| 297 public var bar: Bar! { |
| 298 get { ... } |
| 299 set { ... } |
| 300 } |
| 301 } |
| 302 ``` |
| 303 |
| 304 If the user explicitly sets `bar` to nil, or if it was never set when read from |
| 305 the wire, retrieving the value of `bar` would return a default, statically |
| 306 allocated instance of `Bar` containing default values for its fields. This |
| 307 achieves the desired behavior for default values in the same way that scalar |
| 308 fields are designed, and also allows users to deep-drill into complex object |
| 309 graphs to get or set fields without checking for nil at each step. |
| 310 |
| 311 ## Enum fields |
| 312 |
| 313 The design and implementation of enum fields will differ somewhat drastically |
| 314 depending on whether the message being generated is a proto2 or proto3 message. |
| 315 |
| 316 ### proto2 enums |
| 317 |
| 318 For proto2, we do not need to be concerned about unknown enum values, so we can |
| 319 use the simple raw-value enum syntax provided by Swift. So the following enum in |
| 320 proto2: |
| 321 |
| 322 ```protobuf |
| 323 enum ContentType { |
| 324 TEXT = 0; |
| 325 IMAGE = 1; |
| 326 } |
| 327 ``` |
| 328 |
| 329 would become this Swift enum: |
| 330 |
| 331 ```swift |
| 332 public enum ContentType: Int32, NilLiteralConvertible { |
| 333 case text = 0 |
| 334 case image = 1 |
| 335 |
| 336 public init(nilLiteral: ()) { |
| 337 self = .text |
| 338 } |
| 339 } |
| 340 ``` |
| 341 |
| 342 See below for the discussion about `NilLiteralConvertible`. |
| 343 |
| 344 ### proto3 enums |
| 345 |
| 346 For proto3, we need to be able to preserve unknown enum values that may come |
| 347 across the wire so that they can be written back if unmodified. We can |
| 348 accomplish this in Swift by using a case with an associated value for unknowns. |
| 349 So the following enum in proto3: |
| 350 |
| 351 ```protobuf |
| 352 enum ContentType { |
| 353 TEXT = 0; |
| 354 IMAGE = 1; |
| 355 } |
| 356 ``` |
| 357 |
| 358 would become this Swift enum: |
| 359 |
| 360 ```swift |
| 361 public enum ContentType: RawRepresentable, NilLiteralConvertible { |
| 362 case text |
| 363 case image |
| 364 case UNKNOWN_VALUE(Int32) |
| 365 |
| 366 public typealias RawValue = Int32 |
| 367 |
| 368 public init(nilLiteral: ()) { |
| 369 self = .text |
| 370 } |
| 371 |
| 372 public init(rawValue: RawValue) { |
| 373 switch rawValue { |
| 374 case 0: self = .text |
| 375 case 1: self = .image |
| 376 default: self = .UNKNOWN_VALUE(rawValue) |
| 377 } |
| 378 |
| 379 public var rawValue: RawValue { |
| 380 switch self { |
| 381 case .text: return 0 |
| 382 case .image: return 1 |
| 383 case .UNKNOWN_VALUE(let value): return value |
| 384 } |
| 385 } |
| 386 } |
| 387 ``` |
| 388 |
| 389 Note that the use of a parameterized case prevents us from inheriting from the |
| 390 raw `Int32` type; Swift does not allow an enum with a raw type to have cases |
| 391 with arguments. Instead, we must implement the raw value initializer and |
| 392 computed property manually. The `UNKNOWN_VALUE` case is explicitly chosen to be |
| 393 "ugly" so that it stands out and does not conflict with other possible case |
| 394 names. |
| 395 |
| 396 Using this approach, proto3 consumers must always have a default case or handle |
| 397 the `.UNKNOWN_VALUE` case to satisfy case exhaustion in a switch statement; the |
| 398 Swift compiler considers it an error if switch statements are not exhaustive. |
| 399 |
| 400 ### NilLiteralConvertible conformance |
| 401 |
| 402 This is required to clean up the usage of enum-typed properties in switch |
| 403 statements. Unlike other field types, enum properties cannot be |
| 404 implicitly-unwrapped optionals without requiring that uses in switch statements |
| 405 be explicitly unwrapped. For example, if we consider a message with the enum |
| 406 above, this usage will fail to compile: |
| 407 |
| 408 ```swift |
| 409 // Without NilLiteralConvertible conformance on ContentType |
| 410 public struct SomeMessage: ProtoMessage { |
| 411 public var contentType: ContentType! { ... } |
| 412 } |
| 413 |
| 414 // ERROR: no case named text or image |
| 415 switch someMessage.contentType { |
| 416 case .text: { ... } |
| 417 case .image: { ... } |
| 418 } |
| 419 ``` |
| 420 |
| 421 Even though our implementation guarantees that `contentType` will never be nil, |
| 422 if it is an optional type, its cases would be `some` and `none`, not the cases |
| 423 of the underlying enum type. In order to use it in this context, the user must |
| 424 write `someMessage.contentType!` in their switch statement. |
| 425 |
| 426 Making the enum itself `NilLiteralConvertible` permits us to make the property |
| 427 non-optional, so the user can still set it to nil to clear it (i.e., reset it to |
| 428 its default value), while eliminating the need to explicitly unwrap it in a |
| 429 switch statement. |
| 430 |
| 431 ```swift |
| 432 // With NilLiteralConvertible conformance on ContentType |
| 433 public struct SomeMessage: ProtoMessage { |
| 434 // Note that the property type is no longer optional |
| 435 public var contentType: ContentType { ... } |
| 436 } |
| 437 |
| 438 // OK: Compiles and runs as expected |
| 439 switch someMessage.contentType { |
| 440 case .text: { ... } |
| 441 case .image: { ... } |
| 442 } |
| 443 |
| 444 // The enum can be reset to its default value this way |
| 445 someMessage.contentType = nil |
| 446 ``` |
| 447 |
| 448 One minor oddity with this approach is that nil will be auto-converted to the |
| 449 default value of the enum in any context, not just field assignment. In other |
| 450 words, this is valid: |
| 451 |
| 452 ```swift |
| 453 func foo(contentType: ContentType) { ... } |
| 454 foo(nil) // Inside foo, contentType == .text |
| 455 ``` |
| 456 |
| 457 That being said, the advantage of being able to simultaneously support |
| 458 nil-resettability and switch-without-unwrapping outweighs this side effect, |
| 459 especially if appropriately documented. It is our hope that a new form of |
| 460 resettable properties will be added to Swift that eliminates this inconsistency. |
| 461 Some community members have already drafted or sent proposals for review that |
| 462 would benefit our designs: |
| 463 |
| 464 * [SE-0030: Property Behaviors] |
| 465 (https://github.com/apple/swift-evolution/blob/master/proposals/0030-propert
y-behavior-decls.md) |
| 466 * [Drafted: Resettable Properties] |
| 467 (https://github.com/patters/swift-evolution/blob/master/proposals/0000-reset
table-properties.md) |
| 468 |
| 469 ### Enum aliases |
| 470 |
| 471 The `allow_alias` option in protobuf slightly complicates the use of Swift enums |
| 472 to represent that type, because raw values of cases in an enum must be unique. |
| 473 Swift lets us define static variables in an enum that alias actual cases. For |
| 474 example, the following protobuf enum: |
| 475 |
| 476 ```protobuf |
| 477 enum Foo { |
| 478 option allow_alias = true; |
| 479 BAR = 0; |
| 480 BAZ = 0; |
| 481 } |
| 482 ``` |
| 483 |
| 484 will be represented in Swift as: |
| 485 |
| 486 ```swift |
| 487 public enum Foo: Int32, NilLiteralConvertible { |
| 488 case bar = 0 |
| 489 static public let baz = bar |
| 490 |
| 491 // ... etc. |
| 492 } |
| 493 |
| 494 // Can still use .baz shorthand to reference the alias in contexts |
| 495 // where the type is inferred |
| 496 ``` |
| 497 |
| 498 That is, we use the first name as the actual case and use static variables for |
| 499 the other aliases. One drawback to this approach is that the static aliases |
| 500 cannot be used as cases in a switch statement (the compiler emits the error |
| 501 *“Enum case ‘baz’ not found in type ‘Foo’”*). However, in our own code bases, |
| 502 there are only a few places where enum aliases are not mere renamings of an |
| 503 older value, but they also don’t appear to be the type of value that one would |
| 504 expect to switch on (for example, a group of named constants representing |
| 505 metrics rather than a set of options), so this restriction is not significant. |
| 506 |
| 507 This strategy also implies that changing the name of an enum and adding the old |
| 508 name as an alias below the new name will be a breaking change in the generated |
| 509 Swift code. |
| 510 |
| 511 ## Oneof types |
| 512 |
| 513 The `oneof` feature represents a “variant/union” data type that maps nicely to |
| 514 Swift enums with associated values (algebraic types). These fields can also be |
| 515 accessed independently though, and, specifically in the case of proto2, it’s |
| 516 reasonable to expect access to default values when accessing a field that is not |
| 517 explicitly set. |
| 518 |
| 519 Taking all this into account, we can represent a `oneof` in Swift with two sets |
| 520 of constructs: |
| 521 |
| 522 * Properties in the message that correspond to the `oneof` fields. |
| 523 * A nested enum named after the `oneof` and which provides the corresponding |
| 524 field values as case arguments. |
| 525 |
| 526 This approach fulfills the needs of proto consumers by providing a |
| 527 Swift-idiomatic way of simultaneously checking which field is set and accessing |
| 528 its value, providing individual properties to access the default values |
| 529 (important for proto2), and safely allows a field to be moved into a `oneof` |
| 530 without breaking clients. |
| 531 |
| 532 Consider the following proto: |
| 533 |
| 534 ```protobuf |
| 535 message MyMessage { |
| 536 oneof record { |
| 537 string name = 1 [default = "unnamed"]; |
| 538 int32 id_number = 2 [default = 0]; |
| 539 } |
| 540 } |
| 541 ``` |
| 542 |
| 543 In Swift, we would generate an enum, a property for that enum, and properties |
| 544 for the fields themselves: |
| 545 |
| 546 ```swift |
| 547 public struct MyMessage: ProtoMessage { |
| 548 public enum Record: NilLiteralConvertible { |
| 549 case name(String) |
| 550 case idNumber(Int32) |
| 551 case NOT_SET |
| 552 |
| 553 public init(nilLiteral: ()) { self = .NOT_SET } |
| 554 } |
| 555 |
| 556 // This is the "Swifty" way of accessing the value |
| 557 public var record: Record { ... } |
| 558 |
| 559 // Direct access to the underlying fields |
| 560 public var name: String! { ... } |
| 561 public var idNumber: Int32! { ... } |
| 562 } |
| 563 ``` |
| 564 |
| 565 This makes both usage patterns possible: |
| 566 |
| 567 ```swift |
| 568 // Usage 1: Case-based dispatch |
| 569 switch message.record { |
| 570 case .name(let name): |
| 571 // Do something with name if it was explicitly set |
| 572 case .idNumber(let id): |
| 573 // Do something with id_number if it was explicitly set |
| 574 case .NOT_SET: |
| 575 // Do something if it’s not set |
| 576 } |
| 577 |
| 578 // Usage 2: Direct access for default value fallback |
| 579 // Sets the label text to the name if it was explicitly set, or to |
| 580 // "unnamed" (the default value for the field) if id_number was set |
| 581 // instead |
| 582 let myLabel = UILabel() |
| 583 myLabel.text = message.name |
| 584 ``` |
| 585 |
| 586 As with proto enums, the generated `oneof` enum conforms to |
| 587 `NilLiteralConvertible` to avoid switch statement issues. Setting the property |
| 588 to nil will clear it (i.e., reset it to `NOT_SET`). |
| 589 |
| 590 ## Unknown Fields (proto2 only) |
| 591 |
| 592 To be written. |
| 593 |
| 594 ## Extensions (proto2 only) |
| 595 |
| 596 To be written. |
| 597 |
| 598 ## Reflection and Descriptors |
| 599 |
| 600 We will not include reflection or descriptors in the first version of the Swift |
| 601 library. The use cases for reflection on mobile are not as strong and the static |
| 602 data to represent the descriptors would add bloat when we wish to keep the code |
| 603 size small. |
| 604 |
| 605 In the future, we will investigate whether they can be included as extensions |
| 606 which might be able to be excluded from a build and/or automatically dead |
| 607 stripped by the compiler if they are not used. |
| 608 |
| 609 ## Appendix A: Rejected strategies to handle packages |
| 610 |
| 611 ### Each package is its own Swift module |
| 612 |
| 613 Each proto package could be declared as its own Swift module, replacing dots |
| 614 with underscores (e.g., package `foo.bar` becomes module `Foo_Bar`). Then, users |
| 615 would simply import modules containing whatever proto modules they want to use |
| 616 and refer to the generated types by their short names. |
| 617 |
| 618 **This solution is simply not possible, however.** Swift modules cannot |
| 619 circularly reference each other, but there is no restriction against proto |
| 620 packages doing so. Circular imports are forbidden (e.g., `foo.proto` importing |
| 621 `bar.proto` importing `foo.proto`), but nothing prevents package `foo` from |
| 622 using a type in package `bar` which uses a different type in package `foo`, as |
| 623 long as there is no import cycle. If these packages were generated as Swift |
| 624 modules, then `Foo` would contain an `import Bar` statement and `Bar` would |
| 625 contain an `import Foo` statement, and there is no way to compile this. |
| 626 |
| 627 ### Ad hoc namespacing with structs |
| 628 |
| 629 We can “fake” namespaces in Swift by declaring empty structs with private |
| 630 initializers. Since modules are constructed based on compiler arguments, not by |
| 631 syntactic constructs, and because there is no pure Swift way to define |
| 632 submodules (even though Clang module maps support this), there is no |
| 633 source-drive way to group generated code into namespaces aside from this |
| 634 approach. |
| 635 |
| 636 Types can be added to those intermediate package structs using Swift extensions. |
| 637 For example, a message `Baz` in package `foo.bar` could be represented in Swift |
| 638 as follows: |
| 639 |
| 640 ```swift |
| 641 public struct Foo { |
| 642 private init() {} |
| 643 } |
| 644 |
| 645 public extension Foo { |
| 646 public struct Bar { |
| 647 private init() {} |
| 648 } |
| 649 } |
| 650 |
| 651 public extension Foo.Bar { |
| 652 public struct Baz { |
| 653 // Message fields and other methods |
| 654 } |
| 655 } |
| 656 |
| 657 let baz = Foo.Bar.Baz() |
| 658 ``` |
| 659 |
| 660 Each of these constructs would actually be defined in a separate file; Swift |
| 661 lets us keep them separate and add multiple structs to a single “namespace” |
| 662 through extensions. |
| 663 |
| 664 Unfortunately, these intermediate structs generate symbols of their own |
| 665 (metatype information in the data segment). This becomes problematic if multiple |
| 666 build targets contain Swift sources generated from different messages in the |
| 667 same package. At link time, these symbols would collide, resulting in multiple |
| 668 definition errors. |
| 669 |
| 670 This approach also has the disadvantage that there is no automatic “short” way |
| 671 to refer to the generated messages at the deepest nesting levels; since this use |
| 672 of structs is a hack around the lack of namespaces, there is no equivalent to |
| 673 import (Java) or using (C++) to simplify this. Users would have to declare type |
| 674 aliases to make this cleaner, or we would have to generate them for users. |
OLD | NEW |