├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── dependabot.yml
├── pull_request_template.md
├── release.yml
└── workflows
│ ├── add-to-project.yml
│ ├── ci.yml
│ ├── pr-title.yml
│ └── release.yml
├── .gitignore
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── IDETemplateMacros.plist
├── Connect-Swift-Mocks.podspec
├── Connect-Swift.podspec
├── Examples
├── ElizaCocoaPodsApp
│ ├── ElizaCocoaPodsApp.xcodeproj
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace
│ │ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── ElizaCocoaPodsApp.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── ElizaCocoaPodsApp
│ │ └── Assets.xcassets
│ │ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ │ └── Contents.json
│ ├── Podfile
│ ├── Podfile.lock
│ └── README.md
├── ElizaSharedSources
│ ├── AppSources
│ │ ├── ElizaApp.swift
│ │ ├── MenuView.swift
│ │ ├── Message.swift
│ │ ├── MessagingView.swift
│ │ └── MessagingViewModel.swift
│ ├── GeneratedSources
│ │ └── connectrpc
│ │ │ └── eliza
│ │ │ └── v1
│ │ │ ├── eliza.connect.swift
│ │ │ └── eliza.pb.swift
│ └── README.md
├── ElizaSwiftPackageApp
│ ├── ElizaSwiftPackageApp.xcodeproj
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace
│ │ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ ├── ElizaSwiftPackageApp
│ │ ├── Assets.xcassets
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ └── Info.plist
│ └── README.md
├── README.md
└── buf.gen.yaml
├── LICENSE
├── Libraries
├── Connect
│ ├── Internal
│ │ ├── GeneratedSources
│ │ │ └── proto
│ │ │ │ └── grpc
│ │ │ │ └── status
│ │ │ │ └── v1
│ │ │ │ └── status.pb.swift
│ │ ├── Interceptors
│ │ │ ├── ConnectInterceptor.swift
│ │ │ ├── GRPCWebInterceptor.swift
│ │ │ └── InterceptorChain.swift
│ │ ├── Streaming
│ │ │ ├── BidirectionalAsyncStream.swift
│ │ │ ├── BidirectionalStream.swift
│ │ │ ├── ClientOnlyAsyncStream.swift
│ │ │ ├── ClientOnlyStream.swift
│ │ │ ├── ClientOnlyStreamValidation.swift
│ │ │ ├── ConnectEndStreamResponse.swift
│ │ │ ├── ServerOnlyAsyncStream.swift
│ │ │ ├── ServerOnlyStream.swift
│ │ │ └── URLSessionStream.swift
│ │ ├── Unary
│ │ │ └── UnaryAsyncWrapper.swift
│ │ └── Utilities
│ │ │ ├── Lock.swift
│ │ │ ├── Locked.swift
│ │ │ └── TimeoutTimer.swift
│ ├── PackageInternal
│ │ ├── ConnectError+GRPC.swift
│ │ ├── Envelope.swift
│ │ ├── Headers+GRPC.swift
│ │ └── Trailers+gRPC.swift
│ ├── Public
│ │ ├── Implementation
│ │ │ ├── Clients
│ │ │ │ ├── ProtocolClient.swift
│ │ │ │ └── URLSessionHTTPClient.swift
│ │ │ ├── Codecs
│ │ │ │ ├── JSONCodec.swift
│ │ │ │ └── ProtoCodec.swift
│ │ │ └── Compression
│ │ │ │ └── GzipCompressionPool.swift
│ │ └── Interfaces
│ │ │ ├── Cancelable.swift
│ │ │ ├── Code.swift
│ │ │ ├── Codec.swift
│ │ │ ├── CompressionPool.swift
│ │ │ ├── ConnectError.swift
│ │ │ ├── HTTPClientInterface.swift
│ │ │ ├── HTTPMethod.swift
│ │ │ ├── HTTPMetrics.swift
│ │ │ ├── HTTPRequest.swift
│ │ │ ├── HTTPResponse.swift
│ │ │ ├── HeaderConstants.swift
│ │ │ ├── Headers.swift
│ │ │ ├── IdempotencyLevel.swift
│ │ │ ├── Interceptors
│ │ │ ├── Interceptor.swift
│ │ │ ├── InterceptorFactory.swift
│ │ │ ├── StreamInterceptor.swift
│ │ │ └── UnaryInterceptor.swift
│ │ │ ├── MethodSpec.swift
│ │ │ ├── NetworkProtocol.swift
│ │ │ ├── ProtobufMessage.swift
│ │ │ ├── ProtocolClientConfig.swift
│ │ │ ├── ProtocolClientInterface.swift
│ │ │ ├── ResponseMessage.swift
│ │ │ ├── Streaming
│ │ │ ├── AsyncAwait
│ │ │ │ ├── BidirectionalAsyncStreamInterface.swift
│ │ │ │ ├── ClientOnlyAsyncStreamInterface.swift
│ │ │ │ └── ServerOnlyAsyncStreamInterface.swift
│ │ │ ├── Callbacks
│ │ │ │ ├── BidirectionalStreamInterface.swift
│ │ │ │ ├── ClientOnlyStreamInterface.swift
│ │ │ │ ├── RequestCallbacks.swift
│ │ │ │ ├── ResponseCallbacks.swift
│ │ │ │ └── ServerOnlyStreamInterface.swift
│ │ │ └── StreamResult.swift
│ │ │ └── Trailers.swift
│ ├── README.md
│ ├── buf.gen.yaml
│ └── proto
│ │ ├── README.md
│ │ ├── buf.yaml
│ │ └── grpc
│ │ └── status
│ │ └── v1
│ │ └── status.proto
├── ConnectMocks
│ ├── MockBidirectionalAsyncStream.swift
│ ├── MockBidirectionalStream.swift
│ ├── MockClientOnlyAsyncStream.swift
│ ├── MockClientOnlyStream.swift
│ ├── MockServerOnlyAsyncStream.swift
│ ├── MockServerOnlyStream.swift
│ └── README.md
└── ConnectNIO
│ ├── Internal
│ ├── ConnectStreamChannelHandler.swift
│ ├── ConnectUnaryChannelHandler.swift
│ ├── Extensions
│ │ ├── ConnectError+Extensions.swift
│ │ ├── HTTPRequestHead+Extensions.swift
│ │ └── Headers+Extensions.swift
│ └── GRPCInterceptor.swift
│ ├── Public
│ ├── NIOHTTPClient.swift
│ └── NetworkProtocol+Extensions.swift
│ └── README.md
├── MAINTAINERS.md
├── Makefile
├── Package.resolved
├── Package.swift
├── Plugins
├── ConnectMocksPlugin
│ └── ConnectMockGenerator.swift
├── ConnectPluginUtilities
│ ├── FilePathComponents.swift
│ ├── Generator.swift
│ ├── GeneratorOptions.swift
│ ├── MethodDescriptor+Extensions.swift
│ └── ServiceDescriptor+Extensions.swift
└── ConnectSwiftPlugin
│ └── ConnectClientGenerator.swift
├── README.md
├── SECURITY.md
└── Tests
├── ConformanceClient
├── GeneratedSources
│ └── connectrpc
│ │ └── conformance
│ │ └── v1
│ │ ├── client_compat.pb.swift
│ │ ├── config.pb.swift
│ │ ├── server_compat.pb.swift
│ │ ├── service.connect.swift
│ │ ├── service.pb.swift
│ │ └── suite.pb.swift
├── InvocationConfigs
│ ├── nio.yaml
│ └── urlsession.yaml
├── README.md
├── Sources
│ ├── CommandLineArgument.swift
│ ├── ConformanceInvoker.swift
│ └── main.swift
└── buf.gen.yaml
└── UnitTests
├── ConnectLibraryTests
├── ConnectMocksTests
│ └── ConnectMocksTests.swift
├── ConnectTests
│ ├── ConnectEndStreamResponseTests.swift
│ ├── ConnectErrorTests.swift
│ ├── EnvelopeTests.swift
│ ├── GzipCompressionPoolTests.swift
│ ├── InterceptorChainIterationTests.swift
│ ├── InterceptorFactoryTests.swift
│ ├── InterceptorIntegrationTests.swift
│ ├── JSONCodecTests.swift
│ ├── ProtoCodecTests.swift
│ ├── ProtocolClientConfigTests.swift
│ └── ServiceMetadataTests.swift
├── GeneratedSources
│ └── connectrpc
│ │ └── conformance
│ │ └── v1
│ │ ├── client_compat.pb.swift
│ │ ├── config.pb.swift
│ │ ├── server_compat.pb.swift
│ │ ├── service.connect.swift
│ │ ├── service.mock.swift
│ │ ├── service.pb.swift
│ │ └── suite.pb.swift
├── TestResources
│ └── gzip-test.txt.gz
└── buf.gen.yaml
├── ConnectPluginUtilitiesTests
└── FilePathComponentsTests.swift
└── README.md
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Community Code of Conduct
2 |
3 | Connect follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | **Before submitting your PR:** Please read through the contribution guide at https://github.com/connectrpc/connect-swift/blob/main/.github/CONTRIBUTING.md
2 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | changelog:
2 | exclude:
3 | labels:
4 | - ignore-for-release
5 | authors:
6 | - dependabot
7 | categories:
8 | - title: Enhancements
9 | labels:
10 | - enhancement
11 | - title: Bugfixes
12 | labels:
13 | - bug
14 | - title: Other changes
15 | labels:
16 | - "*"
17 |
--------------------------------------------------------------------------------
/.github/workflows/add-to-project.yml:
--------------------------------------------------------------------------------
1 | name: Add issues and PRs to project
2 |
3 | on:
4 | issues:
5 | types:
6 | - opened
7 | - reopened
8 | - transferred
9 | pull_request_target:
10 | types:
11 | - opened
12 | - reopened
13 | issue_comment:
14 | types:
15 | - created
16 |
17 | jobs:
18 | call-workflow-add-to-project:
19 | name: Call workflow to add issue to project
20 | uses: connectrpc/base-workflows/.github/workflows/add-to-project.yaml@main
21 | secrets: inherit
22 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | workflow_dispatch: {} # support manual runs
8 | env:
9 | # Sets the Xcode version to use for the CI.
10 | # Available Versions: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md#xcode
11 | # Ref: https://www.jessesquires.com/blog/2020/01/06/selecting-an-xcode-version-on-github-ci/
12 | DEVELOPER_DIR: /Applications/Xcode_16.3.app/Contents/Developer
13 | permissions:
14 | contents: read
15 | jobs:
16 | build-eliza-cocoapods-example:
17 | runs-on: macos-15
18 | steps:
19 | - uses: actions/checkout@v4
20 | - name: Build Eliza CocoaPods example
21 | run: |
22 | cd Examples/ElizaCocoaPodsApp
23 | pod install
24 | set -o pipefail && xcodebuild -workspace ElizaCocoaPodsApp.xcworkspace -scheme ElizaCocoaPodsApp build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify
25 | build-eliza-swiftpm-example:
26 | runs-on: macos-15
27 | steps:
28 | - uses: actions/checkout@v4
29 | - name: Build Eliza Swift PM example
30 | run: |
31 | cd Examples/ElizaSwiftPackageApp
32 | set -o pipefail && xcodebuild -scheme ElizaSwiftPackageApp build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify
33 | build-library-ios:
34 | runs-on: macos-15
35 | steps:
36 | - uses: actions/checkout@v4
37 | - name: Build Connect iOS library
38 | run: set -o pipefail && xcodebuild -scheme Connect-Package -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.4' | xcbeautify
39 | build-library-macos:
40 | runs-on: macos-15
41 | steps:
42 | - uses: actions/checkout@v4
43 | - name: Build Connect macOS library
44 | run: set -o pipefail && xcodebuild -scheme Connect-Package -destination 'platform=macOS' | xcbeautify
45 | build-library-tvos:
46 | runs-on: macos-15
47 | steps:
48 | - uses: actions/checkout@v4
49 | - name: Build Connect tvOS library
50 | run: set -o pipefail && xcodebuild -scheme Connect-Package -destination 'platform=tvOS Simulator,name=Apple TV,OS=18.4' | xcbeautify
51 | build-library-watchos:
52 | runs-on: macos-15
53 | steps:
54 | - uses: actions/checkout@v4
55 | - name: Build Connect watchOS library
56 | run: set -o pipefail && xcodebuild -scheme Connect-Package -destination 'platform=watchOS Simulator,name=Apple Watch Series 10 (42mm),OS=11.0' | xcbeautify
57 | build-plugin-and-generate:
58 | runs-on: macos-15
59 | steps:
60 | - uses: actions/checkout@v4
61 | - uses: bufbuild/buf-setup-action@v1.50.0
62 | with:
63 | github_token: ${{ github.token }}
64 | - name: Build plugins
65 | run: make buildplugins
66 | - name: Generate outputs
67 | run: make generate
68 | - name: Ensure no generated diff
69 | run: |
70 | git update-index --refresh --add --remove
71 | git diff-index --quiet HEAD --
72 | run-conformance-tests:
73 | runs-on: macos-15
74 | steps:
75 | - uses: actions/checkout@v4
76 | - name: Install conformance runner
77 | run: make installconformancerunner
78 | - name: Run conformance tests
79 | run: make testconformance
80 | run-unit-tests:
81 | runs-on: macos-15
82 | steps:
83 | - uses: actions/checkout@v4
84 | - uses: actions/setup-go@v5
85 | with:
86 | go-version: 1.21.x
87 | - name: Run unit tests
88 | run: make testunit
89 | run-swiftlint:
90 | runs-on: ubuntu-latest
91 | container:
92 | image: ghcr.io/realm/swiftlint:0.58.2
93 | steps:
94 | - uses: actions/checkout@v4
95 | - name: Run SwiftLint
96 | run: swiftlint lint --strict
97 | validate-license-headers:
98 | runs-on: ubuntu-latest
99 | steps:
100 | - uses: actions/checkout@v4
101 | - name: Validate license headers
102 | run: |
103 | make licenseheaders
104 | git update-index --refresh
105 | git diff-index --quiet HEAD --
106 |
--------------------------------------------------------------------------------
/.github/workflows/pr-title.yml:
--------------------------------------------------------------------------------
1 | name: Lint PR Title
2 | # Prevent writing to the repository using the CI token.
3 | # Ref: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions
4 | permissions:
5 | pull-requests: read
6 | on:
7 | pull_request:
8 | # By default, a workflow only runs when a pull_request's activity type is opened,
9 | # synchronize, or reopened. We explicity override here so that PR titles are
10 | # re-linted when the PR text content is edited.
11 | types:
12 | - opened
13 | - edited
14 | - reopened
15 | - synchronize
16 | jobs:
17 | lint:
18 | uses: bufbuild/base-workflows/.github/workflows/pr-title.yaml@main
19 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 | on:
3 | push:
4 | tags:
5 | - "*"
6 | workflow_dispatch: {} # support manual runs
7 | env:
8 | # Sets the Xcode version to use for the CI.
9 | # Available Versions: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md#xcode
10 | # Ref: https://www.jessesquires.com/blog/2020/01/06/selecting-an-xcode-version-on-github-ci/
11 | DEVELOPER_DIR: /Applications/Xcode_16.3.app/Contents/Developer
12 | permissions:
13 | contents: write
14 | jobs:
15 | release:
16 | runs-on: macos-15
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: bufbuild/buf-setup-action@v1.50.0
20 | with:
21 | github_token: ${{ github.token }}
22 | - name: Build plugins
23 | run: make buildplugins
24 | - name: Zip artifacts
25 | run: |
26 | cd ./.tmp/bin
27 | mkdir ./artifacts
28 | tar -zcvf ./artifacts/protoc-gen-connect-swift.tar.gz ./protoc-gen-connect-swift
29 | tar -zcvf ./artifacts/protoc-gen-connect-swift-mocks.tar.gz ./protoc-gen-connect-swift-mocks
30 | cd ./artifacts
31 | for file in $(find . -maxdepth 1 -type f | sed 's/^\.\///' | sort | uniq); do
32 | shasum -a 256 "${file}" >> sha256.txt
33 | done
34 | - name: Publish release
35 | uses: softprops/action-gh-release@v2
36 | with:
37 | generate_release_notes: true
38 | append_body: true
39 | files: |
40 | ./.tmp/bin/artifacts/*
41 | publish-podspecs:
42 | runs-on: macos-15
43 | steps:
44 | - uses: actions/checkout@v4
45 | - name: Publish podspecs to CocoaPods
46 | env:
47 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
48 | # Note that --synchronous is used since Mocks depends on the primary spec.
49 | run: |
50 | pod trunk push Connect-Swift.podspec --allow-warnings --synchronous
51 | pod trunk push Connect-Swift-Mocks.podspec --allow-warnings --synchronous
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.xcscheme
2 | *.xcsettings
3 | *.xcuserdatad
4 | *.xcuserstate
5 | *.xcworkspacedata
6 | .build
7 | .DS_Store
8 | /.tmp
9 | DerivedData
10 | Examples/ElizaCocoaPodsApp/ElizaCocoaPodsApp.xcworkspace
11 | Pods
12 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | included:
2 | - Examples/ElizaSharedSources/AppSources
3 | - Libraries
4 | - Plugins
5 | - Tests
6 | excluded:
7 | - Libraries/Connect/Internal/GeneratedSources
8 | - Tests/ConformanceClient/GeneratedSources
9 | - Tests/UnitTests/ConnectLibraryTests/GeneratedSources
10 | disabled_rules:
11 | - blanket_disable_command
12 | - cyclomatic_complexity
13 | - file_length
14 | - function_body_length
15 | - function_parameter_count
16 | - identifier_name
17 | - opening_brace
18 | - nesting
19 | - redundant_string_enum_value
20 | - todo
21 | - type_body_length
22 | - type_name
23 | - unavailable_function
24 | opt_in_rules:
25 | - array_init
26 | - attributes
27 | - closure_end_indentation
28 | - closure_spacing
29 | - collection_alignment
30 | - contains_over_filter_count
31 | - contains_over_filter_is_empty
32 | - contains_over_first_not_nil
33 | - contains_over_range_nil_comparison
34 | - discouraged_object_literal
35 | - empty_collection_literal
36 | - empty_count
37 | - empty_string
38 | - empty_xctest_method
39 | - enum_case_associated_values_count
40 | - explicit_init
41 | - fallthrough
42 | - fatal_error_message
43 | - file_name
44 | - first_where
45 | - flatmap_over_map_reduce
46 | - identical_operands
47 | - inclusive_language
48 | - joined_default_parameter
49 | - legacy_random
50 | - let_var_whitespace
51 | - last_where
52 | - literal_expression_end_indentation
53 | - lower_acl_than_parent
54 | - modifier_order
55 | - nimble_operator
56 | - nslocalizedstring_key
57 | - number_separator
58 | - operator_usage_whitespace
59 | - overridden_super_call
60 | - override_in_extension
61 | - prefer_self_in_static_references
62 | - private_action
63 | - prohibited_super_call
64 | - quick_discouraged_call
65 | - quick_discouraged_focused_test
66 | - quick_discouraged_pending_test
67 | - reduce_into
68 | - redundant_nil_coalescing
69 | - redundant_type_annotation
70 | - single_test_class
71 | - sorted_first_last
72 | - sorted_imports
73 | - static_operator
74 | - toggle_bool
75 | - unavailable_function
76 | - unneeded_parentheses_in_closure_argument
77 | - vertical_parameter_alignment_on_call
78 | - vertical_whitespace_closing_braces
79 | - vertical_whitespace_opening_braces
80 | - xct_specific_matcher
81 | - yoda_condition
82 | trailing_whitespace:
83 | ignores_comments: false
84 | ignores_empty_lines: false
85 | trailing_comma:
86 | mandatory_comma: true
87 | line_length: 100
88 | private_over_fileprivate:
89 | validate_extensions: true
90 | modifier_order:
91 | preferred_modifier_order:
92 | - acl
93 | - setterACL
94 | - final
95 | - override
96 | - required
97 | - typeMethods
98 | - mutators
99 | - owned
100 | - lazy
101 | - dynamic
102 | - convenience
103 | deployment_target:
104 | iOS_deployment_target: 12.0
105 |
106 | custom_rules:
107 | newline_after_brace:
108 | name: "Opening braces shouldn't have empty lines under them"
109 | regex: '\{\n\n'
110 | newline_before_brace:
111 | name: "Closing braces shouldn't have empty lines before them"
112 | regex: '\n\n\}'
113 | sendable_order:
114 | name: "@escaping should precede @Sendable when used together"
115 | regex: '@Sendable\s+@escaping'
116 | space_before_comma:
117 | name: "Commas should never have a space before them"
118 | regex: '\s+,'
119 | spaces_over_tabs:
120 | name: "Use (4) spaces instead of tabs"
121 | regex: '\t'
122 | xctestcase:
123 | name: "Use XCTestCase over XCTest to ensure tests run properly"
124 | regex: ': XCTest[\s,]+'
125 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDETemplateMacros.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FILEHEADER
6 | Copyright 2022-2025 The Connect Authors
7 | //
8 | // Licensed under the Apache License, Version 2.0 (the "License");
9 | // you may not use this file except in compliance with the License.
10 | // You may obtain a copy of the License at
11 | //
12 | // http://www.apache.org/licenses/LICENSE-2.0
13 | //
14 | // Unless required by applicable law or agreed to in writing, software
15 | // distributed under the License is distributed on an "AS IS" BASIS,
16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | // See the License for the specific language governing permissions and
18 | // limitations under the License.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Connect-Swift-Mocks.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'Connect-Swift-Mocks'
3 | spec.module_name = 'ConnectMocks'
4 | spec.version = '1.0.3'
5 | spec.license = { :type => 'Apache 2.0', :file => 'LICENSE' }
6 | spec.summary = 'Mocks for testing with Connect-Swift.'
7 | spec.homepage = 'https://github.com/connectrpc/connect-swift'
8 | spec.author = 'The Connect Authors'
9 | spec.source = { :git => 'https://github.com/connectrpc/connect-swift.git', :tag => spec.version }
10 |
11 | spec.ios.deployment_target = '12.0'
12 | spec.osx.deployment_target = '10.15'
13 | spec.tvos.deployment_target = '13.0'
14 | spec.watchos.deployment_target = '6.0'
15 |
16 | spec.dependency 'Connect-Swift', "#{spec.version.to_s}"
17 | spec.dependency 'SwiftProtobuf', '~> 1.28.2'
18 |
19 | spec.source_files = 'Libraries/ConnectMocks/**/*.swift'
20 |
21 | spec.swift_versions = ['5.0']
22 | end
23 |
--------------------------------------------------------------------------------
/Connect-Swift.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'Connect-Swift'
3 | spec.module_name = 'Connect'
4 | spec.version = '1.0.3'
5 | spec.license = { :type => 'Apache 2.0', :file => 'LICENSE' }
6 | spec.summary = 'Idiomatic gRPC & Connect RPCs for Swift.'
7 | spec.homepage = 'https://github.com/connectrpc/connect-swift'
8 | spec.author = 'The Connect Authors'
9 | spec.source = { :git => 'https://github.com/connectrpc/connect-swift.git', :tag => spec.version }
10 |
11 | spec.ios.deployment_target = '12.0'
12 | spec.osx.deployment_target = '10.15'
13 | spec.tvos.deployment_target = '13.0'
14 | spec.watchos.deployment_target = '6.0'
15 |
16 | spec.dependency 'SwiftProtobuf', '~> 1.28.2'
17 |
18 | spec.source_files = 'Libraries/Connect/**/*.swift'
19 |
20 | spec.swift_versions = ['5.0']
21 | end
22 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/ElizaCocoaPodsApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/ElizaCocoaPodsApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/ElizaCocoaPodsApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/ElizaCocoaPodsApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/ElizaCocoaPodsApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '15.0'
2 |
3 | target 'ElizaCocoaPodsApp' do
4 | use_frameworks!
5 |
6 | # For real projects, use a version instead of a path:
7 | # pod 'Connect-Swift', '~> x.y.z'
8 | pod 'Connect-Swift', :path => '../..'
9 | end
10 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Connect-Swift (1.0.3):
3 | - SwiftProtobuf (~> 1.28.2)
4 | - SwiftProtobuf (1.28.2)
5 |
6 | DEPENDENCIES:
7 | - Connect-Swift (from `../..`)
8 |
9 | SPEC REPOS:
10 | trunk:
11 | - SwiftProtobuf
12 |
13 | EXTERNAL SOURCES:
14 | Connect-Swift:
15 | :path: "../.."
16 |
17 | SPEC CHECKSUMS:
18 | Connect-Swift: 537f3cb4148768054c7310ca0382c4a36011b4af
19 | SwiftProtobuf: 4dbaffec76a39a8dc5da23b40af1a5dc01a4c02d
20 |
21 | PODFILE CHECKSUM: b598f373a6ab5add976b09c2ac79029bf2200d48
22 |
23 | COCOAPODS: 1.15.2
24 |
--------------------------------------------------------------------------------
/Examples/ElizaCocoaPodsApp/README.md:
--------------------------------------------------------------------------------
1 | # ElizaSwiftPackageApp example
2 |
3 | This example app imports the `Connect` library using CocoaPods,
4 | and provides an interface for
5 | [chatting with Eliza](https://connectrpc.com/demo).
6 |
7 | The app has support for chatting using a variety of protocols supported by
8 | the Connect library:
9 |
10 | - [Connect](https://connectrpc.com) + unary
11 | - [Connect](https://connectrpc.com) + streaming
12 | - [gRPC-Web](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) + unary
13 | - [gRPC-Web](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) + streaming
14 |
15 | **Note that vanilla gRPC support is not available in this example because
16 | [SwiftNIO does not support CocoaPods](https://github.com/apple/swift-nio/issues/2393).**
17 |
18 | ## Try it out
19 |
20 | 1. Ensure you have CocoaPods installed (`brew install cocoapods`)
21 | 2. `cd` into this directory and install the pods (`pod install`)
22 | 3. Open the generated `.xcworkspace` file (`xed .`)
23 | 4. Build the app target using Xcode
24 |
25 | Note that the [`Podfile`](./Podfile) uses a local path reference to the
26 | Connect library in this repository, rather than the one in the CocoaPods
27 | specs repo.
28 |
--------------------------------------------------------------------------------
/Examples/ElizaSharedSources/AppSources/ElizaApp.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2022-2025 The Connect Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import SwiftUI
16 |
17 | @main
18 | struct ElizaApp: App {
19 | var body: some Scene {
20 | WindowGroup {
21 | MenuView()
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Examples/ElizaSharedSources/AppSources/Message.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2022-2025 The Connect Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Foundation
16 |
17 | struct Message {
18 | enum Author {
19 | case eliza
20 | case user
21 | }
22 |
23 | let id = UUID()
24 | let message: String
25 | let author: Author
26 | }
27 |
28 | extension Message: Identifiable {
29 | typealias ID = UUID
30 | }
31 |
--------------------------------------------------------------------------------
/Examples/ElizaSharedSources/AppSources/MessagingView.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2022-2025 The Connect Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Combine
16 | import SwiftUI
17 |
18 | struct MessagingView: View {
19 | @State private var currentMessage = ""
20 | @ObservedObject private var viewModel: ViewModel
21 |
22 | @Environment(\.presentationMode)
23 | private var presentationMode
24 |
25 | init(viewModel: ViewModel) {
26 | self.viewModel = viewModel
27 | }
28 |
29 | var body: some View {
30 | VStack {
31 | ScrollViewReader { listView in
32 | // ScrollViewReader crashes in iOS 16 with ListView:
33 | // https://developer.apple.com/forums/thread/712510
34 | // Using ScrollView + ForEach as a workaround.
35 | ScrollView {
36 | ForEach(self.viewModel.messages) { message in
37 | VStack {
38 | switch message.author {
39 | case .user:
40 | HStack {
41 | Spacer()
42 | Text("You")
43 | .foregroundColor(.gray)
44 | .fontWeight(.semibold)
45 | }
46 | HStack {
47 | Spacer()
48 | Text(message.message)
49 | .multilineTextAlignment(.trailing)
50 | }
51 | case .eliza:
52 | HStack {
53 | Text("Eliza")
54 | .foregroundColor(.blue)
55 | .fontWeight(.semibold)
56 | Spacer()
57 | }
58 | HStack {
59 | Text(message.message)
60 | .multilineTextAlignment(.leading)
61 | Spacer()
62 | }
63 | }
64 | }
65 | .id(message.id)
66 | }
67 | }
68 | .onChange(of: self.viewModel.messages.count) { messageCount in
69 | listView.scrollTo(self.viewModel.messages[messageCount - 1].id)
70 | }
71 | }
72 |
73 | HStack {
74 | TextField("Write your message...", text: self.$currentMessage)
75 | .onSubmit { self.sendMessage() }
76 | .submitLabel(.send)
77 | Button("Send", action: { self.sendMessage() })
78 | .foregroundColor(.blue)
79 | }
80 | }
81 | .padding()
82 | .navigationBarTitleDisplayMode(.inline)
83 | .navigationBarBackButtonHidden(true)
84 | .toolbar {
85 | ToolbarItem(placement: .navigationBarTrailing) {
86 | Button("End Chat") {
87 | self.viewModel.endChat()
88 | self.presentationMode.wrappedValue.dismiss()
89 | }
90 | }
91 | }
92 | }
93 |
94 | private func sendMessage() {
95 | let messageToSend = self.currentMessage
96 | if messageToSend.isEmpty {
97 | return
98 | }
99 |
100 | Task { await self.viewModel.send(messageToSend) }
101 | self.currentMessage = ""
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Examples/ElizaSharedSources/AppSources/MessagingViewModel.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2022-2025 The Connect Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Combine
16 | import Connect
17 | import os.log
18 |
19 | private typealias ConverseRequest = Connectrpc_Eliza_V1_ConverseRequest
20 | private typealias ConverseResponse = Connectrpc_Eliza_V1_ConverseResponse
21 |
22 | private typealias SayRequest = Connectrpc_Eliza_V1_SayRequest
23 | private typealias SayResponse = Connectrpc_Eliza_V1_SayResponse
24 |
25 | /// View model that can be injected into a `MessagingView`.
26 | @MainActor
27 | protocol MessagingViewModel: ObservableObject {
28 | /// The current set of messages. Observable by storing the view model as an `@ObservedObject`.
29 | var messages: [Message] { get }
30 |
31 | /// Send a message to the upstream service.
32 | /// This message and any responses will be appended to `messages`.
33 | ///
34 | /// - parameter message: The message to send.
35 | func send(_ message: String) async
36 |
37 | /// End the chat session (and close connections if needed).
38 | func endChat()
39 | }
40 |
41 | /// View model that uses unary requests for messaging.
42 | @MainActor
43 | final class UnaryMessagingViewModel: MessagingViewModel {
44 | private let client: Connectrpc_Eliza_V1_ElizaServiceClientInterface
45 |
46 | @Published private(set) var messages = [Message]()
47 |
48 | init(client: Connectrpc_Eliza_V1_ElizaServiceClientInterface) {
49 | self.client = client
50 | }
51 |
52 | func send(_ sentence: String) async {
53 | let request = SayRequest.with { $0.sentence = sentence }
54 | self.messages.append(Message(message: sentence, author: .user))
55 |
56 | let response = await self.client.say(request: request, headers: [:])
57 | os_log(.debug, "Eliza unary response: %@", String(describing: response))
58 | self.messages.append(Message(
59 | message: response.message?.sentence ?? "No response", author: .eliza
60 | ))
61 | }
62 |
63 | func endChat() {}
64 | }
65 |
66 | /// View model that uses bidirectional streaming for messaging.
67 | @MainActor
68 | final class BidirectionalStreamingMessagingViewModel: MessagingViewModel {
69 | private let client: Connectrpc_Eliza_V1_ElizaServiceClientInterface
70 | private lazy var elizaStream = self.client.converse(headers: [:])
71 |
72 | @Published private(set) var messages = [Message]()
73 |
74 | init(client: Connectrpc_Eliza_V1_ElizaServiceClientInterface) {
75 | self.client = client
76 | self.observeResponses()
77 | }
78 |
79 | func send(_ sentence: String) async {
80 | do {
81 | let request = ConverseRequest.with { $0.sentence = sentence }
82 | self.messages.append(Message(message: sentence, author: .user))
83 | try self.elizaStream.send(request)
84 | } catch let error {
85 | os_log(
86 | .error, "Failed to write message to stream: %@", error.localizedDescription
87 | )
88 | }
89 | }
90 |
91 | func endChat() {
92 | self.elizaStream.close()
93 | }
94 |
95 | private func observeResponses() {
96 | Task {
97 | for await result in self.elizaStream.results() {
98 | switch result {
99 | case .headers(let headers):
100 | os_log(.debug, "Eliza headers: %@", headers)
101 |
102 | case .message(let message):
103 | os_log(.debug, "Eliza message: %@", String(describing: message))
104 | self.messages.append(Message(message: message.sentence, author: .eliza))
105 |
106 | case .complete(_, let error, let trailers):
107 | os_log(.debug, "Eliza completed with trailers: %@", trailers ?? [:])
108 | let sentence: String
109 | if let error = error {
110 | os_log(.error, "Eliza error: %@", error.localizedDescription)
111 | sentence = "[Error: \(error)]"
112 | } else {
113 | sentence = "[Conversation ended]"
114 | }
115 | self.messages.append(Message(message: sentence, author: .eliza))
116 | }
117 | }
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Examples/ElizaSharedSources/GeneratedSources/connectrpc/eliza/v1/eliza.connect.swift:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-connect-swift. DO NOT EDIT.
2 | //
3 | // Source: connectrpc/eliza/v1/eliza.proto
4 | //
5 |
6 | import Connect
7 | import Foundation
8 | import SwiftProtobuf
9 |
10 | /// ElizaService provides a way to talk to Eliza, a port of the DOCTOR script
11 | /// for Joseph Weizenbaum's original ELIZA program. Created in the mid-1960s at
12 | /// the MIT Artificial Intelligence Laboratory, ELIZA demonstrates the
13 | /// superficiality of human-computer communication. DOCTOR simulates a
14 | /// psychotherapist, and is commonly found as an Easter egg in emacs
15 | /// distributions.
16 | internal protocol Connectrpc_Eliza_V1_ElizaServiceClientInterface: Sendable {
17 |
18 | /// Say is a unary RPC. Eliza responds to the prompt with a single sentence.
19 | @available(iOS 13, *)
20 | func `say`(request: Connectrpc_Eliza_V1_SayRequest, headers: Connect.Headers) async -> ResponseMessage
21 |
22 | /// Converse is a bidirectional RPC. The caller may exchange multiple
23 | /// back-and-forth messages with Eliza over a long-lived connection. Eliza
24 | /// responds to each ConverseRequest with a ConverseResponse.
25 | @available(iOS 13, *)
26 | func `converse`(headers: Connect.Headers) -> any Connect.BidirectionalAsyncStreamInterface
27 |
28 | /// Introduce is a server streaming RPC. Given the caller's name, Eliza
29 | /// returns a stream of sentences to introduce itself.
30 | @available(iOS 13, *)
31 | func `introduce`(headers: Connect.Headers) -> any Connect.ServerOnlyAsyncStreamInterface
32 | }
33 |
34 | /// Concrete implementation of `Connectrpc_Eliza_V1_ElizaServiceClientInterface`.
35 | internal final class Connectrpc_Eliza_V1_ElizaServiceClient: Connectrpc_Eliza_V1_ElizaServiceClientInterface, Sendable {
36 | private let client: Connect.ProtocolClientInterface
37 |
38 | internal init(client: Connect.ProtocolClientInterface) {
39 | self.client = client
40 | }
41 |
42 | @available(iOS 13, *)
43 | internal func `say`(request: Connectrpc_Eliza_V1_SayRequest, headers: Connect.Headers = [:]) async -> ResponseMessage {
44 | return await self.client.unary(path: "/connectrpc.eliza.v1.ElizaService/Say", idempotencyLevel: .noSideEffects, request: request, headers: headers)
45 | }
46 |
47 | @available(iOS 13, *)
48 | internal func `converse`(headers: Connect.Headers = [:]) -> any Connect.BidirectionalAsyncStreamInterface {
49 | return self.client.bidirectionalStream(path: "/connectrpc.eliza.v1.ElizaService/Converse", headers: headers)
50 | }
51 |
52 | @available(iOS 13, *)
53 | internal func `introduce`(headers: Connect.Headers = [:]) -> any Connect.ServerOnlyAsyncStreamInterface {
54 | return self.client.serverOnlyStream(path: "/connectrpc.eliza.v1.ElizaService/Introduce", headers: headers)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Examples/ElizaSharedSources/README.md:
--------------------------------------------------------------------------------
1 | # ElizaSharedSources example
2 |
3 | This directory contains sources that are shared by the Eliza example apps.
4 | See the examples for more details and usage:
5 |
6 | - [ElizaCocoaPodsApp](../ElizaCocoaPodsApp)
7 | - [ElizaSwiftPackageApp](../ElizaSwiftPackageApp)
8 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/ElizaSwiftPackageApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/ElizaSwiftPackageApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "swift-atomics",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/apple/swift-atomics.git",
7 | "state" : {
8 | "revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10",
9 | "version" : "1.1.0"
10 | }
11 | },
12 | {
13 | "identity" : "swift-collections",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/apple/swift-collections.git",
16 | "state" : {
17 | "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
18 | "version" : "1.1.4"
19 | }
20 | },
21 | {
22 | "identity" : "swift-nio",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/apple/swift-nio.git",
25 | "state" : {
26 | "revision" : "34d486b01cd891297ac615e40d5999536a1e138d",
27 | "version" : "2.83.0"
28 | }
29 | },
30 | {
31 | "identity" : "swift-nio-http2",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/apple/swift-nio-http2.git",
34 | "state" : {
35 | "revision" : "4281466512f63d1bd530e33f4aa6993ee7864be0",
36 | "version" : "1.36.0"
37 | }
38 | },
39 | {
40 | "identity" : "swift-nio-ssl",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/apple/swift-nio-ssl.git",
43 | "state" : {
44 | "revision" : "4b38f35946d00d8f6176fe58f96d83aba64b36c7",
45 | "version" : "2.31.0"
46 | }
47 | },
48 | {
49 | "identity" : "swift-protobuf",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/apple/swift-protobuf.git",
52 | "state" : {
53 | "revision" : "ebc7251dd5b37f627c93698e4374084d98409633",
54 | "version" : "1.28.2"
55 | }
56 | },
57 | {
58 | "identity" : "swift-system",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/apple/swift-system.git",
61 | "state" : {
62 | "revision" : "a34201439c74b53f0fd71ef11741af7e7caf01e1",
63 | "version" : "1.4.2"
64 | }
65 | }
66 | ],
67 | "version" : 2
68 | }
69 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/ElizaSwiftPackageApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/ElizaSwiftPackageApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/ElizaSwiftPackageApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/ElizaSwiftPackageApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Examples/ElizaSwiftPackageApp/README.md:
--------------------------------------------------------------------------------
1 | # ElizaSwiftPackageApp example
2 |
3 | This example app imports the `Connect` library using Swift Package Manager,
4 | and provides an interface for
5 | [chatting with Eliza](https://buf.build/connectrpc/eliza).
6 |
7 | The app has support for chatting using a variety of protocols supported by
8 | the Connect library:
9 |
10 | - [Connect](https://connectrpc.com) + unary
11 | - [Connect](https://connectrpc.com) + streaming
12 | - [gRPC](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) + unary (using `ConnectGRPC` + `SwiftNIO`)
13 | - [gRPC](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) + streaming (using `ConnectGRPC` + `SwiftNIO`)
14 | - [gRPC-Web](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) + unary
15 | - [gRPC-Web](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) + streaming
16 |
17 | ## Try it out
18 |
19 | Simply open the `.xcodeproj` in this directory and build the app target
20 | using Xcode.
21 |
22 | Note that the project uses a local reference to the Connect package,
23 | rather than the GitHub URL.
24 |
--------------------------------------------------------------------------------
/Examples/README.md:
--------------------------------------------------------------------------------
1 | # ConnectExamples
2 |
3 | This directory contains comprehensive example apps that utilize
4 | Connect-Swift and provide the ability to [chat with ELIZA][eliza-demo] over
5 | each supported protocol using either unary or streaming APIs:
6 |
7 | - [`ElizaSwiftPackageApp`](./ElizaSwiftPackageApp): Uses
8 | **Swift Package Manager**.
9 | - [`ElizaCocoaPodsApp`](./ElizaCocoaPodsApp): Uses **CocoaPods**.
10 |
11 | [eliza-demo]: https://connectrpc.com/demo
12 |
--------------------------------------------------------------------------------
/Examples/buf.gen.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | plugins:
3 | - plugin: buf.build/apple/swift:v1.28.2
4 | opt: Visibility=Internal
5 | out: ./ElizaSharedSources/GeneratedSources
6 | - name: connect-swift
7 | opt:
8 | - GenerateServiceMetadata=false
9 | - Visibility=Internal
10 | out: ./ElizaSharedSources/GeneratedSources
11 | path: ../.tmp/bin/protoc-gen-connect-swift
12 |
--------------------------------------------------------------------------------
/Libraries/Connect/Internal/GeneratedSources/proto/grpc/status/v1/status.pb.swift:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT.
2 | // swift-format-ignore-file
3 | // swiftlint:disable all
4 | //
5 | // Generated by the Swift generator plugin for the protocol buffer compiler.
6 | // Source: proto/grpc/status/v1/status.proto
7 | //
8 | // For information on using the generated types, please see the documentation:
9 | // https://github.com/apple/swift-protobuf/
10 |
11 | // Copyright 2022-2025 The Connect Authors
12 | //
13 | // Licensed under the Apache License, Version 2.0 (the "License");
14 | // you may not use this file except in compliance with the License.
15 | // You may obtain a copy of the License at
16 | //
17 | // http://www.apache.org/licenses/LICENSE-2.0
18 | //
19 | // Unless required by applicable law or agreed to in writing, software
20 | // distributed under the License is distributed on an "AS IS" BASIS,
21 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 | // See the License for the specific language governing permissions and
23 | // limitations under the License.
24 |
25 | import SwiftProtobuf
26 |
27 | // If the compiler emits an error on this type, it is because this file
28 | // was generated by a version of the `protoc` Swift plug-in that is
29 | // incompatible with the version of SwiftProtobuf to which you are linking.
30 | // Please ensure that you are building against the same version of the API
31 | // that was used to generate this file.
32 | fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
33 | struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
34 | typealias Version = _2
35 | }
36 |
37 | /// See https://cloud.google.com/apis/design/errors.
38 | ///
39 | /// This struct must remain binary-compatible with
40 | /// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto.
41 | struct Grpc_Status_V1_Status: Sendable {
42 | // SwiftProtobuf.Message conformance is added in an extension below. See the
43 | // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
44 | // methods supported on all messages.
45 |
46 | /// a google.rpc.Code
47 | var code: Int32 = 0
48 |
49 | /// developer-facing, English (localize in details or client-side)
50 | var message: String = String()
51 |
52 | var details: [SwiftProtobuf.Google_Protobuf_Any] = []
53 |
54 | var unknownFields = SwiftProtobuf.UnknownStorage()
55 |
56 | init() {}
57 | }
58 |
59 | // MARK: - Code below here is support for the SwiftProtobuf runtime.
60 |
61 | fileprivate let _protobuf_package = "grpc.status.v1"
62 |
63 | extension Grpc_Status_V1_Status: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
64 | static let protoMessageName: String = _protobuf_package + ".Status"
65 | static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
66 | 1: .same(proto: "code"),
67 | 2: .same(proto: "message"),
68 | 3: .same(proto: "details"),
69 | ]
70 |
71 | mutating func decodeMessage(decoder: inout D) throws {
72 | while let fieldNumber = try decoder.nextFieldNumber() {
73 | // The use of inline closures is to circumvent an issue where the compiler
74 | // allocates stack space for every case branch when no optimizations are
75 | // enabled. https://github.com/apple/swift-protobuf/issues/1034
76 | switch fieldNumber {
77 | case 1: try { try decoder.decodeSingularInt32Field(value: &self.code) }()
78 | case 2: try { try decoder.decodeSingularStringField(value: &self.message) }()
79 | case 3: try { try decoder.decodeRepeatedMessageField(value: &self.details) }()
80 | default: break
81 | }
82 | }
83 | }
84 |
85 | func traverse(visitor: inout V) throws {
86 | if self.code != 0 {
87 | try visitor.visitSingularInt32Field(value: self.code, fieldNumber: 1)
88 | }
89 | if !self.message.isEmpty {
90 | try visitor.visitSingularStringField(value: self.message, fieldNumber: 2)
91 | }
92 | if !self.details.isEmpty {
93 | try visitor.visitRepeatedMessageField(value: self.details, fieldNumber: 3)
94 | }
95 | try unknownFields.traverse(visitor: &visitor)
96 | }
97 |
98 | static func ==(lhs: Grpc_Status_V1_Status, rhs: Grpc_Status_V1_Status) -> Bool {
99 | if lhs.code != rhs.code {return false}
100 | if lhs.message != rhs.message {return false}
101 | if lhs.details != rhs.details {return false}
102 | if lhs.unknownFields != rhs.unknownFields {return false}
103 | return true
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Libraries/Connect/Internal/Streaming/BidirectionalAsyncStream.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2022-2025 The Connect Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import SwiftProtobuf
16 |
17 | /// Concrete **internal** implementation of `BidirectionalAsyncStreamInterface`.
18 | /// Provides the necessary wiring to bridge from closures/callbacks to Swift's `AsyncStream`
19 | /// to work with async/await.
20 | ///
21 | /// If the library removes callback support in favor of only supporting async/await in the future,
22 | /// this class can be simplified.
23 | @available(iOS 13, *)
24 | class BidirectionalAsyncStream<
25 | Input: ProtobufMessage, Output: ProtobufMessage
26 | >: @unchecked Sendable {
27 | /// The underlying async stream that will be exposed to the consumer.
28 | /// Force unwrapped because it captures `self` on `init`.
29 | private var asyncStream: AsyncStream>!
30 | /// Stored closure to provide access to the `AsyncStream.Continuation` so that result data
31 | /// can be passed through to the `AsyncStream` when received.
32 | /// Force unwrapped because it must be set within the context of the `AsyncStream.Continuation`.
33 | private var receiveResult: ((StreamResult