diff --git a/http/get_simple/swift/ArrowGet/.gitignore b/http/get_simple/swift/ArrowGet/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/http/get_simple/swift/ArrowGet/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/http/get_simple/swift/ArrowGet/Package.swift b/http/get_simple/swift/ArrowGet/Package.swift new file mode 100644 index 0000000..482dd36 --- /dev/null +++ b/http/get_simple/swift/ArrowGet/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "ArrowGet", + platforms: [ + .macOS(.v14) + ], + dependencies: [ + .package(name: "Arrow", path: "vendor/Arrow"), + .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.3.0"), + .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.10.0") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .executableTarget( + name: "ArrowGet", + dependencies: [ + .product(name: "Arrow", package: "Arrow"), + .product(name: "Hummingbird", package: "hummingbird"), + .product(name: "Alamofire", package: "Alamofire") + ] + ), + ] +) diff --git a/http/get_simple/swift/ArrowGet/README.md b/http/get_simple/swift/ArrowGet/README.md new file mode 100644 index 0000000..5ca34e2 --- /dev/null +++ b/http/get_simple/swift/ArrowGet/README.md @@ -0,0 +1,33 @@ + + +# HTTP GET Arrow Data: Simple Swift Client Example + +This directory contains a minimal example of an HTTP client implemented in Swift. The client: +1. Sends an HTTP GET request to a server. +2. Receives an HTTP 200 response from the server, with the response body containing an Arrow IPC stream record batch. +3. Prints some of the record batches attributes and data to the terminal + +To run this example: +1. download and copy Apache Arrow Swift's Arrow folder into the vendor/Arrow folder. (the dependency and location are already defined in Package.swift) +2. run: + +```sh +swift run +``` \ No newline at end of file diff --git a/http/get_simple/swift/ArrowGet/Sources/main.swift b/http/get_simple/swift/ArrowGet/Sources/main.swift new file mode 100644 index 0000000..3dd1cef --- /dev/null +++ b/http/get_simple/swift/ArrowGet/Sources/main.swift @@ -0,0 +1,108 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import Foundation +import Arrow +import Hummingbird +import Alamofire + + +func makeRecordBatch() throws -> RecordBatch { + let doubleBuilder: NumberArrayBuilder = try ArrowArrayBuilders.loadNumberArrayBuilder() + doubleBuilder.append(11.11) + doubleBuilder.append(22.22) + doubleBuilder.append(33.33) + doubleBuilder.append(44.44) + let stringBuilder = try ArrowArrayBuilders.loadStringArrayBuilder() + stringBuilder.append("test10") + stringBuilder.append("test22") + stringBuilder.append("test33") + stringBuilder.append("test44") + let date32Builder = try ArrowArrayBuilders.loadDate32ArrayBuilder() + let date2 = Date(timeIntervalSinceReferenceDate: 86400 * 1) + let date1 = Date(timeIntervalSinceReferenceDate: 86400 * 5000 + 352) + date32Builder.append(date1) + date32Builder.append(date2) + date32Builder.append(date1) + date32Builder.append(date2) + let doubleHolder = ArrowArrayHolderImpl(try doubleBuilder.finish()) + let stringHolder = ArrowArrayHolderImpl(try stringBuilder.finish()) + let date32Holder = ArrowArrayHolderImpl(try date32Builder.finish()) + let result = RecordBatch.Builder() + .addColumn("col1", arrowArray: doubleHolder) + .addColumn("col2", arrowArray: stringHolder) + .addColumn("col3", arrowArray: date32Holder) + .finish() + switch result { + case .success(let recordBatch): + return recordBatch + case .failure(let error): + throw error + } +} + +final class HttpTest { + var appStarted = false + var done = false + var stopFunc: (() -> Void)? + + func run() throws { + _ = Task { + let router = Router() + router.get("/") { request, _ -> ByteBuffer in + let recordBatch = try makeRecordBatch() + let arrowWriter = ArrowWriter() + let writerInfo = ArrowWriter.Info(.recordbatch, schema: recordBatch.schema, batches: [recordBatch]) + switch arrowWriter.toStream(writerInfo) { + case .success(let writeData): + return ByteBuffer(data: writeData) + case.failure(let error): + throw error + } + } + + // create application using router + let app = Application( + router: router, + configuration: .init(address: .hostname("127.0.0.1", port: 8081)) + ) + + try await app.runService() + return ByteBuffer() + } + + let sem = DispatchSemaphore(value: 0) + let url = URL(string: "http://127.0.0.1:8081")! + let task = URLSession.shared.dataTask(with: url) { data, response, error in + defer {sem.signal()} + if let writeData = data { + let arrowReader = ArrowReader() + switch arrowReader.fromStream(writeData) { + case .success(let result): + let recordBatches = result.batches + print("recordBatch columns: \(recordBatches.count)") + let rb = recordBatches[0] + print("recordBatch columns: \(rb.columnCount)") + for (idx, column) in rb.columns.enumerated() { + print("col \(idx)") + let array = column.array + for idx in 0..