main f63736db6fe7 cached
56 files
135.8 KB
36.9k tokens
1 requests
Download .txt
Repository: launchdarkly/swift-eventsource
Branch: main
Commit: f63736db6fe7
Files: 56
Total size: 135.8 KB

Directory structure:
gitextract_1la2044u/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── actions/
│   │   ├── build-docs/
│   │   │   └── action.yml
│   │   ├── build-ios/
│   │   │   └── action.yml
│   │   ├── build-macos/
│   │   │   └── action.yml
│   │   ├── build-tvos/
│   │   │   └── action.yml
│   │   ├── build-watchos/
│   │   │   └── action.yml
│   │   ├── contract-tests/
│   │   │   └── action.yml
│   │   ├── lint/
│   │   │   └── action.yml
│   │   ├── publish/
│   │   │   └── action.yml
│   │   ├── publish-docs/
│   │   │   └── action.yml
│   │   ├── test-swiftpm/
│   │   │   └── action.yml
│   │   └── update-versions/
│   │       └── action.yml
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       ├── lint-pr-title.yml
│       ├── manual-publish-docs.yml
│       ├── manual-publish.yml
│       ├── release-please.yml
│       └── stale.yml
├── .gitignore
├── .jazzy.yaml
├── .release-please-manifest.json
├── .swiftlint.yml
├── CHANGELOG.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── ContractTestService/
│   ├── .gitignore
│   ├── Package.resolved
│   ├── Package.swift
│   ├── README.md
│   └── Sources/
│       └── ContractTestService/
│           └── main.swift
├── LDSwiftEventSource.podspec
├── LDSwiftEventSource.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           └── LDSwiftEventSource.xcscheme
├── LICENSE.txt
├── Makefile
├── Package.swift
├── README.md
├── SECURITY.md
├── Source/
│   ├── .swiftlint.yml
│   ├── EventParser.swift
│   ├── Info.plist
│   ├── LDSwiftEventSource.h
│   ├── LDSwiftEventSource.swift
│   ├── Logs.swift
│   ├── Types.swift
│   └── UTF8LineParser.swift
├── Tests/
│   ├── .swiftlint.yml
│   ├── EventParserTests.swift
│   ├── LDSwiftEventSourceTests.swift
│   ├── MockHandler.swift
│   ├── TestUtil.swift
│   └── UTF8LineParserTests.swift
└── release-please-config.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To reproduce**
Steps to reproduce the behavior.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Logs**
If applicable, add any log output related to your problem.

**Library version**
The version that you are using.

**XCode and Swift version**
For instance, XCode 11.5, Swift 5.1.

**Platform the issue occurs on**
iPhone, iPad, macOS, tvOS, or watchOS.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I would love to see the library [...does something new...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context about the feature request here.


================================================
FILE: .github/actions/build-docs/action.yml
================================================
name: Build Documentation
description: 'Build Documentation.'

runs:
  using: composite
  steps:
    - name: Install jazzy gem
      shell: bash
      run: gem install jazzy

    - name: Build Documentation
      shell: bash
      run: jazzy -o docs

    - name: Validate coverage
      shell: bash
      run: |
        FULLDOC=`jq '.warnings | length == 0' docs/undocumented.json`
        [ $FULLDOC == "true" ]


================================================
FILE: .github/actions/build-ios/action.yml
================================================
name: Build & Test iOS
description: 'Build for iOS device and run tests on iOS Simulator.'
inputs:
  ios-sim:
    description: 'iOS Simulator to use for testing'
    required: true

runs:
  using: composite
  steps:
    # Workaround for intermittent macos-15 runner issue where simulator
    # runtimes aren't loaded. Listing devices forces the simulator service
    # to initialize. See https://github.com/actions/runner-images/issues/12948
    - name: Prepare iOS Simulator runtime
      shell: bash
      run: xcrun simctl list devices available

    - name: Build Tests for iOS device
      shell: bash
      run: xcodebuild build-for-testing -scheme 'LDSwiftEventSource' -sdk iphoneos CODE_SIGN_IDENTITY= | xcpretty

    - name: Build & Test on iOS Simulator
      shell: bash
      run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk iphonesimulator -destination '${{ inputs.ios-sim }}' CODE_SIGN_IDENTITY= | xcpretty


================================================
FILE: .github/actions/build-macos/action.yml
================================================
name: Build & Test macOS
description: 'Build and test for macOS.'

runs:
  using: composite
  steps:
    - name: Build & Test on macOS
      shell: bash
      run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk macosx -destination 'platform=macOS' | xcpretty

    - name: Build for ARM64 macOS
      shell: bash
      run: xcodebuild build -scheme 'LDSwiftEventSource' -arch arm64e -sdk macosx | xcpretty


================================================
FILE: .github/actions/build-tvos/action.yml
================================================
name: Build & Test tvOS
description: 'Build for tvOS device and run tests on tvOS Simulator.'

runs:
  using: composite
  steps:
    # Workaround for intermittent macos-15 runner issue where simulator
    # runtimes aren't loaded. Listing devices forces the simulator service
    # to initialize. See https://github.com/actions/runner-images/issues/12948
    - name: Prepare tvOS Simulator runtime
      shell: bash
      run: xcrun simctl list devices available

    - name: Build Tests for tvOS device
      shell: bash
      run: xcodebuild build-for-testing -scheme 'LDSwiftEventSource' -sdk appletvos CODE_SIGN_IDENTITY= | xcpretty

    - name: Build & Test on tvOS Simulator
      shell: bash
      run: xcodebuild test -scheme 'LDSwiftEventSource' -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' | xcpretty


================================================
FILE: .github/actions/build-watchos/action.yml
================================================
name: Build watchOS
description: 'Build for watchOS device and simulator.'

runs:
  using: composite
  steps:
    # Workaround for intermittent macos-15 runner issue where simulator
    # runtimes aren't loaded. Listing devices forces the simulator service
    # to initialize. See https://github.com/actions/runner-images/issues/12948
    - name: Prepare watchOS Simulator runtime
      shell: bash
      run: xcrun simctl list devices available

    - name: Build for watchOS simulator
      shell: bash
      run: xcodebuild build -scheme 'LDSwiftEventSource' -sdk watchsimulator | xcpretty

    - name: Build for watchOS device
      shell: bash
      run: xcodebuild build -scheme 'LDSwiftEventSource' -sdk watchos | xcpretty


================================================
FILE: .github/actions/contract-tests/action.yml
================================================
name: Contract Tests
description: 'Build and run SDK contract tests.'
inputs:
  token:
    description: 'GH token used to download SDK test harness.'
    required: true

runs:
  using: composite
  steps:
    - name: Build contract test service
      shell: bash
      run: make build-contract-tests

    - name: Start contract test service
      shell: bash
      run: make start-contract-test-service-bg

    - name: Run contract tests
      uses: launchdarkly/gh-actions/actions/contract-tests@main
      with:
        repo: sse-contract-tests
        branch: main
        version: v2
        token: ${{ inputs.token }}
        test_service_port: '8000'
        debug_logging: 'true'
        enable_persistence_tests: 'false'
        extra_params: "-skip 'basic parsing/large message in one chunk' -skip 'basic parsing/large message in two chunks'"


================================================
FILE: .github/actions/lint/action.yml
================================================
name: Lint
description: 'Run podspec and swiftlint checks.'

runs:
  using: composite
  steps:
    - name: Install swiftlint
      shell: bash
      run: brew install swiftlint

    - name: Install cocoapods
      shell: bash
      run: gem install cocoapods

    - name: Lint the podspec
      shell: bash
      # --quick skips building since dedicated build jobs already compile all platforms
      run: pod lib lint LDSwiftEventSource.podspec --allow-warnings --quick

    - name: Run swiftlint
      shell: bash
      run: swiftlint lint


================================================
FILE: .github/actions/publish/action.yml
================================================
name: Publish Package
description: 'Publish the package to Cocoapods'
inputs:
  dry_run:
    description: 'Is this a dry run. If so no package will be published.'
    required: true

runs:
  using: composite
  steps:
    - name: Push to cocoapods
      if: ${{ inputs.dry_run == 'false' }}
      shell: bash
      run: pod trunk push LDSwiftEventSource.podspec --allow-warnings --verbose


================================================
FILE: .github/actions/publish-docs/action.yml
================================================
name: Publish Documentation
description: 'Publish the documentation to GitHub pages'
inputs:
  token:
    description: 'Token to use for publishing.'
    required: true

runs:
  using: composite
  steps:
    - uses: launchdarkly/gh-actions/actions/publish-pages@publish-pages-v1.0.2
      name: 'Publish to GitHub pages'
      with:
        docs_path: docs
        github_token: ${{ inputs.token }}


================================================
FILE: .github/actions/test-swiftpm/action.yml
================================================
name: Test SwiftPM
description: 'Build and test using Swift Package Manager.'

runs:
  using: composite
  steps:
    - name: Build & Test with SwiftPM
      shell: bash
      run: swift test -v 2>&1 | xcpretty


================================================
FILE: .github/actions/update-versions/action.yml
================================================
name: Update xcode project version numbers
description: 'Update xcode project version numbers'
inputs:
  branch:
    description: 'The branch to checkout and push updates to'
    required: true

runs:
  using: composite
  steps:
    - uses: actions/checkout@v4
      with:
        ref: ${{ inputs.branch }}

    - name: Calculate version numbers
      id: version
      shell: bash
      run: |
        version=$(jq -r '."."' .release-please-manifest.json)
        major=$(echo "$version" | cut -f1 -d.)
        minor=$(echo "$version" | cut -f2 -d.)
        patch=$(echo "$version" | cut -f3 -d.)
        # 64 + version gives us a letter offset for the framework version.
        framework=$(echo $((major + 64)) | awk '{ printf("%c", $1) }')

        echo "major=${major}" >> "$GITHUB_OUTPUT"
        echo "minor=${minor}" >> "$GITHUB_OUTPUT"
        echo "patch=${patch}" >> "$GITHUB_OUTPUT"
        echo "framework=${framework}" >> "$GITHUB_OUTPUT"

    - name: Update other version numbers
      shell: bash
      run: |
        sed -i .bak -E \
            -e 's/MARKETING_VERSION = [^;]+/MARKETING_VERSION = ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}/' \
            -e 's/DYLIB_CURRENT_VERSION = [^;]+/DYLIB_CURRENT_VERSION = ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}/' \
            -e 's/DYLIB_COMPATIBILITY_VERSION = [^;]+/DYLIB_COMPATIBILITY_VERSION = ${{ steps.version.outputs.major }}.0.0/' \
            -e 's/FRAMEWORK_VERSION = .*/FRAMEWORK_VERSION = ${{ steps.version.outputs.framework }};/' \
        LDSwiftEventSource.xcodeproj/project.pbxproj

        sed -i .bak -E \
            -e "s/pod 'LDSwiftEventSource', '~> [0-9]+.[0-9]+'/pod 'LDSwiftEventSource', '~> ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}'/" \
            -e "s/github \"LaunchDarkly\/swift-eventsource\" ~> [0-9]+.[0-9]+/github \"LaunchDarkly\/swift-eventsource\" ~> ${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}/" README.md

        rm -f LDSwiftEventSource.xcodeproj/project.pbxproj.bak README.md.bak
        if [ $(git status --porcelain | wc -l) -gt 0 ]; then
          git config --global user.name 'LaunchDarklyReleaseBot'
          git config --global user.email 'LaunchDarklyReleaseBot@launchdarkly.com'

          git add LDSwiftEventSource.xcodeproj/project.pbxproj
          git add README.md

          git commit -m 'Updating generated project and readme files'
          git push
        fi


================================================
FILE: .github/pull_request_template.md
================================================
**Requirements**

- [ ] I have added test coverage for new or changed functionality
- [ ] I have followed the repository's [pull request submission guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [ ] I have validated my changes against all supported platform versions

**Related issues**

Provide links to any issues in this repository or elsewhere relating to this pull request.

**Describe the solution you've provided**

Provide a clear and concise description of what you expect to happen.

**Describe alternatives you've considered**

Provide a clear and concise description of any alternative solutions or features you've considered.

**Additional context**

Add any other context about the pull request here.


================================================
FILE: .github/workflows/ci.yml
================================================
name: Run CI
on:
  push:
    branches: [ main ]
    paths-ignore:
      - '**.md' # Do not need to run CI for markdown changes.
  pull_request:
    branches: [ main ]
    paths-ignore:
      - '**.md'

jobs:
  lint:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/lint

  build-ios:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/build-ios
        with:
          ios-sim: 'platform=iOS Simulator,name=iPhone 16'

  build-macos:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/build-macos

  build-tvos:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/build-tvos

  build-watchos:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/build-watchos

  test-swiftpm:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/test-swiftpm

  contract-tests:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/contract-tests
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  build-docs:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/build-docs

  linux-build:
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        swift-version:
          - 5.7
          - 5.8
          - 5.9

    container: swift:${{ matrix.swift-version }}

    steps:
      - uses: actions/checkout@v4

      - name: Build and test
        run: swift test --enable-test-discovery

  windows-build:
    name: Windows - Swift 6.1
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Swift
        uses: compnerd/gha-setup-swift@cd348eb89f2f450b0664c07fb1cb66880addf17d
        with:
          branch: swift-6.1-release
          tag: 6.1-RELEASE
      - name: Build and test
        run: swift test


================================================
FILE: .github/workflows/lint-pr-title.yml
================================================
name: Lint PR title

on:
  pull_request_target:
    types:
      - opened
      - edited
      - synchronize

jobs:
  lint-pr-title:
    uses: launchdarkly/gh-actions/.github/workflows/lint-pr-title.yml@main


================================================
FILE: .github/workflows/manual-publish-docs.yml
================================================
on:
  workflow_dispatch:

name: Publish Documentation
jobs:
  build-publish:
    runs-on: macos-15

    permissions:
      id-token: write # Needed if using OIDC to get release secrets.
      contents: write # Needed in this case to write github pages.

    steps:
      - uses: actions/checkout@v4

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/lint

      - uses: ./.github/actions/build-ios
        with:
          ios-sim: 'platform=iOS Simulator,name=iPhone 16'

      - uses: ./.github/actions/build-macos

      - uses: ./.github/actions/build-tvos

      - uses: ./.github/actions/build-watchos

      - uses: ./.github/actions/test-swiftpm

      - uses: ./.github/actions/contract-tests
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: ./.github/actions/build-docs

      - uses: ./.github/actions/publish-docs
        with:
          token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/manual-publish.yml
================================================
name: Publish Package
on:
  workflow_dispatch:
    inputs:
      dry_run:
        description: 'Is this a dry run. If so no package will be published.'
        type: boolean
        required: true

jobs:
  build-publish:
    runs-on: macos-15

    # Needed to get tokens during publishing.
    permissions:
      id-token: write
      contents: read

    steps:
      - uses: actions/checkout@v4

      - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0
        name: 'Get Cocoapods token'
        with:
          aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
          ssm_parameter_pairs: '/production/common/releasing/cocoapods/token = COCOAPODS_TRUNK_TOKEN'

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/lint

      - uses: ./.github/actions/build-ios
        with:
          ios-sim: 'platform=iOS Simulator,name=iPhone 16'

      - uses: ./.github/actions/build-macos

      - uses: ./.github/actions/build-tvos

      - uses: ./.github/actions/build-watchos

      - uses: ./.github/actions/test-swiftpm

      - uses: ./.github/actions/contract-tests
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: ./.github/actions/publish
        with:
          dry_run: ${{ inputs.dry_run }}


================================================
FILE: .github/workflows/release-please.yml
================================================
name: Run Release Please

on:
  push:
    branches:
      - main

jobs:
  release-package:
    runs-on: macos-15

    permissions:
      id-token: write # Needed if using OIDC to get release secrets.
      contents: write # Contents and pull-requests are for release-please to make releases.
      pull-requests: write

    steps:
      - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0
        id: release
        with:
          target-branch: ${{ github.ref_name }}

      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Full history is required for proper changelog generation

      #
      # This step runs and updates an existing PR
      #
      - uses: ./.github/actions/update-versions
        if: ${{ steps.release.outputs.prs_created == 'true' }}
        with:
          branch: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}

      #
      # These remaining steps are ONLY run if a release was actually created
      #
      - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        name: 'Get Cocoapods token'
        with:
          aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
          ssm_parameter_pairs: '/production/common/releasing/cocoapods/token = COCOAPODS_TRUNK_TOKEN'

      - uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # 60606e260d2fc5762a71e64e74b2174e8ea3c8bd
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        with:
          xcode-version: 16.4

      - uses: ./.github/actions/lint
        if: ${{ steps.release.outputs.releases_created == 'true' }}

      - uses: ./.github/actions/build-ios
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        with:
          ios-sim: 'platform=iOS Simulator,name=iPhone 16'

      - uses: ./.github/actions/build-macos
        if: ${{ steps.release.outputs.releases_created == 'true' }}

      - uses: ./.github/actions/build-tvos
        if: ${{ steps.release.outputs.releases_created == 'true' }}

      - uses: ./.github/actions/build-watchos
        if: ${{ steps.release.outputs.releases_created == 'true' }}

      - uses: ./.github/actions/test-swiftpm
        if: ${{ steps.release.outputs.releases_created == 'true' }}

      - uses: ./.github/actions/contract-tests
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: ./.github/actions/build-docs
        if: ${{ steps.release.outputs.releases_created == 'true' }}

      - uses: ./.github/actions/publish
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        with:
          token: ${{secrets.GITHUB_TOKEN}}
          dry_run: false

      - uses: ./.github/actions/publish-docs
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        with:
          token: ${{secrets.GITHUB_TOKEN}}


================================================
FILE: .github/workflows/stale.yml
================================================
name: "Close stale issues and PRs"
on:
  workflow_dispatch:
  schedule:
    # Happen once per day at 1:30 AM
    - cron: "30 1 * * *"

permissions:
  issues: write
  pull-requests: write

jobs:
  sdk-close-stale:
    uses: launchdarkly/gh-actions/.github/workflows/sdk-stale.yml@main


================================================
FILE: .gitignore
================================================
*~
\#*
.\#*
.DS_Store
/.build
xcuserdata/
IDEWorkspaceChecks.plist
.swiftpm
/docs

================================================
FILE: .jazzy.yaml
================================================
module: LDSwiftEventSource
author: LaunchDarkly
author_url: https://launchdarkly.com
github_url: https://github.com/launchdarkly/swift-eventsource
clean: true
swift_build_tool: spm
readme: README.md
documentation:
  - CHANGELOG.md
  - CONTRIBUTING.md
  - LICENSE.txt

copyright: 'Copyright © 2020 Catamorphic Co.'


================================================
FILE: .release-please-manifest.json
================================================
{
  ".": "3.3.0"
}


================================================
FILE: .swiftlint.yml
================================================
# See sub-configurations at `Source/.swiftlint.yml` and `Tests/.swiftlint.yml`.

disabled_rules:
  - identifier_name
  - weak_delegate

opt_in_rules:
  - anyobject_protocol
  - array_init
  - attributes
  - closure_body_length
  - closure_end_indentation
  - closure_spacing
  - collection_alignment
  - conditional_returns_on_newline
  - contains_over_filter_count
  - contains_over_filter_is_empty
  - contains_over_first_not_nil
  - contains_over_range_nil_comparison
  - discouraged_object_literal
  - discouraged_optional_boolean
  - discouraged_optional_collection
  - empty_collection_literal
  - empty_count
  - empty_string
  - empty_xctest_method
  - enum_case_associated_values_count
  - expiring_todo
  - explicit_init
  - explicit_self
  - extension_access_modifier
  - fallthrough
  - fatal_error_message
  - file_header
  - file_name_no_space
  - first_where
  - flatmap_over_map_reduce
  - function_default_parameter_at_end
  - identical_operands
  - implicit_return
  - joined_default_parameter
  - last_where
  - legacy_multiple
  - legacy_random
  - let_var_whitespace
  - literal_expression_end_indentation
  - missing_docs
  - modifier_order
  - no_grouping_extension
  - nslocalizedstring_key
  - nslocalizedstring_require_bundle
  - number_separator
  - object_literal
  - operator_usage_whitespace
  - optional_enum_case_matching
  - overridden_super_call
  - override_in_extension
  - pattern_matching_keywords
  - prefer_self_type_over_type_of_self
  - prefixed_toplevel_constant
  - private_action
  - private_outlet
  - prohibited_interface_builder
  - prohibited_super_call
  - raw_value_for_camel_cased_codable_enum
  - reduce_into
  - redundant_nil_coalescing
  - required_enum_case
  - single_test_class
  - sorted_first_last
  - static_operator
  - strict_fileprivate
  - strong_iboutlet
  - switch_case_on_newline
  - toggle_bool
  - trailing_closure
  - unavailable_function
  - unneeded_parentheses_in_closure_argument
  - unowned_variable_capture
  - untyped_error_in_catch
  - unused_declaration
  - unused_import
  - vertical_parameter_alignment_on_call
  - vertical_whitespace_closing_braces
  - vertical_whitespace_opening_braces
  - yoda_condition

included:
  - Source
  - Tests

reporter: "xcode"


================================================
FILE: CHANGELOG.md
================================================
# Change log

All notable changes to the LaunchDarkly Swift EventSource library will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).

## [3.3.0](https://github.com/launchdarkly/swift-eventsource/compare/3.2.0...3.3.0) (2024-05-31)


### Features

* adds ability to provide a OSLog instance via the Config.logger property ([c10ec29](https://github.com/launchdarkly/swift-eventsource/commit/c10ec2936e77959f828a041b71ea56e454e39ff2))
* adds ability to provide a OSLog instance via the Config.logger property ([#78](https://github.com/launchdarkly/swift-eventsource/issues/78)) ([2220929](https://github.com/launchdarkly/swift-eventsource/commit/2220929cbc98edd88e0e7d8b5ccb1f3992b4cf4f))

## [3.2.0](https://github.com/launchdarkly/swift-eventsource/compare/3.1.1...3.2.0) (2023-12-29)


### Features

* Add Compilation & Testing On Windows ([#68](https://github.com/launchdarkly/swift-eventsource/issues/68)) ([ac5f18c](https://github.com/launchdarkly/swift-eventsource/commit/ac5f18ccb5b197bbc9f37f9f799017a397eee43e))

## [3.1.1] - 2023-06-12
### Fixed:
- Per the SSE spec, an HTTP 204 will now halt retry attempts by default.

## [3.1.0] - 2023-06-05
### Changed:
- Enforce TLS v1.2 as a required minimum.

### Fixed:
- Fix re-entrancy issue with `start` command. (Thanks, [g-mark](https://github.com/launchdarkly/swift-eventsource/pull/56)!)

## [3.0.0] - 2022-10-06
### Changed
- Dropped support for older versions in accordance with the new [Xcode 14 release](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes).

## [2.0.0] - 2022-08-29
### Changed
- The CI build now incorporates the cross-platform contract tests defined in https://github.com/launchdarkly/sse-contract-tests to ensure consistent test coverage across different LaunchDarkly SSE implementations.
- Removed explicit typed package products. Thanks to @simba909 for the PR ([#48](https://github.com/launchdarkly/swift-eventsource/pull/48)).

## [1.3.1] - 2022-03-11
### Fixed
- Fixed a race condition that could cause a crash when `stop()` is called when there is a pending reconnection attempt.

## [1.3.0] - 2022-01-18
### Added
- Added the configuration option `urlSessionConfiguration` to `EventSource.Config` which allows setting the `URLSessionConfiguration` used by the `EventSource` to create `URLSession` instances.

### Fixed
- Fixed a retain cycle issue when the stream connection is ended.
- Removed deprecated `VALID_ARCHS` build setting from Xcode project.
- Unterminated events will no longer be dispatched when the stream connection is dropped.
- Stream events that set the `lastEventId` will now record the updated `lastEventId` even if the event does not generate a `MessageEvent`.
- Empty stream "data" fields will now always record a newline to the resultant `MessageEvent` data.
- Empty stream "event" fields will result in now result in the default "message" event type rather than an event type of "".

## [1.2.1] - 2021-02-10
### Added
- [SwiftLint](https://github.com/realm/SwiftLint) configuration. Linting will be automatically run as part of the build if [Mint](https://github.com/yonaskolb/Mint) is installed.
- Support for building docs with [jazzy](https://github.com/realm/jazzy). These docs are available through [GitHub Pages](https://launchdarkly.github.io/swift-eventsource/).

### Fixed
- Reconnection backoff was always reset if the  previous successful connection was at least `backoffResetThreshold` prior to the scheduling of a reconnection attempt. The connection backoff has been corrected to not reset after the first reconnection attempt until the next successful connection. Thanks to  @tomasf for the PR ([#14](https://github.com/launchdarkly/swift-eventsource/pull/14)).
- On an `UnsuccessfulResponseError` the configured `connectionErrorHandler` would be called twice, the second time with a `URLError.cancelled` error. Only if the second call returned `ConnectionErrorAction.shutdown` would the `EventSource` client actually shutdown. This has been corrected to only call the `connectionErrorHandler` once, and will shutdown the client if `ConnectionErrorAction.shutdown` is returned. Thanks to  @tomasf for the PR ([#13](https://github.com/launchdarkly/swift-eventsource/pull/13)).
- A race condition that could cause the `EventSource` client to restart after shutting down has been fixed.

## [1.2.0] - 2020-10-21
### Added
- Added `headerTransform` closure to `LDConfig` to allow dynamic http header configuration.

## [1.1.0] - 2020-07-20
### Added
- Support `arm64e` on `appletvos`, `iphoneos`, and `macosx` SDKs by extending valid architectures.
- Support for building LDSwiftEventSource on Linux. Currently this library will not generate log messages on Linux, and may not behave correctly on Linux due to Foundation being [incomplete](https://github.com/apple/swift-corelibs-foundation/blob/main/Docs/Status.md).

## [1.0.0] - 2020-07-16
This is the first public release of the LDSwiftEventSource library. The following notes are what changed since the previous pre-release version.
### Changed
- Renamed `EventHandler.onMessage` parameter `event` to `eventType`.
- The `EventSource` class no longer extends `NSObject` or `URLSessionDataDelegate` to not expose `urlSession` functions.

## [0.5.0] - 2020-07-14
### Changed
- Default `LDSwiftEventSource` product defined for the SwiftPM package is now explicitly a dynamic product. An explicitly static product is now available as `LDSwiftEventSourceStatic`.

## [0.4.0] - 2020-07-13
### Changed
- Converted build system to use a single target to produce a universal framework, rather than separate targets for each platform that share a product name. This is to prevent issues with `xcodebuild` resolving the build scheme to an incorrect platform when building dependent packages with 'Find Implicit Dependencies' enabled. This is due to a bug in `xcodebuild`, for more information see [http://www.openradar.me/20490378](http://www.openradar.me/20490378) and [http://www.openradar.me/22008701](http://www.openradar.me/22008701).

## [0.3.0] - 2020-06-02
### Added
- Added `stop()` method to shutdown the EventSource connection.
### Changed
- Logging `subsystem` renamed from `com.launchdarkly.swift-event-source` to `com.launchdarkly.swift-eventsource`

## [0.2.0] - 2020-05-21
### Added
- Public constructors for `UnsuccessfulResponseError` and `MessageEvent` to allow consumers of the library to use them for unit tests.

## [0.1.0] - 2020-05-09
### Added
- Initial implementation for internal alpha testing.


================================================
FILE: CODEOWNERS
================================================
# Repository Maintainers
* @launchdarkly/team-sdk-swift


================================================
FILE: CONTRIBUTING.md
================================================
Contributing to the LDSwiftEventSource library
================================================

Submitting bug reports and feature requests
------------------

The LaunchDarkly SDK team monitors the [issue tracker](https://github.com/launchdarkly/swift-eventsource/issues) for the EventSource repository. Bug reports and feature requests specific to this library should be filed in this issue tracker.

Submitting pull requests
------------------

We encourage pull requests and other contributions from the community. Before submitting pull requests, ensure that all temporary or unintended code is removed. Don't worry about adding reviewers to the pull request; the LaunchDarkly SDK team will add themselves.

Build instructions
------------------

### Prerequisites

This library is built with [XCode](https://developer.apple.com/xcode/) or [SwiftPM](https://swift.org/package-manager/). The [CI build](https://github.com/launchdarkly/swift-eventsource/actions/workflows/ci.yml) builds and tests various configurations of the library on various systems, platforms, and devices. For details, see [the GitHub action CI configuration][ci-config].

### Building And Testing

This library can be built directly with the Swift package manager, or through XCode. To build and run tests using SwiftPM simply:

```bash
swift test
```

Or in XCode, simply select the desired target and select `Product -> Test`.

For building on the command line with `xcodebuild`, see the [continuous integration build configuration][ci-config] for examples on building and running tests.

### Running contract tests

To run the standardized contract tests that are run against all LaunchDarkly SSE client implementations:
```
make contract-tests
```

### Generating API documentation

Docs are built with [jazzy](https://github.com/realm/jazzy), which is configured [here](https://github.com/launchdarkly/swift-eventsource/blob/main/.jazzy.yaml). To build them, simply run `jazzy`. Pull requests should keep our documentation coverage at 100%.

[ci-config]: https://github.com/launchdarkly/swift-eventsource/blob/main/.github/workflows/ci.yml


================================================
FILE: ContractTestService/.gitignore
================================================
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata


================================================
FILE: ContractTestService/Package.resolved
================================================
{
  "object": {
    "pins": [
      {
        "package": "Socket",
        "repositoryURL": "https://github.com/Kitura/BlueSocket.git",
        "state": {
          "branch": null,
          "revision": "c9894fd117457f1d006575fbfb2fdfd6f79eac03",
          "version": "1.0.200"
        }
      },
      {
        "package": "SSLService",
        "repositoryURL": "https://github.com/Kitura/BlueSSLService.git",
        "state": {
          "branch": null,
          "revision": "ae5889d2a8b068d2d3ab91ec73aa8f557c03cd8a",
          "version": "1.0.200"
        }
      },
      {
        "package": "Kitura",
        "repositoryURL": "https://github.com/Kitura/Kitura",
        "state": {
          "branch": null,
          "revision": "daf6479097469a0c1fe64755db6e23f788a3ec29",
          "version": "2.9.200"
        }
      },
      {
        "package": "Kitura-net",
        "repositoryURL": "https://github.com/Kitura/Kitura-net.git",
        "state": {
          "branch": null,
          "revision": "e017a57772eb477c294d72b25ab9ae38d25539f5",
          "version": "2.4.200"
        }
      },
      {
        "package": "Kitura-TemplateEngine",
        "repositoryURL": "https://github.com/Kitura/Kitura-TemplateEngine.git",
        "state": {
          "branch": null,
          "revision": "1670b09bfb63b66f7dffff627a06b50492485407",
          "version": "2.0.200"
        }
      },
      {
        "package": "KituraContracts",
        "repositoryURL": "https://github.com/Kitura/KituraContracts.git",
        "state": {
          "branch": null,
          "revision": "8a4778c3aa7833e9e1af884e8819d436c237cd70",
          "version": "1.2.201"
        }
      },
      {
        "package": "LoggerAPI",
        "repositoryURL": "https://github.com/Kitura/LoggerAPI.git",
        "state": {
          "branch": null,
          "revision": "e82d34eab3f0b05391082b11ea07d3b70d2f65bb",
          "version": "1.9.200"
        }
      },
      {
        "package": "swift-log",
        "repositoryURL": "https://github.com/apple/swift-log.git",
        "state": {
          "branch": null,
          "revision": "5d66f7ba25daf4f94100e7022febf3c75e37a6c7",
          "version": "1.4.2"
        }
      },
      {
        "package": "TypeDecoder",
        "repositoryURL": "https://github.com/Kitura/TypeDecoder.git",
        "state": {
          "branch": null,
          "revision": "28ec01815c0aea9236f92982ca8d351e7112a4a0",
          "version": "1.3.201"
        }
      }
    ]
  },
  "version": 1
}


================================================
FILE: ContractTestService/Package.swift
================================================
// swift-tools-version:5.0

import PackageDescription

let package = Package(
  name: "ContractTestService",
  platforms: [
    .iOS(.v11),
    .macOS(.v10_13),
    .watchOS(.v4),
    .tvOS(.v11),
  ],
  products: [
    .executable(
      name: "contract-test-service",
      targets: ["ContractTestService"]
    )
  ],
  dependencies: [
    // Local dependency to LDSwiftEventSource
    .package(path: ".."),
    .package(url: "https://github.com/Kitura/Kitura", from: "2.9.200")
  ],
  targets: [
    .target(
      name: "ContractTestService",
      dependencies: [
        "LDSwiftEventSource",
        "Kitura"
      ]
    )
  ]
)


================================================
FILE: ContractTestService/README.md
================================================
# SSE client contract test service

This directory contains an implementation of the cross-platform SSE testing protocol defined by https://github.com/launchdarkly/sse-contract-tests. See that project's `README` for details of this protocol, and the kinds of SSE client capabilities that are relevant to the contract tests. This code should not need to be updated unless the SSE client has added or removed such capabilities.

To run these tests locally, run `make contract-tests` from the project root directory. This downloads the correct version of the test harness tool automatically.


================================================
FILE: ContractTestService/Sources/ContractTestService/main.swift
================================================
import Dispatch
import Foundation
import Kitura
import LDSwiftEventSource

struct StatusResp: Encodable {
    let name = "swift-eventsource"
    let capabilities = ["server-directed-shutdown-request", "comments", "headers", "last-event-id", "post", "read-timeout", "report"]
}

struct CreateStreamReq: Decodable {
    let streamUrl: URL
    let callbackUrl: URL
    let initialDelayMs: Int?
    let readTimeoutMs: Int?
    let lastEventId: String?
    let headers: [String: String]?
    let method: String?
    let body: String?

    func createEventSourceConfig() -> EventSource.Config {
        var esConfig = EventSource.Config(handler: CallbackHandler(baseUrl: callbackUrl), url: streamUrl)
        if let initialDelayMs = initialDelayMs { esConfig.reconnectTime = Double(initialDelayMs) / 1000.0 }
        if let readTimeoutMs = readTimeoutMs { esConfig.idleTimeout = Double(readTimeoutMs) / 1000.0 }
        if let lastEventId = lastEventId { esConfig.lastEventId = lastEventId }
        if let headers = headers { esConfig.headers = headers }
        if let method = method { esConfig.method = method }
        if let body = body { esConfig.body = Data(body.utf8) }
        return esConfig
    }
}

class CallbackHandler: EventHandler {
    struct EventPayloadEvent: Encodable {
        let type: String
        let data: String
        let id: String?
    }

    struct EventPayload: Encodable {
        let kind = "event"
        let event: EventPayloadEvent
    }

    struct CommentPayload: Encodable {
        let kind = "comment"
        let comment: String
    }

    struct ErrorPayload: Encodable {
        let kind = "error"
    }

    let baseUrl: URL
    var count = 0

    init(baseUrl: URL) {
        self.baseUrl = baseUrl
    }

    func onOpened() { }
    func onClosed() { }

    func sendUpdate<T: Encodable>(_ update: T) {
        count += 1
        var request = URLRequest(url: baseUrl.appendingPathComponent(String(count), isDirectory: false))
        request.httpMethod = "POST"
        let data = try! JSONEncoder().encode(update)
        URLSession.shared.uploadTask(with: request, from: data) { _, _, _ in }.resume()
    }

    func onMessage(eventType type: String, messageEvent msg: MessageEvent) {
        sendUpdate(EventPayload(event: EventPayloadEvent(type: type, data: msg.data, id: msg.lastEventId)))
    }

    func onComment(comment: String) {
        sendUpdate(CommentPayload(comment: comment))
    }

    func onError(error: Error) {
        sendUpdate(ErrorPayload())
    }
}

let stateQueue = DispatchQueue(label: "StateQueue")
var nextId: Int = 0
var state: [String: EventSource] = [:]

let router = Router()

router.get("/") { _, resp, next in
    resp.send(StatusResp())
    next()
}

router.delete("/") { _, resp, next in
    resp.send(["message": "Shutting down contract test service"])
    next()
    Kitura.stop()
}

router.post("/") { req, resp, next in
    guard let createStreamReq = try? req.read(as: CreateStreamReq.self)
    else {
        resp.status(.badRequest).send(["message": "Body of POST to '/' invalid"])
        return next()
    }
    let es = EventSource(config: createStreamReq.createEventSourceConfig())
    let location: String = stateQueue.sync {
        state[String(nextId)] = es
        nextId += 1
        return "/control/\(nextId - 1)"
    }
    es.start()
    resp.headers["Location"] = location
    resp.send(["message": "Created test service entity at \(location)"])
    next()
}

router.delete("/control/:id") { req, resp, next in
    stateQueue.sync {
        if let es = state.removeValue(forKey: req.parameters["id"]!) {
            es.stop()
            resp.send(["message": "Shut down test service entity at \(req.matchedPath)"])
        } else {
            resp.status(.notFound).send(["message": "Test service entity not found at \(req.matchedPath)"])
        }
    }
    next()
}

Kitura.addHTTPServer(onPort: 8000, onAddress: "localhost", with: router)
Kitura.run()


================================================
FILE: LDSwiftEventSource.podspec
================================================
Pod::Spec.new do |s|
  s.name         = "LDSwiftEventSource"
  s.version      = "3.3.0" # x-release-please-version
  s.summary      = "Swift EventSource library"
  s.homepage     = "https://github.com/launchdarkly/swift-eventsource"
  s.license      = { :type => "Apache License, Version 2.0", :file => "LICENSE.txt" }
  s.author       = { "LaunchDarkly" => "sdks@launchdarkly.com" }

  s.ios.deployment_target     = "11.0"
  s.watchos.deployment_target = "4.0"
  s.tvos.deployment_target    = "11.0"
  s.osx.deployment_target     = "10.13"

  s.source       = { :git => s.homepage + '.git', :tag => s.version}
  s.source_files = "Source/**/*.swift"

  s.swift_versions = ['5.0', '5.1', '5.2', '5.3', '5.4', '5.5', '5.6', '5.7']
end


================================================
FILE: LDSwiftEventSource.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 54;
	objects = {

/* Begin PBXBuildFile section */
		B426585E272849AF007B711A /* MockHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B426585D272849AF007B711A /* MockHandler.swift */; };
		B495D4A9248652DF00AE9233 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = B495D4A7248652DF00AE9233 /* Types.swift */; };
		B49B5E4B24667F62008BF867 /* UTF8LineParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49B5E4524667F43008BF867 /* UTF8LineParser.swift */; };
		B49B5E4C24667F62008BF867 /* EventParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49B5E4624667F43008BF867 /* EventParser.swift */; };
		B49B5E4D24667F62008BF867 /* LDSwiftEventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49B5E4724667F43008BF867 /* LDSwiftEventSource.swift */; };
		B49B5E5824668031008BF867 /* EventParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49B5E4024667F43008BF867 /* EventParserTests.swift */; };
		B49B5E5A24668031008BF867 /* UTF8LineParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49B5E4224667F43008BF867 /* UTF8LineParserTests.swift */; };
		B49B5E5B24668031008BF867 /* LDSwiftEventSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49B5E4324667F43008BF867 /* LDSwiftEventSourceTests.swift */; };
		B49B5E67246684B9008BF867 /* LDSwiftEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = B49B5E65246684B9008BF867 /* LDSwiftEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
		B49B5E72246C4796008BF867 /* LDSwiftEventSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B49B5DFC24667D41008BF867 /* LDSwiftEventSource.framework */; };
		B4BCAE6E272753FA000EBD43 /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4BCAE6D272753FA000EBD43 /* TestUtil.swift */; };
		B4C29CC826FF743D008B6DE2 /* Logs.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C29CC726FF743C008B6DE2 /* Logs.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		B49B5E0624667D42008BF867 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = B49B5DB824667C44008BF867 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = B49B5DFB24667D41008BF867;
			remoteInfo = "LDSwiftEventSource macOS";
		};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
		B426585D272849AF007B711A /* MockHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHandler.swift; sourceTree = "<group>"; };
		B495D4A7248652DF00AE9233 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = "<group>"; };
		B49B5DE324667D06008BF867 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		B49B5DFC24667D41008BF867 /* LDSwiftEventSource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LDSwiftEventSource.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		B49B5E0424667D42008BF867 /* LDSwiftEventSource Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LDSwiftEventSource Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
		B49B5E4024667F43008BF867 /* EventParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventParserTests.swift; sourceTree = "<group>"; };
		B49B5E4224667F43008BF867 /* UTF8LineParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTF8LineParserTests.swift; sourceTree = "<group>"; };
		B49B5E4324667F43008BF867 /* LDSwiftEventSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDSwiftEventSourceTests.swift; sourceTree = "<group>"; };
		B49B5E4524667F43008BF867 /* UTF8LineParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTF8LineParser.swift; sourceTree = "<group>"; };
		B49B5E4624667F43008BF867 /* EventParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventParser.swift; sourceTree = "<group>"; };
		B49B5E4724667F43008BF867 /* LDSwiftEventSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDSwiftEventSource.swift; sourceTree = "<group>"; };
		B49B5E65246684B9008BF867 /* LDSwiftEventSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDSwiftEventSource.h; sourceTree = "<group>"; };
		B49B5E6B2466875F008BF867 /* LDSwiftEventSource.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = LDSwiftEventSource.podspec; sourceTree = "<group>"; };
		B49B5E6C2466875F008BF867 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
		B49B5E6D2466875F008BF867 /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = "<group>"; };
		B49B5E6E2466875F008BF867 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
		B49B5E6F2466875F008BF867 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
		B49B5E702466875F008BF867 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
		B4BCAE6D272753FA000EBD43 /* TestUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = "<group>"; };
		B4C29CC726FF743C008B6DE2 /* Logs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logs.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		B49B5DF924667D41008BF867 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		B49B5E0124667D42008BF867 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				B49B5E72246C4796008BF867 /* LDSwiftEventSource.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		B49B5DB724667C44008BF867 = {
			isa = PBXGroup;
			children = (
				B49B5E6A2466873F008BF867 /* Misc */,
				B49B5E4424667F43008BF867 /* Source */,
				B49B5E3F24667F43008BF867 /* Tests */,
				B49B5DC224667C44008BF867 /* Products */,
			);
			sourceTree = "<group>";
		};
		B49B5DC224667C44008BF867 /* Products */ = {
			isa = PBXGroup;
			children = (
				B49B5DFC24667D41008BF867 /* LDSwiftEventSource.framework */,
				B49B5E0424667D42008BF867 /* LDSwiftEventSource Tests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		B49B5E3F24667F43008BF867 /* Tests */ = {
			isa = PBXGroup;
			children = (
				B49B5E4024667F43008BF867 /* EventParserTests.swift */,
				B49B5E4224667F43008BF867 /* UTF8LineParserTests.swift */,
				B49B5E4324667F43008BF867 /* LDSwiftEventSourceTests.swift */,
				B4BCAE6D272753FA000EBD43 /* TestUtil.swift */,
				B426585D272849AF007B711A /* MockHandler.swift */,
			);
			path = Tests;
			sourceTree = "<group>";
		};
		B49B5E4424667F43008BF867 /* Source */ = {
			isa = PBXGroup;
			children = (
				B49B5DE324667D06008BF867 /* Info.plist */,
				B49B5E4524667F43008BF867 /* UTF8LineParser.swift */,
				B49B5E4624667F43008BF867 /* EventParser.swift */,
				B49B5E4724667F43008BF867 /* LDSwiftEventSource.swift */,
				B49B5E65246684B9008BF867 /* LDSwiftEventSource.h */,
				B495D4A7248652DF00AE9233 /* Types.swift */,
				B4C29CC726FF743C008B6DE2 /* Logs.swift */,
			);
			path = Source;
			sourceTree = "<group>";
		};
		B49B5E6A2466873F008BF867 /* Misc */ = {
			isa = PBXGroup;
			children = (
				B49B5E6C2466875F008BF867 /* CHANGELOG.md */,
				B49B5E6D2466875F008BF867 /* CONTRIBUTING.md */,
				B49B5E6B2466875F008BF867 /* LDSwiftEventSource.podspec */,
				B49B5E6E2466875F008BF867 /* LICENSE.txt */,
				B49B5E702466875F008BF867 /* Package.swift */,
				B49B5E6F2466875F008BF867 /* README.md */,
			);
			name = Misc;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
		B49B5DF724667D41008BF867 /* Headers */ = {
			isa = PBXHeadersBuildPhase;
			buildActionMask = 2147483647;
			files = (
				B49B5E67246684B9008BF867 /* LDSwiftEventSource.h in Headers */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXHeadersBuildPhase section */

/* Begin PBXNativeTarget section */
		B49B5DFB24667D41008BF867 /* LDSwiftEventSource */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = B49B5E0D24667D42008BF867 /* Build configuration list for PBXNativeTarget "LDSwiftEventSource" */;
			buildPhases = (
				B46C1C6B24CF348B00283630 /* Linter Script */,
				B49B5DF724667D41008BF867 /* Headers */,
				B49B5DF824667D41008BF867 /* Sources */,
				B49B5DF924667D41008BF867 /* Frameworks */,
				B49B5DFA24667D41008BF867 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = LDSwiftEventSource;
			productName = "LDSwiftEventSource macOS";
			productReference = B49B5DFC24667D41008BF867 /* LDSwiftEventSource.framework */;
			productType = "com.apple.product-type.framework";
		};
		B49B5E0324667D42008BF867 /* LDSwiftEventSource Tests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = B49B5E1024667D42008BF867 /* Build configuration list for PBXNativeTarget "LDSwiftEventSource Tests" */;
			buildPhases = (
				B49B5E0024667D42008BF867 /* Sources */,
				B49B5E0124667D42008BF867 /* Frameworks */,
				B49B5E0224667D42008BF867 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				B49B5E0724667D42008BF867 /* PBXTargetDependency */,
			);
			name = "LDSwiftEventSource Tests";
			productName = "LDSwiftEventSource macOSTests";
			productReference = B49B5E0424667D42008BF867 /* LDSwiftEventSource Tests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		B49B5DB824667C44008BF867 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastSwiftUpdateCheck = 1140;
				LastUpgradeCheck = 1140;
				ORGANIZATIONNAME = LaunchDarkly;
				TargetAttributes = {
					B49B5DFB24667D41008BF867 = {
						CreatedOnToolsVersion = 11.4;
					};
					B49B5E0324667D42008BF867 = {
						CreatedOnToolsVersion = 11.4;
					};
				};
			};
			buildConfigurationList = B49B5DBB24667C44008BF867 /* Build configuration list for PBXProject "LDSwiftEventSource" */;
			compatibilityVersion = "Xcode 10.0";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = B49B5DB724667C44008BF867;
			productRefGroup = B49B5DC224667C44008BF867 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				B49B5DFB24667D41008BF867 /* LDSwiftEventSource */,
				B49B5E0324667D42008BF867 /* LDSwiftEventSource Tests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		B49B5DFA24667D41008BF867 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		B49B5E0224667D42008BF867 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		B46C1C6B24CF348B00283630 /* Linter Script */ = {
			isa = PBXShellScriptBuildPhase;
			alwaysOutOfDate = 1;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
			);
			name = "Linter Script";
			outputFileListPaths = (
			);
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "# Adds support for Apple Silicon brew directory\nexport PATH=\"$PATH:/opt/homebrew/bin\"\n\nif which mint >/dev/null; then\n  /usr/bin/xcrun --sdk macosx mint run realm/SwiftLint\nelse\n  echo \"warning: mint not installed, available from https://github.com/yonaskolb/Mint\"\nfi\n";
			showEnvVarsInLog = 0;
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		B49B5DF824667D41008BF867 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				B49B5E4B24667F62008BF867 /* UTF8LineParser.swift in Sources */,
				B4C29CC826FF743D008B6DE2 /* Logs.swift in Sources */,
				B495D4A9248652DF00AE9233 /* Types.swift in Sources */,
				B49B5E4C24667F62008BF867 /* EventParser.swift in Sources */,
				B49B5E4D24667F62008BF867 /* LDSwiftEventSource.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		B49B5E0024667D42008BF867 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				B49B5E5824668031008BF867 /* EventParserTests.swift in Sources */,
				B49B5E5A24668031008BF867 /* UTF8LineParserTests.swift in Sources */,
				B426585E272849AF007B711A /* MockHandler.swift in Sources */,
				B49B5E5B24668031008BF867 /* LDSwiftEventSourceTests.swift in Sources */,
				B4BCAE6E272753FA000EBD43 /* TestUtil.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		B49B5E0724667D42008BF867 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = B49B5DFB24667D41008BF867 /* LDSwiftEventSource */;
			targetProxy = B49B5E0624667D42008BF867 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin XCBuildConfiguration section */
		B49B5DD324667C44008BF867 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				"COMBINE_HIDPI_IMAGES[sdk=macosx]" = YES;
				COPY_PHASE_STRIP = NO;
				CURRENT_PROJECT_VERSION = 1;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				FRAMEWORK_VERSION = C;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				INFOPLIST_FILE = Source/Info.plist;
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
				MACOSX_DEPLOYMENT_TARGET = 10.13;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				ONLY_ACTIVE_ARCH = YES;
				PRODUCT_BUNDLE_IDENTIFIER = com.launchdarkly.LDSwiftEventSource;
				PRODUCT_NAME = LDSwiftEventSource;
				SDKROOT = macosx;
				SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
				TVOS_DEPLOYMENT_TARGET = 11.0;
				VERSIONING_SYSTEM = "apple-generic";
				VERSION_INFO_PREFIX = "";
				WATCHOS_DEPLOYMENT_TARGET = 4.0;
			};
			name = Debug;
		};
		B49B5DD424667C44008BF867 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				"COMBINE_HIDPI_IMAGES[sdk=macosx]" = YES;
				COPY_PHASE_STRIP = NO;
				CURRENT_PROJECT_VERSION = 1;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				FRAMEWORK_VERSION = C;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				INFOPLIST_FILE = Source/Info.plist;
				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
				MACOSX_DEPLOYMENT_TARGET = 10.13;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				PRODUCT_BUNDLE_IDENTIFIER = com.launchdarkly.LDSwiftEventSource;
				PRODUCT_NAME = LDSwiftEventSource;
				SDKROOT = macosx;
				SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				SWIFT_VERSION = 5.0;
				TVOS_DEPLOYMENT_TARGET = 11.0;
				VERSIONING_SYSTEM = "apple-generic";
				VERSION_INFO_PREFIX = "";
				WATCHOS_DEPLOYMENT_TARGET = 4.0;
			};
			name = Release;
		};
		B49B5E0E24667D42008BF867 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				APPLICATION_EXTENSION_API_ONLY = YES;
				CODE_SIGN_STYLE = Automatic;
				DEFINES_MODULE = YES;
				DYLIB_COMPATIBILITY_VERSION = 3.0.0;
				DYLIB_CURRENT_VERSION = 3.3.0;
				DYLIB_INSTALL_NAME_BASE = "@rpath";
				ENABLE_BITCODE = YES;
				"ENABLE_BITCODE[sdk=macosx*]" = NO;
				INFOPLIST_FILE = "$(inherited)";
				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
					"@loader_path/Frameworks",
				);
				"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
					"$(inherited)",
					"@executable_path/../Frameworks",
					"@loader_path/Frameworks",
				);
				MARKETING_VERSION = 3.3.0;
				SKIP_INSTALL = YES;
				"TARGETED_DEVICE_FAMILY[sdk=appletvos*]" = 3;
				"TARGETED_DEVICE_FAMILY[sdk=appletvsimulator*]" = 3;
				"TARGETED_DEVICE_FAMILY[sdk=iphoneos*]" = "1,2";
				"TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]" = "1,2";
				"TARGETED_DEVICE_FAMILY[sdk=watchos*]" = 4;
				"TARGETED_DEVICE_FAMILY[sdk=watchsimulator*]" = 4;
			};
			name = Debug;
		};
		B49B5E0F24667D42008BF867 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				APPLICATION_EXTENSION_API_ONLY = YES;
				CODE_SIGN_STYLE = Automatic;
				DEFINES_MODULE = YES;
				DYLIB_COMPATIBILITY_VERSION = 3.0.0;
				DYLIB_CURRENT_VERSION = 3.3.0;
				DYLIB_INSTALL_NAME_BASE = "@rpath";
				ENABLE_BITCODE = YES;
				"ENABLE_BITCODE[sdk=macosx*]" = NO;
				INFOPLIST_FILE = "$(inherited)";
				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
					"@loader_path/Frameworks",
				);
				"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
					"$(inherited)",
					"@executable_path/../Frameworks",
					"@loader_path/Frameworks",
				);
				MARKETING_VERSION = 3.3.0;
				SKIP_INSTALL = YES;
				"TARGETED_DEVICE_FAMILY[sdk=appletvos*]" = 3;
				"TARGETED_DEVICE_FAMILY[sdk=appletvsimulator*]" = 3;
				"TARGETED_DEVICE_FAMILY[sdk=iphoneos*]" = "1,2";
				"TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]" = "1,2";
				"TARGETED_DEVICE_FAMILY[sdk=watchos*]" = 4;
				"TARGETED_DEVICE_FAMILY[sdk=watchsimulator*]" = 4;
			};
			name = Release;
		};
		B49B5E1124667D42008BF867 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				CODE_SIGN_STYLE = Automatic;
				INFOPLIST_FILE = "$(inherited)";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
					"@loader_path/Frameworks",
				);
				"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
					"$(inherited)",
					"@executable_path/../Frameworks",
					"@loader_path/../Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.launchdarkly.LDSwiftEventSourceTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator";
			};
			name = Debug;
		};
		B49B5E1224667D42008BF867 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				CODE_SIGN_STYLE = Automatic;
				INFOPLIST_FILE = "$(inherited)";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
					"@loader_path/Frameworks",
				);
				"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
					"$(inherited)",
					"@executable_path/../Frameworks",
					"@loader_path/../Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = com.launchdarkly.LDSwiftEventSourceTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		B49B5DBB24667C44008BF867 /* Build configuration list for PBXProject "LDSwiftEventSource" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				B49B5DD324667C44008BF867 /* Debug */,
				B49B5DD424667C44008BF867 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		B49B5E0D24667D42008BF867 /* Build configuration list for PBXNativeTarget "LDSwiftEventSource" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				B49B5E0E24667D42008BF867 /* Debug */,
				B49B5E0F24667D42008BF867 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		B49B5E1024667D42008BF867 /* Build configuration list for PBXNativeTarget "LDSwiftEventSource Tests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				B49B5E1124667D42008BF867 /* Debug */,
				B49B5E1224667D42008BF867 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = B49B5DB824667C44008BF867 /* Project object */;
}


================================================
FILE: LDSwiftEventSource.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:LDSwiftEventSource.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: LDSwiftEventSource.xcodeproj/xcshareddata/xcschemes/LDSwiftEventSource.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1140"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "B49B5DFB24667D41008BF867"
               BuildableName = "LDSwiftEventSource.framework"
               BlueprintName = "LDSwiftEventSource"
               ReferencedContainer = "container:LDSwiftEventSource.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES"
      systemAttachmentLifetime = "keepNever"
      codeCoverageEnabled = "YES">
      <Testables>
         <TestableReference
            skipped = "NO">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "B49B5E0324667D42008BF867"
               BuildableName = "LDSwiftEventSource Tests.xctest"
               BlueprintName = "LDSwiftEventSource Tests"
               ReferencedContainer = "container:LDSwiftEventSource.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "B49B5DFB24667D41008BF867"
            BuildableName = "LDSwiftEventSource.framework"
            BlueprintName = "LDSwiftEventSource"
            ReferencedContainer = "container:LDSwiftEventSource.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "B49B5DFB24667D41008BF867"
            BuildableName = "LDSwiftEventSource.framework"
            BlueprintName = "LDSwiftEventSource"
            ReferencedContainer = "container:LDSwiftEventSource.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: LICENSE.txt
================================================
Copyright 2020 Catamorphic, Co.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: Makefile
================================================
build:
	swift build

clean:
	swift clean

test:
	swift test

TEMP_TEST_OUTPUT=/tmp/sse-contract-test-service.log

build-contract-tests:
	cd ContractTestService && swift build

start-contract-test-service:
	./ContractTestService/.build/debug/contract-test-service

start-contract-test-service-bg:
	echo "Test service output will be captured in $(TEMP_TEST_OUTPUT)"
	make start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &

run-contract-tests:
	curl -s https://raw.githubusercontent.com/launchdarkly/sse-contract-tests/main/downloader/run.sh \
		| VERSION=v2 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end -skip 'basic parsing/large message in one chunk' -skip 'basic parsing/large message in two chunks'" sh

contract-tests: build-contract-tests start-contract-test-service-bg run-contract-tests

.PHONY: build clean test build-contract-tests start-contract-test-service run-contract-tests contract-tests


================================================
FILE: Package.swift
================================================
// swift-tools-version:5.0

import PackageDescription

let package = Package(
    name: "LDSwiftEventSource",
    platforms: [
        .iOS(.v11),
        .macOS(.v10_13),
        .watchOS(.v4),
        .tvOS(.v11)
    ],
    products: [
        .library(name: "LDSwiftEventSource", targets: ["LDSwiftEventSource"]),
    ],
    dependencies: [],
    targets: [
        .target(
            name: "LDSwiftEventSource",
            path: "Source"),
        .testTarget(
            name: "LDSwiftEventSourceTests",
            dependencies: ["LDSwiftEventSource"],
            path: "Tests"),
    ],
    swiftLanguageVersions: [.v5])


================================================
FILE: README.md
================================================
# LDSwiftEventSource

[![Run CI](https://github.com/launchdarkly/swift-eventsource/actions/workflows/ci.yml/badge.svg)](https://github.com/launchdarkly/swift-eventsource/actions/workflows/ci.yml)
[![CocoaPods](https://img.shields.io/cocoapods/v/LDSwiftEventSource.svg)](https://cocoapods.org/pods/LDSwiftEventSource)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-4BC51D.svg?style=flat)](https://swift.org/package-manager/)
[![Platform](https://img.shields.io/cocoapods/p/LDSwiftEventSource.svg?style=flat)](https://cocoapods.org/pods/LDSwiftEventSource)

LDSwiftEventSource is a cross platform implementation of the [EventSource specification](https://html.spec.whatwg.org/multipage/server-sent-events.html) written in Swift. It was developed for use in the [LaunchDarkly iOS SDK](https://github.com/launchdarkly/ios-client-sdk). Generated API docs are available on [GitHub Pages](https://launchdarkly.github.io/swift-eventsource/).

## Requirements
- iOS 11.0+ / watchOS 4.0+ / tvOS 11.0+ / macOS 10.13+
- Swift 5.1+

## Installation

### CocoaPods

To use the [CocoaPods](https://cocoapods.org) dependency manager to integrate LDSwiftEventSource into your Xcode project, specify it in your `Podfile`:

```ruby
pod 'LDSwiftEventSource', '~> 3.3'
```

### Carthage

To use the [Carthage](https://github.com/Carthage/Carthage) dependency manager to integrate LDSwiftEventSource into your Xcode project, specify it in your `Cartfile`:

```ogdl
github "LaunchDarkly/swift-eventsource" ~> 3.3
```

### Swift Package Manager

The [Swift Package Manager](https://swift.org/package-manager/) is a dependency manager integrated into the `swift` compiler and Xcode. Note that the LDSwiftEventSource Swift package provides both a `LDSwiftEventSource` product, which is explicitly dynamic, and a `LDSwiftEventSourceStatic` product which is explicitly static.

To integrate LDSwiftEventSource into an Xcode project, go to the project editor, and select `Swift Packages`. From here hit the `+` button and follow the prompts using  `https://github.com/LaunchDarkly/swift-eventsource.git` as the URL.

To include LDSwiftEventSource in a Swift package, simply add it to the dependencies section of your `Package.swift` file. And add the desired product as a dependency for your targets.

<!-- x-release-please-start-version -->
```swift
dependencies: [
    .package(url: "https://github.com/LaunchDarkly/swift-eventsource.git", .upToNextMajor(from: "3.3.0"))
]
```
<!-- x-release-please-end -->

## Contributing

We encourage pull requests and other contributions from the community. Check out our [contributing guidelines](https://github.com/LaunchDarkly/swift-eventsource/blob/main/CONTRIBUTING.md) for instructions on how to contribute to this SDK.

## About LaunchDarkly

* LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard.  With LaunchDarkly, you can:
    * Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
    * Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
    * Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
    * Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan). Disable parts of your application to facilitate maintenance, without taking everything offline.
* LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Check out [our documentation](https://docs.launchdarkly.com/sdk) for a complete list.
* Explore LaunchDarkly
    * [launchdarkly.com](https://www.launchdarkly.com/ "LaunchDarkly Main Website") for more information
    * [docs.launchdarkly.com](https://docs.launchdarkly.com/  "LaunchDarkly Documentation") for our documentation and SDK reference guides
    * [apidocs.launchdarkly.com](https://apidocs.launchdarkly.com/  "LaunchDarkly API Documentation") for our API documentation
    * [blog.launchdarkly.com](https://blog.launchdarkly.com/  "LaunchDarkly Blog Documentation") for the latest product updates


================================================
FILE: SECURITY.md
================================================
# Reporting and Fixing Security Issues

Please report all security issues to the LaunchDarkly security team by submitting a bug bounty report to our [HackerOne program](https://hackerone.com/launchdarkly?type=team). LaunchDarkly will triage and address all valid security issues following the response targets defined in our program policy. Valid security issues may be eligible for a bounty.

Please do not open issues or pull requests for security issues. This makes the problem immediately visible to everyone, including potentially malicious actors.


================================================
FILE: Source/.swiftlint.yml
================================================
disabled_rules:

opt_in_rules:
  - force_unwrapping
  - implicitly_unwrapped_optional


================================================
FILE: Source/EventParser.swift
================================================
import Foundation

class EventParser {
    private struct Constants {
        static let dataLabel: Substring = "data"
        static let idLabel: Substring = "id"
        static let eventLabel: Substring = "event"
        static let retryLabel: Substring = "retry"
    }

    private let handler: EventHandler

    private var data: String = ""
    private var eventType: String = ""
    private var lastEventIdBuffer: String?
    private var lastEventId: String
    private var currentRetry: TimeInterval

    init(handler: EventHandler, initialEventId: String, initialRetry: TimeInterval) {
        self.handler = handler
        self.lastEventId = initialEventId
        self.currentRetry = initialRetry
    }

    func parse(line: String) {
        let splitByColon = line.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)

        switch (splitByColon[0], splitByColon[safe: 1]) {
        case ("", nil): // Empty line
            dispatchEvent()
        case let ("", .some(comment)): // Line starting with ':' is a comment
            handler.onComment(comment: String(comment))
        case let (field, data):
            processField(field: field, value: dropLeadingSpace(str: data ?? ""))
        }
    }

    func getLastEventId() -> String { lastEventId }

    func reset() -> TimeInterval {
        data = ""
        eventType = ""
        lastEventIdBuffer = nil
        return currentRetry
    }

    private func dropLeadingSpace(str: Substring) -> Substring {
        if str.first == " " {
            return str[str.index(after: str.startIndex)...]
        }
        return str
    }

    private func processField(field: Substring, value: Substring) {
        switch field {
        case Constants.dataLabel:
            data.append(contentsOf: value)
            data.append(contentsOf: "\n")
        case Constants.idLabel:
            // See https://github.com/whatwg/html/issues/689 for reasoning on not setting lastEventId if the value
            // contains a null code point.
            if !value.contains("\u{0000}") {
                lastEventIdBuffer = String(value)
            }
        case Constants.eventLabel:
            eventType = String(value)
        case Constants.retryLabel:
            if value.allSatisfy(("0"..."9").contains), let reconnectionTime = Int64(value) {
                currentRetry = Double(reconnectionTime) * 0.001
            }
        default:
            break
        }
    }

    private func dispatchEvent() {
        lastEventId = lastEventIdBuffer ?? lastEventId
        lastEventIdBuffer = nil
        guard !data.isEmpty
        else {
            eventType = ""
            return
        }
        // remove the last LF
        _ = data.popLast()
        let messageEvent = MessageEvent(data: data, lastEventId: lastEventId)
        handler.onMessage(eventType: eventType.isEmpty ? "message" : eventType, messageEvent: messageEvent)
        data = ""
        eventType = ""
    }
}

private extension Array {
    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        index >= startIndex && index < endIndex ? self[index] : nil
    }
}


================================================
FILE: Source/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
	<key>CFBundleShortVersionString</key>
	<string>$(MARKETING_VERSION)</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>


================================================
FILE: Source/LDSwiftEventSource.h
================================================
@import Foundation;

FOUNDATION_EXPORT double LDSwiftEventSourceVersionNumber;
FOUNDATION_EXPORT const unsigned char LDSwiftEventSourceVersionString[];


================================================
FILE: Source/LDSwiftEventSource.swift
================================================
import Foundation

#if os(Linux) || os(Windows)
import FoundationNetworking
#endif

#if canImport(os)
// os_log is not supported on some platforms, but we want to use it for most of our customer's
// use cases that use Apple OSs
import os.log
#endif

/**
 Provides an EventSource client for consuming Server-Sent Events.

 See the [Server-Sent Events spec](https://html.spec.whatwg.org/multipage/server-sent-events.html) for more details.
 */
public class EventSource {
    private let esDelegate: EventSourceDelegate

    /**
     Initialize the `EventSource` client with the given configuration.

     - Parameter config: The configuration for initializing the `EventSource` client.
     */
    public init(config: Config) {
        esDelegate = EventSourceDelegate(config: config)
    }

    /**
     Start the `EventSource` client.

     This will initiate a streaming connection to the configured URL. The application will be informed of received
     events and state changes using the configured `EventHandler`.
     */
    public func start() {
        esDelegate.start()
    }

    /// Shuts down the `EventSource` client. It is not valid to restart the client after calling this function.
    public func stop() {
        esDelegate.stop()
    }

    /// Get the most recently received event ID, or the value of `EventSource.Config.lastEventId` if no event IDs have
    /// been received.
    public func getLastEventId() -> String? { esDelegate.getLastEventId() }

    /// Struct for configuring the EventSource.
    public struct Config {
        /// The `EventHandler` called in response to activity on the stream.
        public let handler: EventHandler
        /// The `URL` of the request used when connecting to the EventSource API.
        public let url: URL

        /// The HTTP method to use for the API request.
        public var method: String = "GET"
        /// Optional HTTP body to be included in the API request.
        public var body: Data?
        /// Additional HTTP headers to be set on the request
        public var headers: [String: String] = [:]
        /// Transform function to allow dynamically configuring the headers on each API request.
        public var headerTransform: HeaderTransform = { $0 }
        /// An initial value for the last-event-id header to be sent on the initial request
        public var lastEventId: String = ""
        
#if canImport(os)
        /// Configure the logger that will be used.
        public var logger: OSLog = OSLog(subsystem: "com.launchdarkly.swift-eventsource", category: "LDEventSource")
#endif
        
        /// The minimum amount of time to wait before reconnecting after a failure
        public var reconnectTime: TimeInterval = 1.0
        /// The maximum amount of time to wait before reconnecting after a failure
        public var maxReconnectTime: TimeInterval = 30.0
        /// The minimum amount of time for an `EventSource` connection to remain open before allowing the connection
        /// backoff to reset.
        public var backoffResetThreshold: TimeInterval = 60.0
        /// The maximum amount of time between receiving any data before considering the connection to have timed out.
        public var idleTimeout: TimeInterval = 300.0

        private var _urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default
        /**
         The `URLSessionConfiguration` used to create the `URLSession`.

         - Important:
            Note that this copies the given `URLSessionConfiguration` when set, and returns copies (updated with any
         overrides specified by other configuration options) when the value is retrieved. This prevents updating the
         `URLSessionConfiguration` after initializing `EventSource` with the `Config`, and prevents the `EventSource`
         from updating any properties of the given `URLSessionConfiguration`.

         - Since: 1.3.0
         */
        public var urlSessionConfiguration: URLSessionConfiguration {
            get {
                // swiftlint:disable:next force_cast
                let sessionConfig = _urlSessionConfiguration.copy() as! URLSessionConfiguration
                sessionConfig.httpAdditionalHeaders = ["Accept": "text/event-stream", "Cache-Control": "no-cache"]
                sessionConfig.timeoutIntervalForRequest = idleTimeout

                #if !os(Linux) && !os(Windows)
                if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
                    sessionConfig.tlsMinimumSupportedProtocolVersion = .TLSv12
                } else {
                    sessionConfig.tlsMinimumSupportedProtocol = .tlsProtocol12
                }
                #endif
                return sessionConfig
            }
            set {
                // swiftlint:disable:next force_cast
                _urlSessionConfiguration = newValue.copy() as! URLSessionConfiguration
            }
        }

        /**
         An error handler that is called when an error occurs and can shut down the client in response.

         The default error handler will always attempt to reconnect on an
         error, unless `EventSource.stop()` is called or the error code is 204.
         */
        public var connectionErrorHandler: ConnectionErrorHandler = { error in
            guard let unsuccessfulResponseError = error as? UnsuccessfulResponseError
            else { return .proceed }

            let responseCode: Int = unsuccessfulResponseError.responseCode
            if 204 == responseCode {
                return .shutdown
            }
            return .proceed
        }

        /// Create a new configuration with an `EventHandler` and a `URL`
        public init(handler: EventHandler, url: URL) {
            self.handler = handler
            self.url = url
        }
    }
}

class ReconnectionTimer {
    private let maxDelay: TimeInterval
    private let resetInterval: TimeInterval

    var backoffCount: Int = 0
    var connectedTime: Date?

    init(maxDelay: TimeInterval, resetInterval: TimeInterval) {
        self.maxDelay = maxDelay
        self.resetInterval = resetInterval
    }

    func reconnectDelay(baseDelay: TimeInterval) -> TimeInterval {
        backoffCount += 1
        if let connectedTime = connectedTime, Date().timeIntervalSince(connectedTime) >= resetInterval {
            backoffCount = 0
        }
        self.connectedTime = nil
        let maxSleep = min(maxDelay, baseDelay * pow(2.0, Double(backoffCount)))
        return maxSleep / 2 + Double.random(in: 0...(maxSleep / 2))
    }
}

// MARK: EventSourceDelegate
class EventSourceDelegate: NSObject, URLSessionDataDelegate {
    private let delegateQueue: DispatchQueue = DispatchQueue(label: "ESDelegateQueue")
    
    public var logger: InternalLogging
    
    private let config: EventSource.Config

    private var readyState: ReadyState = .raw {
        didSet {
            logger.log(.debug, "State: %@ -> %@", oldValue.rawValue, readyState.rawValue)
        }
    }

    private let utf8LineParser: UTF8LineParser = UTF8LineParser()
    private let eventParser: EventParser
    private let reconnectionTimer: ReconnectionTimer
    private var urlSession: URLSession?
    private var sessionTask: URLSessionDataTask?

    init(config: EventSource.Config) {
        self.config = config
        
#if canImport(os)
        self.logger = OSLogAdapter(osLog: config.logger)
#else
        self.logger = NoOpLogging()
#endif
        
        
        self.eventParser = EventParser(handler: config.handler,
                                       initialEventId: config.lastEventId,
                                       initialRetry: config.reconnectTime)
        self.reconnectionTimer = ReconnectionTimer(maxDelay: config.maxReconnectTime,
                                                   resetInterval: config.backoffResetThreshold)
    }

    func start() {
        delegateQueue.async { [weak self] in
            guard let self = self
            else { return }
            guard self.readyState == .raw
            else {
                self.logger.log(.info, "start() called on already-started EventSource object. Returning")
                return
            }
            self.readyState = .connecting
            self.urlSession = self.createSession()
            self.connect()
        }
    }

    func stop() {
        delegateQueue.async {
            let previousState = self.readyState
            self.readyState = .shutdown
            self.sessionTask?.cancel()
            if previousState == .open {
                self.config.handler.onClosed()
            }
            self.urlSession?.invalidateAndCancel()
            self.urlSession = nil
        }
    }

    func getLastEventId() -> String { eventParser.getLastEventId() }

    func createSession() -> URLSession {
        let opQueue = OperationQueue()
        opQueue.underlyingQueue = self.delegateQueue
        return URLSession(configuration: config.urlSessionConfiguration, delegate: self, delegateQueue: opQueue)
    }

    func createRequest() -> URLRequest {
        var urlRequest = URLRequest(url: self.config.url,
                                    cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData,
                                    timeoutInterval: self.config.idleTimeout)
        urlRequest.httpMethod = self.config.method
        urlRequest.httpBody = self.config.body
        if !eventParser.getLastEventId().isEmpty {
            urlRequest.setValue(eventParser.getLastEventId(), forHTTPHeaderField: "Last-Event-Id")
        }
        urlRequest.allHTTPHeaderFields = self.config.headerTransform(
            urlRequest.allHTTPHeaderFields?.merging(self.config.headers) { $1 } ?? self.config.headers
        )
        return urlRequest
    }

    private func connect() {
        logger.log(.info, "Starting EventSource client")
        let task = urlSession?.dataTask(with: createRequest())
        task?.resume()
        sessionTask = task
    }

    func dispatchError(error: Error) -> ConnectionErrorAction {
        let action: ConnectionErrorAction = config.connectionErrorHandler(error)
        if action != .shutdown {
            config.handler.onError(error: error)
        }
        return action
    }

    // MARK: URLSession Delegates

    // Tells the delegate that the task finished transferring data.
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           didCompleteWithError error: Error?) {
        utf8LineParser.closeAndReset()
        let currentRetry = eventParser.reset()

        guard readyState != .shutdown
        else { return }

        if let error = error {
            if (error as NSError).code != NSURLErrorCancelled {
                logger.log(.info, "Connection error: %@", error.localizedDescription)
                if dispatchError(error: error) == .shutdown {
                    logger.log(.info, "Connection has been explicitly shut down by error handler")
                    if readyState == .open {
                        config.handler.onClosed()
                    }
                    readyState = .shutdown
                    return
                }
            }
        } else {
            logger.log(.info, "Connection unexpectedly closed.")
        }

        if readyState == .open {
            config.handler.onClosed()
        }

        readyState = .closed
        let sleep = reconnectionTimer.reconnectDelay(baseDelay: currentRetry)
        // this formatting shenanigans is to workaround String not implementing CVarArg on Swift<5.4 on Linux
        logger.log(.info, "Waiting %@ seconds before reconnecting...", String(format: "%.3f", sleep))
        delegateQueue.asyncAfter(deadline: .now() + sleep) { [weak self] in
            self?.connect()
        }
    }

    // Tells the delegate that the data task received the initial reply (headers) from the server.
    public func urlSession(_ session: URLSession,
                           dataTask: URLSessionDataTask,
                           didReceive response: URLResponse,
                           completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        logger.log(.debug, "Initial reply received")

        guard readyState != .shutdown
        else {
            completionHandler(.cancel)
            return
        }

        // swiftlint:disable:next force_cast
        let httpResponse = response as! HTTPURLResponse
        let statusCode = httpResponse.statusCode
        if (200..<300).contains(statusCode) && statusCode != 204 {
            reconnectionTimer.connectedTime = Date()
            readyState = .open
            config.handler.onOpened()
            completionHandler(.allow)
        } else {
            // this formatting shenanigans is to workaround String not implementing CVarArg on Swift<5.4 on Linux
            logger.log(.info, "Unsuccessful response: %@", String(format: "%d", statusCode))
            if dispatchError(error: UnsuccessfulResponseError(responseCode: statusCode)) == .shutdown {
                logger.log(.info, "Connection has been explicitly shut down by error handler")
                readyState = .shutdown
            }
            completionHandler(.cancel)
        }
    }

    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        utf8LineParser.append(data).forEach(eventParser.parse)
    }
}


================================================
FILE: Source/Logs.swift
================================================
import Foundation

#if canImport(os)
import os.log
#endif

protocol InternalLogging {
    func log(_ level: Level, _ staticMsg: StaticString)
    func log(_ level: Level, _ staticMsg: StaticString, _ arg: String)
    func log(_ level: Level, _ staticMsg: StaticString, _ arg1: String, _ arg2: String)
}

enum Level {
    case debug, info, warn, error

#if canImport(os)
    private static let osLogTypes = [ Level.debug: OSLogType.debug,
                                      Level.info: OSLogType.info,
                                      Level.warn: OSLogType.default,
                                      Level.error: OSLogType.error]
    var osLogType: OSLogType { Level.osLogTypes[self]! }
#endif
}

#if canImport(os)
class OSLogAdapter: InternalLogging {
    
    private let osLog: OSLog
    
    init(osLog: OSLog) {
        self.osLog = osLog
    }
    
    func log(_ level: Level, _ staticMsg: StaticString) {
        os_log(staticMsg, log: self.osLog, type: level.osLogType)
    }
    
    func log(_ level: Level, _ staticMsg: StaticString, _ arg: String) {
        os_log(staticMsg, log: self.osLog, type: level.osLogType, arg)
    }
    
    func log(_ level: Level, _ staticMsg: StaticString, _ arg1: String, _ arg2: String) {
        os_log(staticMsg, log: self.osLog, type: level.osLogType, arg1, arg2)
    }
}
#endif

class NoOpLogging: InternalLogging {
    func log(_ level: Level, _ staticMsg: StaticString) {}
    func log(_ level: Level, _ staticMsg: StaticString, _ arg: String) {}
    func log(_ level: Level, _ staticMsg: StaticString, _ arg1: String, _ arg2: String) {}
}


================================================
FILE: Source/Types.swift
================================================
import Foundation

/**
 Type for a function that will be notified when the `EventSource` client encounters a connection failure.

 This is different from `EventHandler.onError(error:)` in that it will not be called for other kinds of errors; also,
 it has the ability to tell the client to stop reconnecting by returning a `ConnectionErrorAction.shutdown`.
*/
public typealias ConnectionErrorHandler = (Error) -> ConnectionErrorAction

/**
 Type for a function that will take in the current HTTP headers and return a new set of HTTP headers to be used when
 connecting and reconnecting to a stream.
 */
public typealias HeaderTransform = ([String: String]) -> [String: String]

/// Potential actions a `ConnectionErrorHandler` can return
public enum ConnectionErrorAction {
    /**
     Specifies that the error should be logged normally and dispatched to the `EventHandler`. Connection retrying will
     proceed normally if appropriate.
     */
    case proceed
    /**
     Specifies that the connection should be immediately shut down and not retried. The error will not be dispatched
     to the `EventHandler`
     */
    case shutdown
}

/// Struct representing received event from the stream.
public struct MessageEvent: Equatable, Hashable {
    /// The event data of the event.
    public let data: String
    /// The last seen event id, or the event id set in the Config if none have been received.
    public let lastEventId: String

    /**
     Constructor for a `MessageEvent`

     - Parameter data: The `data` field of the `MessageEvent`.
     - Parameter eventType: The `lastEventId` field of the `MessageEvent`.
     */
    public init(data: String, lastEventId: String = "") {
        self.data = data
        self.lastEventId = lastEventId
    }
}

/// Protocol for an object that will receive SSE events.
public protocol EventHandler {
    /// EventSource calls this method when the stream connection has been opened.
    func onOpened()

    /// EventSource calls this method when the stream connection has been closed.
    func onClosed()

    /**
     EventSource calls this method when it has received a new event from the stream.

     - Parameter eventType: The type of the event.
     - Parameter messageEvent: The data for the event.
     */
    func onMessage(eventType: String, messageEvent: MessageEvent)

    /**
     EventSource calls this method when it has received a comment line from the stream.

     - Parameter comment: The comment received.
     */
    func onComment(comment: String)

    /**
     This method will be called for all exceptions that occur on the network connection (including an
     `UnsuccessfulResponseError` if the server returns an unexpected HTTP status), but only after the
     ConnectionErrorHandler (if any) has processed it.  If you need to do anything that affects the state of the
     connection, use ConnectionErrorHandler.

     - Parameter error: The error received.
     */
    func onError(error: Error)
}

/// Enum values representing the states of an EventSource
public enum ReadyState: String, Equatable {
    /// The `EventSource` client has not been started yet.
    case raw
    /// The `EventSource` client is attempting to make a connection.
    case connecting
    /// The `EventSource` client is active and listening for events.
    case open
    /// The connection has been closed or has failed, and the `EventSource` will attempt to reconnect.
    case closed
    /// The connection has been permanently closed and the `EventSource` not reconnect.
    case shutdown
}

/// Error class that indicates the remote server returned an unsuccessful HTTP response code.
public class UnsuccessfulResponseError: Error {
    /// The HTTP response code received.
    public let responseCode: Int

    /**
     Constructor for an `UnsuccessfulResponseError`.

     - Parameter responseCode: The HTTP response code of the unsuccessful response.
     */
    public init(responseCode: Int) {
        self.responseCode = responseCode
    }
}


================================================
FILE: Source/UTF8LineParser.swift
================================================
import Foundation

struct DataIter: IteratorProtocol {
    var data: Data
    var position: Data.Index { data.startIndex }

    mutating func next() -> UInt8? {
        data.popFirst()
    }
}

class UTF8LineParser {
    private let lf = Unicode.Scalar(0x0A)
    private let cr = Unicode.Scalar(0x0D)
    private let replacement = String(Unicode.UTF8.decode(Unicode.UTF8.encodedReplacementCharacter))

    var utf8Parser = Unicode.UTF8.ForwardParser()
    var remainder: Data = Data()
    var currentString: String = ""
    var seenCr = false

    func append(_ body: Data) -> [String] {
        let data = remainder + body
        var dataIter = DataIter(data: data)
        var remainderPos = data.endIndex
        var lines: [String] = []

        Decode: while true {
            switch utf8Parser.parseScalar(from: &dataIter) {
            case .valid(let scalarResult):
                let scalar = Unicode.UTF8.decode(scalarResult)

                if seenCr && scalar == lf {
                    seenCr = false
                    continue
                }

                seenCr = scalar == cr
                if scalar == cr || scalar == lf {
                    lines.append(currentString)
                    currentString = ""
                } else {
                    currentString.append(String(scalar))
                }
            case .emptyInput:
                break Decode
            case .error(let len):
                seenCr = false
                if dataIter.position == data.endIndex {
                    // Error at end of block, carry over in case of split code point
                    remainderPos = data.index(data.endIndex, offsetBy: -len)
                    // May as well break here as next will be .emptyInput
                    break Decode
                } else {
                    // Invalid character, replace with replacement character
                    currentString.append(replacement)
                }
            }
        }

        remainder = data.subdata(in: remainderPos..<data.endIndex)
        return lines
    }

    func closeAndReset() {
        seenCr = false
        currentString = ""
        remainder = Data()
    }
}


================================================
FILE: Tests/.swiftlint.yml
================================================
disabled_rules:

opt_in_rules:
  # Must specify even though enabled by default to update configuration of rule below.
  - line_length
  - type_body_length

# Provide a little extra lenience for test code line and body length.
line_length:
  warning: 140
type_body_length:
  warning: 400
  error: 500

excluded:
  # Autogenerated manifest of tests
  - XCTestManifests.swift


================================================
FILE: Tests/EventParserTests.swift
================================================
import XCTest
@testable import LDSwiftEventSource

final class EventParserTests: XCTestCase {
    var handler: MockHandler!
    var parser: EventParser!

    override func setUp() {
        super.setUp()
        handler = MockHandler()
        parser = EventParser(handler: handler, initialEventId: "", initialRetry: 1.0)
    }

    override func tearDown() {
        super.tearDown()
        XCTAssertNil(handler.events.maybeEvent())
    }

    // MARK: Retry time tests
    func testUnsetRetryReturnsConfigured() {
        parser = EventParser(handler: handler, initialEventId: "", initialRetry: 5.0)
        XCTAssertEqual(parser.reset(), 5.0)
    }

    func testSetsRetryTimeToSevenSeconds() {
        parser.parse(line: "retry: 7000")
        XCTAssertEqual(parser.reset(), 7.0)
        XCTAssertEqual(parser.getLastEventId(), "")
    }

    func testRetryWithNoSpace() {
        parser.parse(line: "retry:7000")
        XCTAssertEqual(parser.reset(), 7.0)
        XCTAssertEqual(parser.getLastEventId(), "")
    }

    func testDoesNotSetRetryTimeUnlessEntireValueIsNumeric() {
        parser.parse(line: "retry: 7000L")
        XCTAssertEqual(parser.reset(), 1.0)
    }

    func testSafeToUseEmptyRetryTime() {
        parser.parse(line: "retry")
        XCTAssertEqual(parser.reset(), 1.0)
    }

    func testSafeToAttemptToSetRetryToOutOfBoundsValue() {
        parser.parse(line: "retry: 10000000000000000000000000")
        XCTAssertEqual(parser.reset(), 1.0)
    }

    func testResetDoesNotResetRetry() {
        parser.parse(line: "retry: 7000")
        XCTAssertEqual(parser.reset(), 7.0)
        XCTAssertEqual(parser.reset(), 7.0)
    }

    func testRetryNotChangedDuringOtherMessages() {
        parser.parse(line: "retry: 7000")
        parser.parse(line: "")
        parser.parse(line: ":123")
        parser.parse(line: "event: 123")
        parser.parse(line: "data: 123")
        parser.parse(line: "id: 123")
        parser.parse(line: "none: 123")
        parser.parse(line: "")
        XCTAssertEqual(parser.reset(), 7.0)
        _ = handler.events.maybeEvent()
        _ = handler.events.maybeEvent()
    }

    // MARK: Comment tests
    func testEmptyComment() {
        parser.parse(line: ":")
        XCTAssertEqual(handler.events.maybeEvent(), .comment(""))
    }

    func testCommentBody() {
        parser.parse(line: ": comment")
        XCTAssertEqual(handler.events.maybeEvent(), .comment(" comment"))
    }

    func testCommentCanContainColon() {
        parser.parse(line: ":comment:line")
        XCTAssertEqual(handler.events.maybeEvent(), .comment("comment:line"))
    }

    // MARK: Message data tests
    func testDispatchesEmptyMessageData() {
        parser.parse(line: "data")
        parser.parse(line: "")
        parser.parse(line: "data:")
        parser.parse(line: "")
        parser.parse(line: "data: ")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "", lastEventId: "")))
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "", lastEventId: "")))
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "", lastEventId: "")))
    }

    func testDoesNotRemoveTrailingSpaceWhenColonNotPresent() {
        parser.parse(line: "data ")
        parser.parse(line: "")
        XCTAssertNil(handler.events.maybeEvent())
    }

    func testEmptyFirstDataAppendsNewline() {
        parser.parse(line: "data:")
        parser.parse(line: "data:")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "\n", lastEventId: "")))
    }

    func testDispatchesSingleLineMessage() {
        parser.parse(line: "data: hello")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "hello", lastEventId: "")))
    }

    func testEmptyDataWithBufferedDataAppendsNewline() {
        parser.parse(line: "data: data1")
        parser.parse(line: "data: ")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "data1\n", lastEventId: "")))
    }

    func testDataResetAfterEvent() {
        parser.parse(line: "data: hello")
        parser.parse(line: "")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "hello", lastEventId: "")))
    }

    func testRemovesOnlyFirstSpace() {
        parser.parse(line: "data:  {\"foo\": \"bar baz\"}")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: " {\"foo\": \"bar baz\"}", lastEventId: "")))
    }

    func testDoesNotRemoveOtherWhitespace() {
        parser.parse(line: "data:\t{\"foo\": \"bar baz\"}")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "\t{\"foo\": \"bar baz\"}", lastEventId: "")))
    }

    func testAllowsNoLeadingSpace() {
        parser.parse(line: "data:{\"foo\": \"bar baz\"}")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "{\"foo\": \"bar baz\"}", lastEventId: "")))
    }

    func testMultipleDataDispatch() {
        parser.parse(line: "data: data1")
        parser.parse(line: "data: data2")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "data1\ndata2", lastEventId: "")))
    }

    // MARK: Event type tests
    func testDispatchesMessageWithCustomEventType() {
        parser.parse(line: "event: customEvent")
        parser.parse(line: "data: hello")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("customEvent", MessageEvent(data: "hello", lastEventId: "")))
    }

    func testCustomEventTypeWithoutSpace() {
        parser.parse(line: "event:customEvent")
        parser.parse(line: "data: hello")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("customEvent", MessageEvent(data: "hello", lastEventId: "")))
    }

    func testCustomEventAfterData() {
        parser.parse(line: "data: hello")
        parser.parse(line: "event: customEvent")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("customEvent", MessageEvent(data: "hello", lastEventId: "")))
    }

    func testEmptyEventTypesDefaultToMessage() {
        ["event", "event:", "event: "].forEach {
            parser.parse(line: $0)
            parser.parse(line: "data: foo")
            parser.parse(line: "")
        }
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "foo", lastEventId: "")))
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "foo", lastEventId: "")))
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "foo", lastEventId: "")))
    }

    func testDispatchWithoutDataResetsMessageType() {
        parser.parse(line: "event: customEvent")
        parser.parse(line: "")
        parser.parse(line: "data: foo")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "foo", lastEventId: "")))
    }

    func testDispatchWithDataResetsMessageType() {
        parser.parse(line: "event: customEvent")
        parser.parse(line: "data: foo")
        parser.parse(line: "")
        parser.parse(line: "data: bar")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("customEvent", MessageEvent(data: "foo", lastEventId: "")))
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "bar", lastEventId: "")))
    }

    // MARK: Last event ID tests
    func testLastEventIdNotReturnedUntilDispatch() {
        XCTAssertEqual(parser.getLastEventId(), "")
        parser.parse(line: "id: 1")
        XCTAssertNil(handler.events.maybeEvent())
        XCTAssertEqual(parser.getLastEventId(), "")
    }

    func testRecordsLastEventIdWithoutData() {
        parser.parse(line: "id: 1")
        parser.parse(line: "")
        XCTAssertNil(handler.events.maybeEvent())
        XCTAssertEqual(parser.getLastEventId(), "1")
    }

    func testEventIdIncludedInMessageEvent() {
        parser.parse(line: "data: hello")
        parser.parse(line: "id: 1")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "hello", lastEventId: "1")))
    }

    func testReusesEventIdIfNotSet() {
        parser.parse(line: "data: hello")
        parser.parse(line: "id: reused")
        parser.parse(line: "")
        parser.parse(line: "data: world")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "hello", lastEventId: "reused")))
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "world", lastEventId: "reused")))
        XCTAssertEqual(parser.getLastEventId(), "reused")
    }

    func testEventIdSetTwiceInEvent() {
        parser.parse(line: "id: abc")
        parser.parse(line: "id: def")
        parser.parse(line: "data")
        XCTAssertEqual(parser.getLastEventId(), "")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "", lastEventId: "def")))
        XCTAssertEqual(parser.getLastEventId(), "def")
    }

    func testEventIdContainingNullIgnored() {
        parser.parse(line: "id: reused")
        parser.parse(line: "id: abc\u{0000}def")
        parser.parse(line: "data")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "", lastEventId: "reused")))
        XCTAssertEqual(parser.getLastEventId(), "reused")
    }

    func testResetDoesResetLastEventIdBuffer() {
        parser.parse(line: "id: 1")
        _ = parser.reset()
        parser.parse(line: "data: hello")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "hello", lastEventId: "")))
        XCTAssertEqual(parser.getLastEventId(), "")
    }

    func testResetDoesNotResetLastEventId() {
        parser.parse(line: "id: 1")
        parser.parse(line: "")
        _ = parser.reset()
        parser.parse(line: "data: hello")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("message", MessageEvent(data: "hello", lastEventId: "1")))
        XCTAssertEqual(parser.getLastEventId(), "1")
    }

    // MARK: Mixed and other tests
    func testRepeatedEmptyLines() {
        parser.parse(line: "")
        parser.parse(line: "")
        parser.parse(line: "")
        XCTAssertNil(handler.events.maybeEvent())
    }

    func testNothingDoneForInvalidFieldName() {
        parser.parse(line: "invalid: bar")
        XCTAssertNil(handler.events.maybeEvent())
    }

    func testInvalidFieldNameIgnoredInEvent() {
        parser.parse(line: "data: foo")
        parser.parse(line: "invalid: bar")
        parser.parse(line: "event: msg")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .message("msg", MessageEvent(data: "foo", lastEventId: "")))
    }

    func testCommentInEvent() {
        parser.parse(line: "data: foo")
        parser.parse(line: ":bar")
        parser.parse(line: "event: msg")
        parser.parse(line: "")
        XCTAssertEqual(handler.events.maybeEvent(), .comment("bar"))
        XCTAssertEqual(handler.events.maybeEvent(), .message("msg", MessageEvent(data: "foo", lastEventId: "")))
    }
}


================================================
FILE: Tests/LDSwiftEventSourceTests.swift
================================================
import XCTest
@testable import LDSwiftEventSource

#if os(Linux) || os(Windows)
import FoundationNetworking
#endif

final class LDSwiftEventSourceTests: XCTestCase {
    private var mockHandler: MockHandler!

    override func setUp() {
        super.setUp()
        mockHandler = MockHandler()
        XCTAssertTrue(URLProtocol.registerClass(MockingProtocol.self))
    }

    override func tearDown() {
        super.tearDown()
        URLProtocol.unregisterClass(MockingProtocol.self)
        // Enforce that tests consume all mocked network requests
        MockingProtocol.requested.expectNoEvent(within: 0.01)
        MockingProtocol.resetRequested()
        // Enforce that tests consume all calls to the mock handler
        mockHandler.events.expectNoEvent(within: 0.01)
        mockHandler = nil
    }

    func testConfigDefaults() {
        let url = URL(string: "abc")!
        let config = EventSource.Config(handler: mockHandler, url: url)
        XCTAssertEqual(config.url, url)
        XCTAssertEqual(config.method, "GET")
        XCTAssertEqual(config.body, nil)
        XCTAssertEqual(config.lastEventId, "")
        XCTAssertEqual(config.headers, [:])
        XCTAssertEqual(config.reconnectTime, 1.0)
        XCTAssertEqual(config.maxReconnectTime, 30.0)
        XCTAssertEqual(config.backoffResetThreshold, 60.0)
        XCTAssertEqual(config.idleTimeout, 300.0)
        XCTAssertEqual(config.headerTransform(["abc": "123"]), ["abc": "123"])
        XCTAssertEqual(config.connectionErrorHandler(DummyError()), .proceed)
    }

    func testConfigModification() {
        let url = URL(string: "abc")!
        var config = EventSource.Config(handler: mockHandler, url: url)

        let testBody = "test data".data(using: .utf8)
        let testHeaders = ["Authorization": "basic abc"]

        config.method = "REPORT"
        config.body = testBody
        config.lastEventId = "eventId"
        config.headers = testHeaders
        config.reconnectTime = 2.0
        config.maxReconnectTime = 60.0
        config.backoffResetThreshold = 120.0
        config.idleTimeout = 180.0
        config.headerTransform = { _ in [:] }
        config.connectionErrorHandler = { _ in .shutdown }

        XCTAssertEqual(config.url, url)
        XCTAssertEqual(config.method, "REPORT")
        XCTAssertEqual(config.body, testBody)
        XCTAssertEqual(config.lastEventId, "eventId")
        XCTAssertEqual(config.headers, testHeaders)
        XCTAssertEqual(config.headerTransform(config.headers), [:])
        XCTAssertEqual(config.reconnectTime, 2.0)
        XCTAssertEqual(config.maxReconnectTime, 60.0)
        XCTAssertEqual(config.backoffResetThreshold, 120.0)
        XCTAssertEqual(config.idleTimeout, 180.0)
        XCTAssertEqual(config.connectionErrorHandler(DummyError()), .shutdown)
    }

    func testConfigUrlSession() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!)
        let defaultSessionConfig = config.urlSessionConfiguration
        XCTAssertEqual(defaultSessionConfig.timeoutIntervalForRequest, 300.0)
        XCTAssertEqual(defaultSessionConfig.httpAdditionalHeaders?["Accept"] as? String, "text/event-stream")
        XCTAssertEqual(defaultSessionConfig.httpAdditionalHeaders?["Cache-Control"] as? String, "no-cache")
        // Configuration should return a fresh session configuration each retrieval
        XCTAssertTrue(defaultSessionConfig !== config.urlSessionConfiguration)
        // Updating idleTimeout should effect session config
        config.idleTimeout = 600.0
        XCTAssertEqual(config.urlSessionConfiguration.timeoutIntervalForRequest, 600.0)
        XCTAssertEqual(defaultSessionConfig.timeoutIntervalForRequest, 300.0)
        // Updating returned urlSessionConfiguration without setting should not update the Config until set
        let sessionConfig = config.urlSessionConfiguration
        sessionConfig.allowsCellularAccess = false
        XCTAssertTrue(config.urlSessionConfiguration.allowsCellularAccess)
        config.urlSessionConfiguration = sessionConfig
        XCTAssertFalse(config.urlSessionConfiguration.allowsCellularAccess)
        XCTAssertTrue(sessionConfig !== config.urlSessionConfiguration)
    }

    func testLastEventIdFromConfig() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!)
        var es = EventSource(config: config)
        XCTAssertEqual(es.getLastEventId(), "")
        config.lastEventId = "def"
        es = EventSource(config: config)
        XCTAssertEqual(es.getLastEventId(), "def")
    }

    func testCreatedSession() {
        let config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!)
        let session = EventSourceDelegate(config: config).createSession()
        XCTAssertEqual(session.configuration.timeoutIntervalForRequest, config.idleTimeout)
        XCTAssertEqual(session.configuration.httpAdditionalHeaders?["Accept"] as? String, "text/event-stream")
        XCTAssertEqual(session.configuration.httpAdditionalHeaders?["Cache-Control"] as? String, "no-cache")
    }

    func testCreateRequest() {
        // 192.0.2.1 is assigned as TEST-NET-1 reserved usage.
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://192.0.2.1")!)
        // Testing default configs
        var request = EventSourceDelegate(config: config).createRequest()
        XCTAssertEqual(request.url, config.url)
        XCTAssertEqual(request.httpMethod, config.method)
        XCTAssertEqual(request.httpBody, config.body)
        XCTAssertEqual(request.timeoutInterval, config.idleTimeout)
        XCTAssertEqual(request.allHTTPHeaderFields, config.headers)
        // Testing customized configs
        let testBody = "test data".data(using: .utf8)
        let testHeaders = ["removing": "a", "updating": "b"]
        let overrideHeaders = ["updating": "c", "last-event-id": "eventId2"]
        config.method = "REPORT"
        config.body = testBody
        config.lastEventId = "eventId"
        config.headers = testHeaders
        config.idleTimeout = 180.0
        config.headerTransform = { provided in
            XCTAssertEqual(provided, ["removing": "a", "updating": "b", "Last-Event-Id": "eventId"])
            return overrideHeaders
        }
        request = EventSourceDelegate(config: config).createRequest()
        XCTAssertEqual(request.url, config.url)
        XCTAssertEqual(request.httpMethod, config.method)
        XCTAssertEqual(request.httpBody, config.body)
        XCTAssertEqual(request.timeoutInterval, config.idleTimeout)
        XCTAssertEqual(request.allHTTPHeaderFields, overrideHeaders)
    }

    func testDispatchError() {
        var connectionErrorHandlerCallCount = 0
        var connectionErrorAction: ConnectionErrorAction = .proceed
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!)
        config.connectionErrorHandler = { _ in
            connectionErrorHandlerCallCount += 1
            return connectionErrorAction
        }
        let es = EventSourceDelegate(config: config)
        XCTAssertEqual(es.dispatchError(error: DummyError()), .proceed)
        XCTAssertEqual(connectionErrorHandlerCallCount, 1)
        guard case .error(let err) = mockHandler.events.expectEvent(), err is DummyError
        else {
            XCTFail("handler should receive error if EventSource is not shutting down")
            return
        }
        mockHandler.events.expectNoEvent()
        connectionErrorAction = .shutdown
        XCTAssertEqual(es.dispatchError(error: DummyError()), .shutdown)
        XCTAssertEqual(connectionErrorHandlerCallCount, 2)
    }

    func sessionWithMockProtocol() -> URLSessionConfiguration {
        let sessionConfig = URLSessionConfiguration.default
        sessionConfig.protocolClasses = [MockingProtocol.self] + (sessionConfig.protocolClasses ?? [])
        return sessionConfig
    }

#if !os(Linux) && !os(Windows)
    func testStartDefaultRequest() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        XCTAssertEqual(handler.request.url, config.url)
        XCTAssertEqual(handler.request.httpMethod, config.method)
        XCTAssertEqual(handler.request.httpBody, config.body)
        XCTAssertEqual(handler.request.timeoutInterval, config.idleTimeout)
        XCTAssertEqual(handler.request.allHTTPHeaderFields?["Accept"], "text/event-stream")
        XCTAssertEqual(handler.request.allHTTPHeaderFields?["Cache-Control"], "no-cache")
        XCTAssertNil(handler.request.allHTTPHeaderFields?["Last-Event-Id"])
        es.stop()
    }

    func testStartRequestWithConfiguration() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.method = "REPORT"
        config.body = Data("test body".utf8)
        config.idleTimeout = 500.0
        config.lastEventId = "abc"
        config.headers = ["X-LD-Header": "def"]
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        XCTAssertEqual(handler.request.url, config.url)
        XCTAssertEqual(handler.request.httpMethod, config.method)
        XCTAssertEqual(handler.request.bodyStreamAsData(), config.body)
        XCTAssertEqual(handler.request.timeoutInterval, config.idleTimeout)
        XCTAssertEqual(handler.request.allHTTPHeaderFields?["Accept"], "text/event-stream")
        XCTAssertEqual(handler.request.allHTTPHeaderFields?["Cache-Control"], "no-cache")
        XCTAssertEqual(handler.request.allHTTPHeaderFields?["Last-Event-Id"], config.lastEventId)
        XCTAssertEqual(handler.request.allHTTPHeaderFields?["X-LD-Header"], "def")
        es.stop()
    }

    func testStartRequestIsNotReentrant() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        let es = EventSource(config: config)
        es.start()
        es.start()
        _ = MockingProtocol.requested.expectEvent()
        MockingProtocol.requested.expectNoEvent()
        es.stop()
    }

    func testSuccessfulResponseOpens() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 200)
        XCTAssertEqual(mockHandler.events.expectEvent(), .opened)
        es.stop()
        XCTAssertEqual(mockHandler.events.expectEvent(), .closed)
    }

    func testLastEventIdUpdatedByEvents() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.reconnectTime = 0.1
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 200)
        XCTAssertEqual(mockHandler.events.expectEvent(), .opened)
        XCTAssertEqual(es.getLastEventId(), "")
        handler.respond(didLoad: "id: abc\n\n")
        // Comment used for synchronization
        handler.respond(didLoad: ":comment\n")
        XCTAssertEqual(mockHandler.events.expectEvent(), .comment("comment"))
        XCTAssertEqual(es.getLastEventId(), "abc")
        handler.finish()
        XCTAssertEqual(mockHandler.events.expectEvent(), .closed)
        // Expect to reconnect and include new event id
        let reconnectHandler = MockingProtocol.requested.expectEvent()
        XCTAssertEqual(reconnectHandler.request.allHTTPHeaderFields?["Last-Event-Id"], "abc")
        es.stop()
    }

    func testUsesRetryTime() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        // Long enough to cause a timeout if the retry time is not updated
        config.reconnectTime = 5
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 200)
        XCTAssertEqual(mockHandler.events.expectEvent(), .opened)
        handler.respond(didLoad: "retry: 100\n\n")
        handler.finish()
        XCTAssertEqual(mockHandler.events.expectEvent(), .closed)
        // Expect to reconnect before this times out
        _ = MockingProtocol.requested.expectEvent()
        es.stop()
    }

    func testCallsHandlerWithMessage() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 200)
        XCTAssertEqual(mockHandler.events.expectEvent(), .opened)
        handler.respond(didLoad: "event: custom\ndata: {}\n\n")
        XCTAssertEqual(mockHandler.events.expectEvent(), .message("custom", MessageEvent(data: "{}")))
        es.stop()
        XCTAssertEqual(mockHandler.events.expectEvent(), .closed)
    }

    func testRetryOnInvalidResponseCode() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.reconnectTime = 0.1
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 400)
        guard case let .error(err) = mockHandler.events.expectEvent(),
              let responseErr = err as? UnsuccessfulResponseError
        else {
            XCTFail("Expected UnsuccessfulResponseError to be given to handler")
            return
        }
        XCTAssertEqual(responseErr.responseCode, 400)
        // Expect the client to reconnect
        _ = MockingProtocol.requested.expectEvent()
        es.stop()
    }

    func testShutdownByErrorHandlerOnInitialErrorResponse() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.reconnectTime = 0.1
        config.connectionErrorHandler = { err in
            if let responseErr = err as? UnsuccessfulResponseError {
                XCTAssertEqual(responseErr.responseCode, 400)
            } else {
                XCTFail("Expected UnsuccessfulResponseError to be given to handler")
            }
            return .shutdown
        }
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 400)
        // Expect the client not to reconnect
        MockingProtocol.requested.expectNoEvent(within: 1.0)
        es.stop()
        // Error should not have been given to the handler
        mockHandler.events.expectNoEvent()
    }

    func testShutdownByErrorHandlerOnResponseCompletionError() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.reconnectTime = 0.1
        config.connectionErrorHandler = { _ in
            .shutdown
        }
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 200)
        XCTAssertEqual(mockHandler.events.expectEvent(), .opened)
        handler.finishWith(error: DummyError())
        XCTAssertEqual(mockHandler.events.expectEvent(), .closed)
        // Expect the client not to reconnect
        MockingProtocol.requested.expectNoEvent(within: 1.0)
        es.stop()
        // Error should not have been given to the handler
        mockHandler.events.expectNoEvent()
    }

    func testShutdownBy204Response() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.reconnectTime = 0.1

        let es = EventSource(config: config)
        es.start()

        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 204)

        MockingProtocol.requested.expectNoEvent(within: 1.0)

        es.stop()
        // Error should not have been given to the handler
        mockHandler.events.expectNoEvent()
    }

    func testCanOverride204DefaultBehavior() {
        var config = EventSource.Config(handler: mockHandler, url: URL(string: "http://example.com")!)
        config.urlSessionConfiguration = sessionWithMockProtocol()
        config.reconnectTime = 0.1
        config.connectionErrorHandler = { err in
            if let responseErr = err as? UnsuccessfulResponseError {
                XCTAssertEqual(responseErr.responseCode, 204)
            } else {
                XCTFail("Expected UnsuccessfulResponseError to be given to handler")
            }
            return .shutdown
        }
        let es = EventSource(config: config)
        es.start()
        let handler = MockingProtocol.requested.expectEvent()
        handler.respond(statusCode: 204)
        // Expect the client not to reconnect
        MockingProtocol.requested.expectNoEvent(within: 1.0)
        es.stop()
        // Error should not have been given to the handler
        mockHandler.events.expectNoEvent()
    }
#endif
}

private class DummyError: Error { }


================================================
FILE: Tests/MockHandler.swift
================================================
@testable import LDSwiftEventSource

enum ReceivedEvent: Equatable {
    case opened, closed, message(String, MessageEvent), comment(String), error(Error)

    static func == (lhs: ReceivedEvent, rhs: ReceivedEvent) -> Bool {
        switch (lhs, rhs) {
        case (.opened, .opened):
            return true
        case (.closed, .closed):
            return true
        case let (.message(typeLhs, eventLhs), .message(typeRhs, eventRhs)):
            return typeLhs == typeRhs && eventLhs == eventRhs
        case let (.comment(lhs), .comment(rhs)):
            return lhs == rhs
        case (.error, .error):
            return true
        default:
            return false
        }
    }
}

class MockHandler: EventHandler {
    var events = EventSink<ReceivedEvent>()

    func onOpened() { events.record(.opened) }
    func onClosed() { events.record(.closed) }
    func onMessage(eventType: String, messageEvent: MessageEvent) { events.record(.message(eventType, messageEvent)) }
    func onComment(comment: String) { events.record(.comment(comment)) }
    func onError(error: Error) { events.record(.error(error)) }
}


================================================
FILE: Tests/TestUtil.swift
================================================
import XCTest

#if os(Linux) || os(Windows)
import FoundationNetworking
#endif

struct EventSink<T> {
    private let semaphore = DispatchSemaphore(value: 0)
    private let queue = DispatchQueue(label: "EventSinkQueue." + UUID().uuidString)

    var receivedEvents: [T] = []

    mutating func record(_ event: T) {
        queue.sync { receivedEvents.append(event) }
        semaphore.signal()
    }

    mutating func expectEvent(maxWait: TimeInterval = 1.0) -> T {
        switch semaphore.wait(timeout: DispatchTime.now() + maxWait) {
        case .success:
            return queue.sync { receivedEvents.remove(at: 0) }
        case .timedOut:
            XCTFail("Expected mock handler to be called")
            return (nil as T?)!
        }
    }

    mutating func maybeEvent() -> T? {
        switch semaphore.wait(timeout: DispatchTime.now()) {
        case .success:
            return queue.sync { receivedEvents.remove(at: 0) }
        case .timedOut:
            return nil
        }
    }

    func expectNoEvent(within: TimeInterval = 0.1) {
        if case .success = semaphore.wait(timeout: DispatchTime.now() + within) {
            XCTFail("Expected no events in sink, found \(String(describing: receivedEvents.first))")
        }
    }
}

class RequestHandler {
    let proto: URLProtocol
    let request: URLRequest
    let client: URLProtocolClient?

    var stopped = false

    init(proto: URLProtocol, request: URLRequest, client: URLProtocolClient?) {
        self.proto = proto
        self.request = request
        self.client = client
    }

    func respond(statusCode: Int) {
        let headers = ["Content-Type": "text/event-stream; charset=utf-8", "Transfer-Encoding": "chunked"]
        let resp = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: headers)!
        client?.urlProtocol(proto, didReceive: resp, cacheStoragePolicy: .notAllowed)
    }

    func respond(didLoad: String) {
        respond(didLoad: Data(didLoad.utf8))
    }

    func respond(didLoad: Data) {
        client?.urlProtocol(proto, didLoad: didLoad)
    }

    func finishWith(error: Error) {
        client?.urlProtocol(proto, didFailWithError: error)
    }

    func finish() {
        client?.urlProtocolDidFinishLoading(proto)
    }

    func stop() {
        stopped = true
    }
}

class MockingProtocol: URLProtocol {
    override class func canonicalRequest(for request: URLRequest) -> URLRequest { request }
    override class func canInit(with request: URLRequest) -> Bool { true }
    override class func canInit(with task: URLSessionTask) -> Bool { true }

    static var requested = EventSink<RequestHandler>()

    class func resetRequested() {
        requested = EventSink<RequestHandler>()
    }

    private var currentlyLoading: RequestHandler?

    override func startLoading() {
        let handler = RequestHandler(proto: self, request: request, client: client)
        currentlyLoading = handler
        MockingProtocol.requested.record(handler)
    }

    override func stopLoading() {
        currentlyLoading?.stop()
        currentlyLoading = nil
    }
}

extension URLRequest {
    func bodyStreamAsData() -> Data? {
        guard let bodyStream = self.httpBodyStream
        else { return nil }

        bodyStream.open()
        defer { bodyStream.close() }

        let bufSize: Int = 16
        let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: bufSize)
        defer { buf.deallocate() }

        var data = Data()
        while bodyStream.hasBytesAvailable {
            let readDat = bodyStream.read(buf, maxLength: bufSize)
            data.append(buf, count: readDat)
        }
        return data
    }
}


================================================
FILE: Tests/UTF8LineParserTests.swift
================================================
import XCTest
@testable import LDSwiftEventSource

final class UTF8LineParserTests: XCTestCase {
    var parser = UTF8LineParser()

    override func setUp() {
        super.setUp()
        parser = UTF8LineParser()
    }

    override func tearDown() {
        super.tearDown()
        // Validate that `closeAndReset` completely resets the parser
        parser.closeAndReset()
        XCTAssertEqual(parser.append(Data("\n".utf8)), [""])
    }

    // swiftlint:disable:next empty_xctest_method - Only runs test in tearDown
    func testNoData() { }

    func testEmptyData() {
        XCTAssertEqual(parser.append(Data()), [])
    }

    func testEmptyCrLine() {
        XCTAssertEqual(parser.append(Data("\r".utf8)), [""])
    }

    func testBasicLineUnterminated() {
        let line = "test string"
        XCTAssertEqual(parser.append(Data(line.utf8)), [])
    }

    func testBasicLineCr() {
        let line = "test string"
        let data = Data((line + "\r").utf8)
        XCTAssertEqual(parser.append(data), [line])
    }

    func testBasicLineLf() {
        let line = "test string"
        let data = Data((line + "\n").utf8)
        XCTAssertEqual(parser.append(data), [line])
    }

    func testBasicLineCrLf() {
        let line = "test string"
        let data = Data((line + "\r\n").utf8)
        XCTAssertEqual(parser.append(data), [line])
    }

    func testBasicSplit() {
        XCTAssertEqual(parser.append(Data("test ".utf8)), [])
        XCTAssertEqual(parser.append(Data("string\r".utf8)), ["test string"])
    }

    func testUnicodeString() {
        let line = "¯\\_(ツ)_/¯0️⃣🇺🇸Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮𝓯𝓸𝔁"
        XCTAssertEqual(parser.append(Data((line + "\n").utf8)), [line])
    }

    func testNullCodePoint() {
        let line = "\u{0000}"
        XCTAssertEqual(parser.append(Data((line + "\n").utf8)), [line])
    }

    func testInvalidCharacterReplaced() {
        let line = "test✨string"
        var data = Data((line + "\n").utf8)
        // Remove 3rd and last byte of "✨"
        data.remove(at: 6)
        let expected = "test�string"
        XCTAssertEqual(parser.append(data), [expected])
    }

    // Simulates a multi-code-unit code point being split across received chunks from the network.
    func testCodePointSplitNotReplaced() {
        let line = "test✨string"
        let data = Data((line + "\r").utf8)
        let data1 = data.subdata(in: 0..<6)
        let data2 = data.subdata(in: 6..<14)
        XCTAssertEqual(parser.append(data1), [])
        XCTAssertEqual(parser.append(data2), [line])
    }

    // Simulates the stream dropping part way through a multi-code-unit code point.
    func testResetAfterPartialInvalid() {
        var data = Data("test✨".utf8)
        data.remove(at: 6)
        XCTAssertEqual(parser.append(data), [])
    }

    func testInvalidCharacterReplacedOnNextLineAfterCr() {
        let line = "test\r✨string\r"
        var data = Data(line.utf8)
        // Remove 3rd and last byte of "✨"
        data.remove(at: 7)
        XCTAssertEqual(parser.append(data), ["test", "�string"])
    }

    func testMultiLineDataMixedLineEnding() {
        let line = "test1\rtest2\ntest3\r\ntest4\r\rtest5\n\n"
        let data = Data(line.utf8)
        let expected = ["test1", "test2", "test3", "test4", "", "test5", ""]
        XCTAssertEqual(parser.append(data), expected)
    }
}


================================================
FILE: release-please-config.json
================================================
{
  "packages": {
    ".": {
      "release-type": "simple",
      "bump-minor-pre-major": true,
      "versioning": "default",
      "include-v-in-tag": false,
      "include-component-in-tag": false,
      "extra-files": [
        "LDSwiftEventSource.podspec",
        "README.md"
      ]
    }
  }
}
Download .txt
gitextract_1la2044u/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── actions/
│   │   ├── build-docs/
│   │   │   └── action.yml
│   │   ├── build-ios/
│   │   │   └── action.yml
│   │   ├── build-macos/
│   │   │   └── action.yml
│   │   ├── build-tvos/
│   │   │   └── action.yml
│   │   ├── build-watchos/
│   │   │   └── action.yml
│   │   ├── contract-tests/
│   │   │   └── action.yml
│   │   ├── lint/
│   │   │   └── action.yml
│   │   ├── publish/
│   │   │   └── action.yml
│   │   ├── publish-docs/
│   │   │   └── action.yml
│   │   ├── test-swiftpm/
│   │   │   └── action.yml
│   │   └── update-versions/
│   │       └── action.yml
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       ├── lint-pr-title.yml
│       ├── manual-publish-docs.yml
│       ├── manual-publish.yml
│       ├── release-please.yml
│       └── stale.yml
├── .gitignore
├── .jazzy.yaml
├── .release-please-manifest.json
├── .swiftlint.yml
├── CHANGELOG.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── ContractTestService/
│   ├── .gitignore
│   ├── Package.resolved
│   ├── Package.swift
│   ├── README.md
│   └── Sources/
│       └── ContractTestService/
│           └── main.swift
├── LDSwiftEventSource.podspec
├── LDSwiftEventSource.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           └── LDSwiftEventSource.xcscheme
├── LICENSE.txt
├── Makefile
├── Package.swift
├── README.md
├── SECURITY.md
├── Source/
│   ├── .swiftlint.yml
│   ├── EventParser.swift
│   ├── Info.plist
│   ├── LDSwiftEventSource.h
│   ├── LDSwiftEventSource.swift
│   ├── Logs.swift
│   ├── Types.swift
│   └── UTF8LineParser.swift
├── Tests/
│   ├── .swiftlint.yml
│   ├── EventParserTests.swift
│   ├── LDSwiftEventSourceTests.swift
│   ├── MockHandler.swift
│   ├── TestUtil.swift
│   └── UTF8LineParserTests.swift
└── release-please-config.json
Condensed preview — 56 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (150K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 646,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 606,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": ".github/actions/build-docs/action.yml",
    "chars": 413,
    "preview": "name: Build Documentation\ndescription: 'Build Documentation.'\n\nruns:\n  using: composite\n  steps:\n    - name: Install jaz"
  },
  {
    "path": ".github/actions/build-ios/action.yml",
    "chars": 927,
    "preview": "name: Build & Test iOS\ndescription: 'Build for iOS device and run tests on iOS Simulator.'\ninputs:\n  ios-sim:\n    descri"
  },
  {
    "path": ".github/actions/build-macos/action.yml",
    "chars": 408,
    "preview": "name: Build & Test macOS\ndescription: 'Build and test for macOS.'\n\nruns:\n  using: composite\n  steps:\n    - name: Build &"
  },
  {
    "path": ".github/actions/build-tvos/action.yml",
    "chars": 841,
    "preview": "name: Build & Test tvOS\ndescription: 'Build for tvOS device and run tests on tvOS Simulator.'\n\nruns:\n  using: composite\n"
  },
  {
    "path": ".github/actions/build-watchos/action.yml",
    "chars": 731,
    "preview": "name: Build watchOS\ndescription: 'Build for watchOS device and simulator.'\n\nruns:\n  using: composite\n  steps:\n    # Work"
  },
  {
    "path": ".github/actions/contract-tests/action.yml",
    "chars": 851,
    "preview": "name: Contract Tests\ndescription: 'Build and run SDK contract tests.'\ninputs:\n  token:\n    description: 'GH token used t"
  },
  {
    "path": ".github/actions/lint/action.yml",
    "chars": 542,
    "preview": "name: Lint\ndescription: 'Run podspec and swiftlint checks.'\n\nruns:\n  using: composite\n  steps:\n    - name: Install swift"
  },
  {
    "path": ".github/actions/publish/action.yml",
    "chars": 388,
    "preview": "name: Publish Package\ndescription: 'Publish the package to Cocoapods'\ninputs:\n  dry_run:\n    description: 'Is this a dry"
  },
  {
    "path": ".github/actions/publish-docs/action.yml",
    "chars": 399,
    "preview": "name: Publish Documentation\ndescription: 'Publish the documentation to GitHub pages'\ninputs:\n  token:\n    description: '"
  },
  {
    "path": ".github/actions/test-swiftpm/action.yml",
    "chars": 210,
    "preview": "name: Test SwiftPM\ndescription: 'Build and test using Swift Package Manager.'\n\nruns:\n  using: composite\n  steps:\n    - n"
  },
  {
    "path": ".github/actions/update-versions/action.yml",
    "chars": 2570,
    "preview": "name: Update xcode project version numbers\ndescription: 'Update xcode project version numbers'\ninputs:\n  branch:\n    des"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 737,
    "preview": "**Requirements**\n\n- [ ] I have added test coverage for new or changed functionality\n- [ ] I have followed the repository"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 3068,
    "preview": "name: Run CI\non:\n  push:\n    branches: [ main ]\n    paths-ignore:\n      - '**.md' # Do not need to run CI for markdown c"
  },
  {
    "path": ".github/workflows/lint-pr-title.yml",
    "chars": 208,
    "preview": "name: Lint PR title\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n\njobs:\n  li"
  },
  {
    "path": ".github/workflows/manual-publish-docs.yml",
    "chars": 1017,
    "preview": "on:\n  workflow_dispatch:\n\nname: Publish Documentation\njobs:\n  build-publish:\n    runs-on: macos-15\n\n    permissions:\n   "
  },
  {
    "path": ".github/workflows/manual-publish.yml",
    "chars": 1351,
    "preview": "name: Publish Package\non:\n  workflow_dispatch:\n    inputs:\n      dry_run:\n        description: 'Is this a dry run. If so"
  },
  {
    "path": ".github/workflows/release-please.yml",
    "chars": 2991,
    "preview": "name: Run Release Please\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  release-package:\n    runs-on: macos-15\n\n    pe"
  },
  {
    "path": ".github/workflows/stale.yml",
    "chars": 284,
    "preview": "name: \"Close stale issues and PRs\"\non:\n  workflow_dispatch:\n  schedule:\n    # Happen once per day at 1:30 AM\n    - cron:"
  },
  {
    "path": ".gitignore",
    "chars": 81,
    "preview": "*~\n\\#*\n.\\#*\n.DS_Store\n/.build\nxcuserdata/\nIDEWorkspaceChecks.plist\n.swiftpm\n/docs"
  },
  {
    "path": ".jazzy.yaml",
    "chars": 314,
    "preview": "module: LDSwiftEventSource\nauthor: LaunchDarkly\nauthor_url: https://launchdarkly.com\ngithub_url: https://github.com/laun"
  },
  {
    "path": ".release-please-manifest.json",
    "chars": 19,
    "preview": "{\n  \".\": \"3.3.0\"\n}\n"
  },
  {
    "path": ".swiftlint.yml",
    "chars": 2241,
    "preview": "# See sub-configurations at `Source/.swiftlint.yml` and `Tests/.swiftlint.yml`.\n\ndisabled_rules:\n  - identifier_name\n  -"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 6568,
    "preview": "# Change log\n\nAll notable changes to the LaunchDarkly Swift EventSource library will be documented in this file. This pr"
  },
  {
    "path": "CODEOWNERS",
    "chars": 56,
    "preview": "# Repository Maintainers\n* @launchdarkly/team-sdk-swift\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2123,
    "preview": "Contributing to the LDSwiftEventSource library\n================================================\n\nSubmitting bug reports "
  },
  {
    "path": "ContractTestService/.gitignore",
    "chars": 126,
    "preview": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspac"
  },
  {
    "path": "ContractTestService/Package.resolved",
    "chars": 2511,
    "preview": "{\n  \"object\": {\n    \"pins\": [\n      {\n        \"package\": \"Socket\",\n        \"repositoryURL\": \"https://github.com/Kitura/B"
  },
  {
    "path": "ContractTestService/Package.swift",
    "chars": 636,
    "preview": "// swift-tools-version:5.0\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"ContractTestService\",\n  platforms"
  },
  {
    "path": "ContractTestService/README.md",
    "chars": 589,
    "preview": "# SSE client contract test service\n\nThis directory contains an implementation of the cross-platform SSE testing protocol"
  },
  {
    "path": "ContractTestService/Sources/ContractTestService/main.swift",
    "chars": 3965,
    "preview": "import Dispatch\nimport Foundation\nimport Kitura\nimport LDSwiftEventSource\n\nstruct StatusResp: Encodable {\n    let name ="
  },
  {
    "path": "LDSwiftEventSource.podspec",
    "chars": 733,
    "preview": "Pod::Spec.new do |s|\n  s.name         = \"LDSwiftEventSource\"\n  s.version      = \"3.3.0\" # x-release-please-version\n  s.s"
  },
  {
    "path": "LDSwiftEventSource.xcodeproj/project.pbxproj",
    "chars": 24497,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "LDSwiftEventSource.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 163,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:LDSwiftEventSou"
  },
  {
    "path": "LDSwiftEventSource.xcodeproj/xcshareddata/xcschemes/LDSwiftEventSource.xcscheme",
    "chars": 3387,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1140\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "LICENSE.txt",
    "chars": 557,
    "preview": "Copyright 2020 Catamorphic, Co.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this fi"
  },
  {
    "path": "Makefile",
    "chars": 925,
    "preview": "build:\n\tswift build\n\nclean:\n\tswift clean\n\ntest:\n\tswift test\n\nTEMP_TEST_OUTPUT=/tmp/sse-contract-test-service.log\n\nbuild-"
  },
  {
    "path": "Package.swift",
    "chars": 632,
    "preview": "// swift-tools-version:5.0\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"LDSwiftEventSource\",\n    platfo"
  },
  {
    "path": "README.md",
    "chars": 4749,
    "preview": "# LDSwiftEventSource\n\n[![Run CI](https://github.com/launchdarkly/swift-eventsource/actions/workflows/ci.yml/badge.svg)]("
  },
  {
    "path": "SECURITY.md",
    "chars": 554,
    "preview": "# Reporting and Fixing Security Issues\n\nPlease report all security issues to the LaunchDarkly security team by submittin"
  },
  {
    "path": "Source/.swiftlint.yml",
    "chars": 86,
    "preview": "disabled_rules:\n\nopt_in_rules:\n  - force_unwrapping\n  - implicitly_unwrapped_optional\n"
  },
  {
    "path": "Source/EventParser.swift",
    "chars": 3218,
    "preview": "import Foundation\n\nclass EventParser {\n    private struct Constants {\n        static let dataLabel: Substring = \"data\"\n "
  },
  {
    "path": "Source/Info.plist",
    "chars": 769,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Source/LDSwiftEventSource.h",
    "chars": 152,
    "preview": "@import Foundation;\n\nFOUNDATION_EXPORT double LDSwiftEventSourceVersionNumber;\nFOUNDATION_EXPORT const unsigned char LDS"
  },
  {
    "path": "Source/LDSwiftEventSource.swift",
    "chars": 13510,
    "preview": "import Foundation\n\n#if os(Linux) || os(Windows)\nimport FoundationNetworking\n#endif\n\n#if canImport(os)\n// os_log is not s"
  },
  {
    "path": "Source/Logs.swift",
    "chars": 1603,
    "preview": "import Foundation\n\n#if canImport(os)\nimport os.log\n#endif\n\nprotocol InternalLogging {\n    func log(_ level: Level, _ sta"
  },
  {
    "path": "Source/Types.swift",
    "chars": 4017,
    "preview": "import Foundation\n\n/**\n Type for a function that will be notified when the `EventSource` client encounters a connection "
  },
  {
    "path": "Source/UTF8LineParser.swift",
    "chars": 2197,
    "preview": "import Foundation\n\nstruct DataIter: IteratorProtocol {\n    var data: Data\n    var position: Data.Index { data.startIndex"
  },
  {
    "path": "Tests/.swiftlint.yml",
    "chars": 373,
    "preview": "disabled_rules:\n\nopt_in_rules:\n  # Must specify even though enabled by default to update configuration of rule below.\n  "
  },
  {
    "path": "Tests/EventParserTests.swift",
    "chars": 11889,
    "preview": "import XCTest\n@testable import LDSwiftEventSource\n\nfinal class EventParserTests: XCTestCase {\n    var handler: MockHandl"
  },
  {
    "path": "Tests/LDSwiftEventSourceTests.swift",
    "chars": 18082,
    "preview": "import XCTest\n@testable import LDSwiftEventSource\n\n#if os(Linux) || os(Windows)\nimport FoundationNetworking\n#endif\n\nfina"
  },
  {
    "path": "Tests/MockHandler.swift",
    "chars": 1133,
    "preview": "@testable import LDSwiftEventSource\n\nenum ReceivedEvent: Equatable {\n    case opened, closed, message(String, MessageEve"
  },
  {
    "path": "Tests/TestUtil.swift",
    "chars": 3705,
    "preview": "import XCTest\n\n#if os(Linux) || os(Windows)\nimport FoundationNetworking\n#endif\n\nstruct EventSink<T> {\n    private let se"
  },
  {
    "path": "Tests/UTF8LineParserTests.swift",
    "chars": 3371,
    "preview": "import XCTest\n@testable import LDSwiftEventSource\n\nfinal class UTF8LineParserTests: XCTestCase {\n    var parser = UTF8Li"
  },
  {
    "path": "release-please-config.json",
    "chars": 303,
    "preview": "{\n  \"packages\": {\n    \".\": {\n      \"release-type\": \"simple\",\n      \"bump-minor-pre-major\": true,\n      \"versioning\": \"de"
  }
]

About this extraction

This page contains the full source code of the launchdarkly/swift-eventsource GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 56 files (135.8 KB), approximately 36.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!