├── .bazelignore ├── .bazelrc ├── .bazelversion ├── .github └── workflows │ ├── tests.yml │ └── xcode_select.sh ├── .gitignore ├── BUILD.bazel ├── LICENSE ├── MODULE.bazel ├── README.md ├── WORKSPACE ├── app ├── Assets │ └── Assets.xcassets │ │ ├── AccentColor.colorset │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ └── Contents.json │ │ └── Contents.json ├── BUILD.bazel ├── Info.plist ├── PreviewContent │ └── PreviewAssets.xcassets │ │ └── Contents.json ├── ios app.entitlements └── source │ ├── ContentView.swift │ └── MainApp.swift ├── modules ├── API │ ├── API │ │ └── API.swift │ ├── BUILD.bazel │ └── Tests │ │ └── APITests.swift ├── BUILD.bazel └── Models │ ├── BUILD.bazel │ ├── Models │ └── Models.swift │ └── Tests │ └── ModelsTests.swift └── tools ├── BUILD.bazel ├── extensions.bzl ├── repositories.bzl └── shared.bzl /.bazelignore: -------------------------------------------------------------------------------- 1 | .git 2 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | # Enable Bzlmod for every Bazel command 2 | common --enable_bzlmod 3 | 4 | ## BUILD 5 | 6 | build --macos_minimum_os=13 7 | build --host_macos_minimum_os=13 8 | 9 | # https://bazel.build/reference/command-line-reference#flag--incompatible_strict_action_env 10 | build --incompatible_strict_action_env 11 | 12 | # Disable the worker, which has sandboxing disabled by default, which can hide 13 | # issues with non-hermetic bugs. 14 | build --spawn_strategy=sandboxed,local 15 | build --worker_sandboxing=true 16 | 17 | build --incompatible_disallow_empty_glob 18 | 19 | build --features=swift.use_global_module_cache 20 | 21 | # Enable indexing while building. 22 | build --features swift.use_global_index_store 23 | build --features swift.index_while_building 24 | 25 | # Since there's no way to set the deployment version for swift_{binary,test}, 26 | # this forces all targets' minimum macOS to Github Actions macOS version. 27 | build --macos_minimum_os=12.6 28 | 29 | ## TEST 30 | 31 | # `bazel test` tries to build everything also by default, so skip that so the 32 | # *_library targets in examples/... aren't built (and fail since they are 33 | # platform specific). 34 | test --build_tests_only 35 | 36 | # Show detailed errors for test failures 37 | test --test_output=errors 38 | 39 | # Use llvm-cov instead of gcov (default). 40 | coverage --experimental_use_llvm_covmap 41 | 42 | ## REMOTE CACHE 43 | 44 | # Do actions locally when it makes sense. 45 | build --modify_execution_info=^(BitcodeSymbolsCopy|BundleApp|BundleTreeApp|DsymDwarf|DsymLipo|GenerateAppleSymbolsFile|ObjcBinarySymbolStrip|CppLink|ObjcLink|ProcessAndSign|SignBinary|SwiftArchive|SwiftStdlibCopy)$=+no-remote,^(BundleResources|ImportedDynamicFrameworkProcessor)$=+no-remote-exec 46 | 47 | build:remote_cache --bes_results_url=https://app.buildbuddy.io/invocation/ 48 | build:remote_cache --bes_backend=grpcs://remote.buildbuddy.io 49 | build:remote_cache --remote_cache=grpcs://remote.buildbuddy.io 50 | # https://bazel.build/reference/command-line-reference#flag--remote_download_toplevel 51 | build:remote_cache --remote_download_toplevel 52 | # https://bazel.build/reference/command-line-reference#flag--remote_timeout 53 | build:remote_cache --remote_timeout=3600 54 | # https://bazel.build/reference/command-line-reference#flag--experimental_remote_cache_compression 55 | build:remote_cache --experimental_remote_cache_compression 56 | # https://bazel.build/reference/command-line-reference#flag--experimental_remote_cache_compression_threshold 57 | build:remote_cache --experimental_remote_cache_compression_threshold=100 58 | # https://bazel.build/reference/command-line-reference#flag--legacy_important_outputs 59 | build:remote_cache --nolegacy_important_outputs 60 | 61 | # Only wait for BES upload in CI builds but not dev builds. 62 | # 63 | # https://bazel.build/reference/command-line-reference#flag--bes_upload_mode 64 | build:remote_cache --bes_upload_mode=nowait_for_upload_complete 65 | 66 | # By default don't upload local results to remote cache, only CI does this. 67 | build --noremote_upload_local_results 68 | build:ci --remote_upload_local_results 69 | 70 | ## LOCAL CONFIG 71 | 72 | # Load a user.bazelrc 73 | try-import %workspace%/user.bazelrc 74 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 7.6.0 2 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | # One active job per PR, cancel older ones on push 10 | concurrency: 11 | group: ${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | tests: 16 | name: Build and Test 17 | runs-on: macos-15 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set Up CI 21 | run: .github/workflows/xcode_select.sh 22 | env: 23 | BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} 24 | - name: Lint 25 | run: bazelisk run :lint && git diff --exit-code 26 | - name: Build and Test 27 | run: | 28 | # Build the app 29 | bazelisk build -- //app 30 | 31 | # Run the tests 32 | bazelisk test --local_test_jobs=1 -- //... 33 | - name: Generate Xcode Project 34 | run: | 35 | # Generate Xcode project 36 | bazelisk run -- //:xcodeproj 37 | - uses: actions/upload-artifact@v4 38 | if: failure() 39 | with: 40 | name: bazel-testlogs 41 | path: bazel-testlogs 42 | -------------------------------------------------------------------------------- /.github/workflows/xcode_select.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | echo "Selecting Xcode for environment" 4 | 5 | printenv 6 | 7 | sudo xcode-select -p 8 | sudo xcode-select -s /Applications/Xcode_16.2.app 9 | 10 | echo "Printing available simulators" 11 | 12 | xcrun simctl list devices 13 | 14 | echo "Generating bazelrc" 15 | 16 | # Enable remote cache for all Github Action builds 17 | echo "build --config=remote_cache" > user.bazelrc 18 | echo "build --config=ci" >> user.bazelrc 19 | echo "build --remote_header=x-buildbuddy-api-key=$BUILDBUDDY_API_KEY" >> user.bazelrc 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Bazel 2 | 3 | /bazel-* 4 | /user.bazelrc 5 | /MODULE.bazel.lock 6 | 7 | # macOS 8 | 9 | .DS_Store 10 | 11 | # Xcode 12 | # 13 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 14 | 15 | /*.xcodeproj 16 | 17 | ## User settings 18 | xcuserdata/ 19 | 20 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 21 | *.xcscmblueprint 22 | *.xccheckout 23 | 24 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 25 | build/ 26 | DerivedData/ 27 | *.moved-aside 28 | *.pbxuser 29 | !default.pbxuser 30 | *.mode1v3 31 | !default.mode1v3 32 | *.mode2v3 33 | !default.mode2v3 34 | *.perspectivev3 35 | !default.perspectivev3 36 | 37 | ## Obj-C/Swift specific 38 | *.hmap 39 | 40 | ## App packaging 41 | *.ipa 42 | *.dSYM.zip 43 | *.dSYM 44 | 45 | ## Playgrounds 46 | timeline.xctimeline 47 | playground.xcworkspace 48 | 49 | # Swift Package Manager 50 | # 51 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 52 | # Packages/ 53 | # Package.pins 54 | # Package.resolved 55 | # *.xcodeproj 56 | # 57 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 58 | # hence it is not needed unless you have added a package configuration file to your project 59 | # .swiftpm 60 | 61 | .build/ 62 | 63 | # CocoaPods 64 | # 65 | # We recommend against adding the Pods directory to your .gitignore. However 66 | # you should judge for yourself, the pros and cons are mentioned at: 67 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 68 | # 69 | # Pods/ 70 | # 71 | # Add this line if you want to avoid checking in source code from the Xcode workspace 72 | # *.xcworkspace 73 | 74 | # Carthage 75 | # 76 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 77 | # Carthage/Checkouts 78 | 79 | Carthage/Build/ 80 | 81 | # Accio dependency management 82 | Dependencies/ 83 | .accio/ 84 | 85 | # fastlane 86 | # 87 | # It is recommended to not store the screenshots in the git repo. 88 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 89 | # For more information about the recommended setup visit: 90 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 91 | 92 | fastlane/report.xml 93 | fastlane/Preview.html 94 | fastlane/screenshots/**/*.png 95 | fastlane/test_output 96 | 97 | # Code Injection 98 | # 99 | # After new code Injection tools there's a generated folder /iOSInjectionProject 100 | # https://github.com/johnno1962/injectionforxcode 101 | 102 | iOSInjectionProject/ 103 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "@rules_xcodeproj//xcodeproj:defs.bzl", 3 | "top_level_target", 4 | "xcodeproj", 5 | ) 6 | 7 | # Xcode 8 | 9 | xcodeproj( 10 | name = "xcodeproj", 11 | project_name = "App", 12 | top_level_targets = [ 13 | top_level_target( 14 | "//app", 15 | target_environments = ["simulator"], 16 | ), 17 | top_level_target( 18 | "//modules/API:APITests", 19 | target_environments = ["simulator"], 20 | ), 21 | top_level_target( 22 | "//modules/Models:ModelsTests", 23 | target_environments = ["simulator"], 24 | ), 25 | ], 26 | ) 27 | 28 | # tools 29 | 30 | genrule( 31 | name = "lint", 32 | srcs = [], 33 | outs = ["lint.sh"], 34 | cmd = """ 35 | echo "set -e" > "$@" 36 | echo "./$(location @buildifier_prebuilt//:buildifier) -lint fix -mode fix -r \\$$BUILD_WORKSPACE_DIRECTORY" >> "$@" 37 | echo "./$(location @SwiftLint//:swiftlint) --fix \\$$BUILD_WORKSPACE_DIRECTORY" >> "$@" 38 | """, 39 | executable = True, 40 | tools = [ 41 | "@SwiftLint//:swiftlint", 42 | "@buildifier_prebuilt//:buildifier", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Matt Robinson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | bazel_dep(name = "bazel_skylib", version = "1.7.1") 2 | bazel_dep(name = "rules_xcodeproj", version = "2.10.0") 3 | bazel_dep( 4 | name = "apple_support", 5 | version = "1.21.1", 6 | repo_name = "build_bazel_apple_support", 7 | ) 8 | bazel_dep( 9 | name = "rules_swift", 10 | version = "2.8.1", 11 | repo_name = "build_bazel_rules_swift", 12 | ) 13 | bazel_dep( 14 | name = "rules_apple", 15 | version = "3.20.1", 16 | repo_name = "build_bazel_rules_apple", 17 | ) 18 | 19 | bazel_dep( 20 | name = "buildifier_prebuilt", 21 | version = "6.4.0", 22 | dev_dependency = True, 23 | ) 24 | 25 | non_module_dependencies = use_extension("//tools:extensions.bzl", "non_module_dependencies") 26 | use_repo(non_module_dependencies, "SwiftLint") 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI iOS App with Bazel 2 | 3 | This is an iOS application written in SwiftUI and built via Bazel. This is a starting place similar to creating a new project in Xcode and choosing SwiftUI as the starting place. 4 | 5 | ## Getting Started 6 | 7 | Install Bazelisk via `brew install bazelisk`. `bazel` & `bazelisk` will now use the `.bazelversion` file to download and run the chosen Bazel version. 8 | 9 | ### Generate/Open Project 10 | 11 | ```bash 12 | $ bazel run :xcodeproj 13 | $ open App.xcodeproj 14 | ``` 15 | 16 | ### Build Application (CLI) 17 | 18 | ```bash 19 | $ bazel build //app 20 | ``` 21 | 22 | ### Run All Tests (CLI) 23 | 24 | ```bash 25 | $ bazel test $(bazel query 'kind(ios_unit_test,//...)') 26 | ``` 27 | 28 | If the tests fail, run `xcrun simctl list devices` to check what devices and OS versions are locally available. iOS version is set in [`shared.bzl`](/tools/shared.bzl). 29 | 30 | ## Underlying Tools 31 | 32 | - [`rules_apple`](https://github.com/bazelbuild/rules_apple) 33 | - [`rules_swift`](https://github.com/bazelbuild/rules_swift) 34 | - [`rules_xcodeproj`](https://github.com/buildbuddy-io/rules_xcodeproj) 35 | 36 | ## Making It Your Own 37 | 38 | `tools/shared.bzl` contains a handful of definitions to define the name of the application, bundle identifier, and similar things. Update these values to change the application's name. 39 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "bazel_ios_swiftui_template") 2 | -------------------------------------------------------------------------------- /app/Assets/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/Assets/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /app/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application") 2 | load("@build_bazel_rules_apple//apple:versioning.bzl", "apple_bundle_version") 3 | load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") 4 | load("//tools:shared.bzl", "app_info", "versions") 5 | 6 | # Version 7 | 8 | apple_bundle_version( 9 | name = "Version", 10 | build_version = "0.0.1", 11 | short_version_string = "1.0", 12 | ) 13 | 14 | # Code 15 | 16 | swift_library( 17 | name = "app.library", 18 | srcs = glob(["source/**/*.swift"]), 19 | data = [":PreviewContent"], 20 | module_name = "app", 21 | deps = [ 22 | "//modules/API:APILib", 23 | ], 24 | ) 25 | 26 | # Packaging 27 | 28 | filegroup( 29 | name = "PreviewContent", 30 | srcs = glob(["PreviewContent/**"]), 31 | ) 32 | 33 | ios_application( 34 | name = "app", 35 | app_icons = glob(["Assets/Assets.xcassets/AppIcon.appiconset/**"]), 36 | bundle_id = app_info.bundle_id, 37 | bundle_name = app_info.bundle_name, 38 | entitlements = "ios app.entitlements", 39 | executable_name = app_info.executable_name, 40 | families = [ 41 | "iphone", 42 | "ipad", 43 | ], 44 | infoplists = [":Info.plist"], 45 | minimum_os_version = versions.minimum_ios_version, 46 | resources = glob( 47 | [ 48 | "Assets/Assets.xcassets/**", 49 | ], 50 | exclude = ["Assets/Assets.xcassets/AppIcon.appiconset/**"], 51 | ), 52 | # Add localizable assets here. 53 | # strings = glob(["*.lproj/Localizable.strings"]), 54 | version = ":Version", 55 | visibility = [ 56 | "//:__subpackages__", 57 | "@rules_xcodeproj//xcodeproj:generated", 58 | ], 59 | deps = [ 60 | ":app.library", 61 | ], 62 | ) 63 | -------------------------------------------------------------------------------- /app/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UILaunchScreen 6 | 7 | CFBundleName 8 | $(PRODUCT_NAME) 9 | CFBundleVersion 10 | 1.0 11 | CFBundleShortVersionString 12 | 1.0 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/PreviewContent/PreviewAssets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /app/ios app.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/source/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | var body: some View { 5 | VStack { 6 | Image(systemName: "globe") 7 | .imageScale(.large) 8 | .foregroundColor(.accentColor) 9 | Text("iOS application in SwiftUI!") 10 | } 11 | .padding() 12 | } 13 | } 14 | 15 | struct ContentView_Previews: PreviewProvider { 16 | static var previews: some View { 17 | ContentView() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/source/MainApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct MainApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/API/API/API.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Models 3 | 4 | public struct API { 5 | public static func returnTrue() -> Bool { 6 | _ = ModelA() 7 | _ = ModelB() 8 | return true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/API/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") 2 | load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") 3 | load("//tools:shared.bzl", "versions") 4 | 5 | # Code 6 | 7 | swift_library( 8 | name = "APILib", 9 | srcs = [ 10 | "API/API.swift", 11 | ], 12 | module_name = "API", 13 | visibility = ["//modules:default_library_visibility"], 14 | deps = [ 15 | "//modules/Models:ModelsLib", 16 | ], 17 | ) 18 | 19 | # Tests 20 | 21 | swift_library( 22 | name = "APITestsLib", 23 | testonly = True, 24 | srcs = [ 25 | "Tests/APITests.swift", 26 | ], 27 | module_name = "APITests", 28 | deps = [ 29 | ":APILib", 30 | ], 31 | ) 32 | 33 | ios_unit_test( 34 | name = "APITests", 35 | minimum_os_version = versions.minimum_ios_version, 36 | runner = "//tools:default_test_runner", 37 | visibility = ["//modules:default_test_visibility"], 38 | deps = [":APITestsLib"], 39 | ) 40 | -------------------------------------------------------------------------------- /modules/API/Tests/APITests.swift: -------------------------------------------------------------------------------- 1 | import API 2 | import XCTest 3 | 4 | final class APITests: XCTestCase { 5 | func test_success() { 6 | XCTAssertTrue(API.returnTrue()) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /modules/BUILD.bazel: -------------------------------------------------------------------------------- 1 | package_group( 2 | name = "default_library_visibility", 3 | includes = [ 4 | # Project generation. 5 | "@rules_xcodeproj//xcodeproj:generated", 6 | ], 7 | packages = [ 8 | # The application package. 9 | "//app", 10 | # All other modules. 11 | "//modules/...", 12 | ], 13 | ) 14 | 15 | package_group( 16 | name = "default_test_visibility", 17 | includes = [ 18 | # Project generation. 19 | "@rules_xcodeproj//xcodeproj:generated", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /modules/Models/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") 2 | load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") 3 | load("//tools:shared.bzl", "versions") 4 | 5 | # Code 6 | 7 | swift_library( 8 | name = "ModelsLib", 9 | srcs = [ 10 | "Models/Models.swift", 11 | ], 12 | module_name = "Models", 13 | visibility = ["//modules:default_library_visibility"], 14 | ) 15 | 16 | # Tests 17 | 18 | swift_library( 19 | name = "ModelsTestsLib", 20 | testonly = True, 21 | srcs = [ 22 | "Tests/ModelsTests.swift", 23 | ], 24 | module_name = "ModelsTests", 25 | deps = [ 26 | ":ModelsLib", 27 | ], 28 | ) 29 | 30 | ios_unit_test( 31 | name = "ModelsTests", 32 | minimum_os_version = versions.minimum_ios_version, 33 | runner = "//tools:default_test_runner", 34 | visibility = ["//modules:default_test_visibility"], 35 | deps = [":ModelsTestsLib"], 36 | ) 37 | -------------------------------------------------------------------------------- /modules/Models/Models/Models.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct ModelA { 4 | public init() {} 5 | 6 | func internalBoolFunction() -> Bool { 7 | return true 8 | } 9 | } 10 | 11 | public struct ModelB { 12 | public init() {} 13 | } 14 | -------------------------------------------------------------------------------- /modules/Models/Tests/ModelsTests.swift: -------------------------------------------------------------------------------- 1 | @testable import Models 2 | import XCTest 3 | 4 | final class ModelsTests: XCTestCase { 5 | func test_success() { 6 | XCTAssertTrue(ModelA().internalBoolFunction()) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tools/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load( 2 | "@build_bazel_rules_apple//apple/testing/default_runner:ios_xctestrun_runner.bzl", 3 | "ios_xctestrun_runner", 4 | ) 5 | 6 | ios_xctestrun_runner( 7 | name = "default_test_runner", 8 | device_type = "iPhone 16", 9 | random = True, 10 | visibility = ["//visibility:public"], 11 | ) 12 | -------------------------------------------------------------------------------- /tools/extensions.bzl: -------------------------------------------------------------------------------- 1 | load("//tools:repositories.bzl", "swiftlint_dependency") 2 | 3 | def _non_module_dependencies_impl(_ctx): 4 | swiftlint_dependency() 5 | 6 | non_module_dependencies = module_extension( 7 | implementation = _non_module_dependencies_impl, 8 | ) 9 | -------------------------------------------------------------------------------- /tools/repositories.bzl: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | def swiftlint_dependency(): 4 | http_archive( 5 | name = "SwiftLint", 6 | build_file_content = """ 7 | load("@bazel_skylib//rules:native_binary.bzl", "native_binary") 8 | native_binary( 9 | name = "swiftlint", 10 | src = "bin/swiftlint", 11 | out = "swiftlint", 12 | visibility = ["//visibility:public"], 13 | ) 14 | """, 15 | sha256 = "03416a4f75f023e10f9a76945806ddfe70ca06129b895455cc773c5c7d86b73e", 16 | strip_prefix = "SwiftLintBinary.artifactbundle/swiftlint-0.53.0-macos", 17 | url = "https://github.com/realm/SwiftLint/releases/download/0.53.0/SwiftLintBinary-macos.artifactbundle.zip", 18 | ) 19 | -------------------------------------------------------------------------------- /tools/shared.bzl: -------------------------------------------------------------------------------- 1 | app_info = struct( 2 | bundle_name = "SwiftUIApp", 3 | bundle_id = "com.example.SwiftUIApp", 4 | executable_name = "SwiftUIAppBinary", 5 | ) 6 | 7 | versions = struct( 8 | minimum_ios_version = "18.0", 9 | ) 10 | --------------------------------------------------------------------------------