blob: 2ad88d618c008a269a24acf2e6180f7aac2263e7 [file] [log] [blame] [view]
Chris Simpsona9b6c702018-04-08 07:11:37 -04001Thrift Swift Library
2=========================
3
4License
5-------
6Licensed to the Apache Software Foundation (ASF) under one
7or more contributor license agreements. See the NOTICE file
8distributed with this work for additional information
9regarding copyright ownership. The ASF licenses this file
10to you under the Apache License, Version 2.0 (the
11"License"); you may not use this file except in compliance
12with the License. You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16Unless required by applicable law or agreed to in writing,
17software distributed under the License is distributed on an
18"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19KIND, either express or implied. See the License for the
20specific language governing permissions and limitations
21under the License.
22
Chris Simpson2566ecd2018-08-29 14:40:44 -040023Brought to you by [FiscalNote, Inc](http://www.fiscalnote.com/)
24
Chris Simpsona9b6c702018-04-08 07:11:37 -040025
26## Build
27
28 swift build
29
30## Test
31 swift test
32
33## Install Library
34##### Cocoapods
35Add the following to your podfile
36```ruby
37 pod 'Thrift-swift3', :git => 'git@github.com:apache/thrift.git', :branch => 'master'
38```
39
40##### SPM
41Unfortunately due to some limitations in SPM, the Package manifest and Sources directory must be at the root of the project.
42To get around that for the time being, you can use this mirrored repo.
43Add the following to your Package.swift
44```swift
45dependencies: [
46 .Package(url: "https://github.com/apocolipse/Thrift-Swift.git", majorVersion: 1)
47]
48```
49
50## Thrift Compiler
51
52You can compile IDL sources for Swift 3 with the following command:
53
54 thrift --gen swift thrift_file
55
56## Client Example
57```swift
58let transport = TSocketTransport(hostname: "localhost", port: 9090)!
59
60// var proto = TCompactProtocol(transport: transport)
61let proto = TBinaryProtocol(on: transport)
62// var client = HermesClient(inoutProtocol: proto)
63let client = ThriftTestClient(inoutProtocol: proto)
64do {
65 try client.testVoid()
66} catch let error {
67 print("\(error)")
68}
69```
70
71## Library Notes
72- Eliminated Protocol Factories, They were only used in async clients and server implementations, where Generics provide a better alternative.
73- Swifty Errors, All `TError` types have a nested `ErrorCode` Enum as well as some extra flavor where needed.
74- Value typed everything. `TTransport` operates on value typed `Data` rather than reference typed `NSData` or `UnsafeBufferPointer`s
75- Swift 3 Named protocols. Swift 3 naming conventions suggest the elimination of redundant words that can be inferred from variable/function signatures. This renaming is applied throughout the Swift 3 library converting most naming conventions used in the Swift2/Cocoa library to Swift 3-esque naming conventions. eg.
76```swift
77func readString() throws -> String
78func writeString(_ val: String) throws
79```
80have been renamed to eliminate redundant words:
81```swift
82func read() throws -> String
83func write(_ val: String) throws
84```
85
86- Eliminated `THTTPTransport` that uses `NSURLConnection` due to it being deprecated and not available at all in Swift 3 for Linux. `THTTPSessionTransport` from the Swift2/Cocoa library that uses `NSURLSession` has been renamed to `THTTPTransport` for this library and leverages `URLSession`, providing both synchronous (with semaphores) and asynchronous behavior.
87- Probably some More things I've missed here.
88
89## Generator Notes
90#### Generator Flags
91| Flag | Description |
92| ------------- |:-------------:|
Chris Simpson2566ecd2018-08-29 14:40:44 -040093| async_clients | Generate clients which invoke asynchronously via block syntax. Asynchronous classes are appended with `_Async` |
Chris Simpsona9b6c702018-04-08 07:11:37 -040094| no_strict* | Generates non-strict structs |
95| debug_descriptions | Allow use of debugDescription so the app can add description via a cateogory/extension |
96| log_unexpected | Log every time an unexpected field ID or type is encountered. |
Chris Simpson2566ecd2018-08-29 14:40:44 -040097| safe_enums | Generate enum types with an unknown case to handle unspecified values rather than throw a serialization error |
Chris Simpsona9b6c702018-04-08 07:11:37 -040098
99
100
101*Most thrift libraries allow empty initialization of Structs, initializing `required` fields with nil/null/None (Python and Node generators). Swift on the other hand requires initializers to initialize all non-Optional fields, and thus the Swift 3 generator does not provide default values (unlike the Swift 2/Cocoa generator). In other languages, this allows the sending of NULL values in fields that are marked `required`, and thus will throw an error in Swift clients attempting to validate fields. The `no_strict` option here will ignore the validation check, as well as behave similar to the Swift2/Cocoa generator and initialize required fields with empty initializers (where possible).
102
103
104## Whats implemented
105#### Library
106##### Transports
107- [x] TSocketTransport - CFSocket and PosixSocket variants available. CFSocket variant only currently available for Darwin platforms
108- [x] THTTPTransport - Currently only available for Darwin platforms, Swift Foundation URLSession implementation needs completion on linux.
109- [x] TSocketServer - Uses CFSockets only for binding, should be working on linux
110- [x] TFramedTransport
111- [x] TMemoryBufferTransport
112- [x] TFileTransport - A few variants using File handles and file descriptors.
113- [x] TStreamTransport - Fully functional in Darwin, Foundation backing not yet completed in Linux (This limits TCFSocketTransport to Darwin)
114- [ ] HTTPServer - Currently there is no lightweight HTTPServer implementation the Swift Standard Library, so other 3rd party alternatives are required and out of scope for the Thrift library. Examples using Perfect will be provided.
115- [ ] Other (gz, etc)
116
117##### Protocols
118- [x] TBinaryProtocol
119- [x] TCompactProtocol
120- [ ] TJSONProtocol - This will need to be implemented
121
122##### Generator
123- [x] Code Complete Generator
124- [x] Async clients
125- [x] Documentation Generation - Generator will transplant IDL docs to Swift code for easy lookup in Xcode
126- [ ] Default Values - TODO
127- [ ] no_strict mode - TODO
128- [ ] Namespacing - Still haven't nailed down a good paradigm for namespacing. It will likely involve creating subdirectories for different namespaces and expecting the developer to import each subdirectory as separate modules. It could extend to creating SPM Package manifests with sub-modules within the generated module
129
130
131
132## Example HTTP Server with Perfect
133```swift
134import PerfectLib
135import PerfectHTTP
136import PerfectHTTPServer
137import Dispatch
138
139let logQueue = DispatchQueue(label: "log", qos: .background, attributes: .concurrent)
140let pQueue = DispatchQueue(label: "log", qos: .userInitiated, attributes: .concurrent)
141
142
143class TPerfectServer<InProtocol: TProtocol, OutProtocol: TProtocol> {
144
145 private var server = HTTPServer()
146 private var processor: TProcessor
147
148 init(address: String? = nil,
149 path: String? = nil,
150 port: Int,
151 processor: TProcessor,
152 inProtocol: InProtocol.Type,
153 outProtocol: OutProtocol.Type) throws {
154
155 self.processor = processor
156
157 if let address = address {
158 server.serverAddress = address
159 }
160 server.serverPort = UInt16(port)
161
162 var routes = Routes()
163 var uri = "/"
164 if let path = path {
165 uri += path
166 }
167 routes.add(method: .post, uri: uri) { request, response in
168 pQueue.async {
169 response.setHeader(.contentType, value: "application/x-thrift")
170
171 let itrans = TMemoryBufferTransport()
172 if let bytes = request.postBodyBytes {
173 let data = Data(bytes: bytes)
174 itrans.reset(readBuffer: data)
175 }
176
177 let otrans = TMemoryBufferTransport(flushHandler: { trans, buff in
178 let array = buff.withUnsafeBytes {
179 Array<UInt8>(UnsafeBufferPointer(start: $0, count: buff.count))
180 }
181 response.status = .ok
182 response.setBody(bytes: array)
183 response.completed()
184 })
185
186 let inproto = InProtocol(on: itrans)
187 let outproto = OutProtocol(on: otrans)
188
189 do {
190 try processor.process(on: inproto, outProtocol: outproto)
191 try otrans.flush()
192 } catch {
193 response.status = .badRequest
194 response.completed()
195 }
196 }
197 }
198 server.addRoutes(routes)
199 }
200
201 func serve() throws {
202 try server.start()
203 }
204}
205```
206
207#### Example Usage
208```swift
209class ServiceHandler : Service {
210 ...
211}
212let server = try? TPerfectServer(port: 9090,
213 processor: ServiceProcessor(service: ServiceHandler()),
214 inProtocol: TBinaryProtocol.self,
215 outProtocol: TBinaryProtocol.self)
216
217try? server?.serve()
218```