{"id":23070241,"url":"https://github.com/markbattistella/bezelkit","last_synced_at":"2025-10-15T02:32:26.477Z","repository":{"id":190381942,"uuid":"682430940","full_name":"markbattistella/BezelKit","owner":"markbattistella","description":"BezelKit is a Swift package designed to simplify the process of accessing device-specific bezel sizes in apps. Knowing the exact bezel size can be crucial for aligning UI elements, creating immersive experiences, or when you need pixel-perfect design layouts.","archived":false,"fork":false,"pushed_at":"2025-04-25T10:03:05.000Z","size":3522,"stargazers_count":67,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-02T10:34:52.437Z","etag":null,"topics":["corner-radius","hacktoberfest","ios","macos","open-source","package-manager","swift","swift-package-manager","swiftui","ui-design"],"latest_commit_sha":null,"homepage":"https://swiftpackageindex.com/markbattistella/BezelKit/documentation","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/markbattistella.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":".github/supported-devices.js","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["markbattistella"],"custom":["https://www.paypal.me/markbattistella/5AUD","https://www.paypal.me/markbattistella/10AUD","https://www.paypal.me/markbattistella/20AUD"]}},"created_at":"2023-08-24T06:43:53.000Z","updated_at":"2025-07-26T19:00:41.000Z","dependencies_parsed_at":"2023-08-24T12:08:01.287Z","dependency_job_id":"c22e038e-82cc-4007-9485-abc70d177e1a","html_url":"https://github.com/markbattistella/BezelKit","commit_stats":null,"previous_names":["markbattistella/bezelkit"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/markbattistella/BezelKit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FBezelKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FBezelKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FBezelKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FBezelKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markbattistella","download_url":"https://codeload.github.com/markbattistella/BezelKit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FBezelKit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270578377,"owners_count":24610036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-15T02:00:12.559Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["corner-radius","hacktoberfest","ios","macos","open-source","package-manager","swift","swift-package-manager","swiftui","ui-design"],"created_at":"2024-12-16T06:25:25.734Z","updated_at":"2025-10-15T02:32:21.444Z","avatar_url":"https://github.com/markbattistella.png","language":"Swift","funding_links":["https://github.com/sponsors/markbattistella","https://www.paypal.me/markbattistella/5AUD","https://www.paypal.me/markbattistella/10AUD","https://www.paypal.me/markbattistella/20AUD"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg alt=\"Icon of Package\" src=\"https://raw.githubusercontent.com/markbattistella/BezelKit/main/.github/data/kit-icon.png\" width=\"128\" height=\"128\"/\u003e\n\n# BezelKit\n\n\u003csmall\u003ePerfecting Corners, One Radius at a Time\u003c/small\u003e\n\n![Swift Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fmarkbattistella%2FBezelKit%2Fbadge%3Ftype%3Dswift-versions)\n\n![Platforms](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fmarkbattistella%2FBezelKit%2Fbadge%3Ftype%3Dplatforms)\n\n![Licence](https://img.shields.io/badge/Licence-MIT-white?labelColor=blue\u0026style=flat)\n\n\u003c/div\u003e\n\n## Overview\n\n**BezelKit** is a Swift package designed to simplify the process of accessing device-specific bezel sizes in apps.\n\nKnowing the exact bezel size can be crucial for aligning UI elements, creating immersive experiences, or when you need pixel-perfect design layouts.\n\nBy providing an easy-to-use API, `BezelKit` allows developers to focus more on their app's functionality rather than wrestling with device metrics.\n\n## Rationale\n\n### Quick summary\n\n\u003e - There is **no** public API from Apple for fetching device bezel sizes\n\u003e - Using the internal API can jeopardise App Store eligibility\n\u003e - Static bezel values can cause UI distortions across devices\n\u003e - `BezelKit` offers an easy-to-use solution for accurate bezel metrics\n\n### Longer explanation\n\nApple currently does not offer a public API for fetching the bezel radius of its devices.\n\nAlthough an internal API exists, using it jeopardises the app's eligibility for the App Store — a risk that's not justifiable for a mere UI element.\n\nAnother consideration stems from the variability in screen bezel dimensions across different Apple devices. Setting a static bezel value is problematic for several reasons:\n\n1. If the actual bezel radius is smaller or larger than the static value, the UI corners will appear disproportionately thick or thin.\n\n   ![Zoomed - Static Value](https://raw.githubusercontent.com/markbattistella/BezelKit/main/.github/data/zoomed-static.jpg)\n\n2. On older devices or those with square screens, such as the SE models, the display will inaccurately feature curved corners when it should not.\n\nWhile Apple has provided the [`ContainerRelativeShape`](https://developer.apple.com/documentation/swiftui/containerrelativeshape) inset, its functionality is currently limited to Widgets. For all other applications, this API reports a squared rectangle, making it unsuitable for our needs.\n\nA nice looking solution would look like this:\n\n![Zoomed - BezelKit](https://raw.githubusercontent.com/markbattistella/BezelKit/main/.github/data/zoomed-bezelkit.jpg)\n\n## Compatibility\n\n### Devices\n\nIn terms of the devices supported though, it covers from the initial versions of all devices. See the [supported device list](https://github.com/markbattistella/BezelKit/blob/main/SupportedDeviceList.md).\n\n## Installation\n\n### Swift Package Manager\n\nThe BezelKit package uses Swift Package Manager (SPM) for easy and convenient distribution. Follow these steps to add it to your project:\n\n1. In Xcode, click `File -\u003e Swift Packages -\u003e Add Package Dependency`\n\n2. In the search bar, type `https://github.com/markbattistella/BezelKit` and click `Next`.\n\n3. Specify the version you want to use, then click `Next`.\n\n4. Finally, select the target in which you want to use `BezelKit` and click `Finish`.\n\n## Usage\n\nUsing `BezelKit` is simple and can help you avoid complexities related to device metrics.\n\n### Quick Start\n\n1. Import the BezelKit module:\n\n   ```swift\n   import BezelKit\n   ```\n\n1. Access the device bezel size:\n\n   ```swift\n   let currentBezel: CGFloat = .deviceBezel\n   ```\n\nFor advanced usage, including perfect scaling of UI elements and setting fallback sizes, read the sections below.\n\n### Perfect Scaling\n\nThe `BezelKit` package not only provides an easy way to access device-specific bezel sizes but also enables perfect scaling of rounded corners within the UI.\n\nWhen you have a rounded corner on the outer layer and an inner UI element that also needs a rounded corner, maintaining a perfect aspect ratio becomes essential for a harmonious design. This ensures your UI scales beautifully across different devices.\n\nHere's how you'd manually implement it:\n\n```swift\nlet outerBezel: CGFloat = .deviceBezel\nlet innerBezel = outerBezel - distance  // Perfect ratio\n```\n\nBy following this approach, you can ensure that your UI elements scale perfectly in relation to the device's bezel size.\n\n![Perfect scaling](https://raw.githubusercontent.com/markbattistella/BezelKit/main/.github/data/ratio.jpg)\n\nYou can use the `deviceBezel(with:)` function to pass in the margin size, and it will return the device bezel but perfectly scaled with the inner ratio.\n\n### Setting a Fallback Bezel Size\n\nThe package provides an easy way to specify a fallback bezel size for all Apple device types. By default, the `CGFloat.deviceBezel` attribute returns `0.0` if it cannot ascertain the device's bezel size (on iOS / iPadOS).\n\n#### Enable Zero-Check Option\n\nIn addition to specifying the fallback value, you have the option to return the fallback value even when the bezel size is determined to be zero.\n\n#### UIKit: Setting the Fallback in AppDelegate\n\nFor UIKit-based applications, you can set the fallback value in the `AppDelegate` within the `application(_:didFinishLaunchingWithOptions:)` method. This is the earliest point at which you can set the fallback value for your app.\n\n```swift\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n\n    // Sets a fallback value of 10.0 on iOS and enables zero-check\n    CGFloat.setFallbackConfig(\n      FallbackConfig(\n        iOS: (10.0, true)\n      )\n    )\n\n    return true\n  }\n}\n```\n\n#### SwiftUI: Setting the Fallback on Appear\n\nFor SwiftUI applications, you can set this value in the `init()` function for your main content view.\n\n```swift\nimport SwiftUI\n\n@main\nstruct YourApp: App {\n  init() {\n\n    // Sets a fallback value of 10.0 on iOS and enables zero-check\n    CGFloat.setFallbackConfig(\n      FallbackConfig(\n        iOS: (10.0, true)\n      )\n    )\n  }\n  var body: some Scene {\n    WindowGroup {\n      ContentView()\n    }\n  }\n}\n```\n\n\u003e [!Important]\n\u003e You only need to call `setFallbackConfig(_:)` once, and as early as possible (like in your `App` init) to ensure it's applied globally.\n\n### Effects of Setting a Fallback\n\nIf you've set a fallback value, `CGFloat.deviceBezel` will return this fallback value when it cannot determine the bezel size for the current device, or optionally, when the bezel size is zero.\n\n```swift\n// With fallback set to 10.0 and zero check enabled\nlet currentBezel = CGFloat.deviceBezel\nprint(\"Current device bezel: \\(currentBezel)\")\n\n// Output will be 10.0 if the device is not in the JSON data or the bezel is zero\n```\n\nIf no fallback is set, `CGFloat.BezelKit` defaults to `0.0` when the device-specific bezel size is unavailable.\n\n```swift\n// With no fallback set and zero check disabled\nlet currentBezel = CGFloat.deviceBezel\nprint(\"Current device bezel: \\(currentBezel)\")\n\n// Output will be 0.0 if the device is not in the JSON data\n```\n\n\u003e [!Warning]\n\u003e The previously documented method `setFallbackDeviceBezel(_:ifZero:)` has been deprecated and is unavailable. Please use `setFallbackConfig(_:)` with a `FallbackConfig` instance instead.\n\n### Handling Errors with BezelKit\n\nBezelKit offers optional error handling to manage unexpected issues like missing device bezel data or data parsing problems.\n\nBy using BezelKit's error callback, developers are alerted of these hiccups, allowing them to handle them as they see fit, whether it's logging for debugging or user notifications.\n\nThis ensures a smoother and more resilient app experience.\n\n#### SwiftUI: Error handling\n\n```swift\nimport SwiftUI\nimport BezelKit\n\nstruct ContentView: View {\n  @State private var showErrorAlert: Bool = false\n  @State private var errorMessage: String = \"\"\n\n  var body: some View {\n    RoundedRectangle(cornerRadius: .deviceBezel)\n      .stroke(Color.green, lineWidth: 20)\n      .ignoresSafeArea()\n      .alert(isPresented: $showErrorAlert) {\n        Alert(title: Text(\"Error\"),\n              message: Text(errorMessage),\n              dismissButton: .default(Text(\"Got it!\")))\n      }\n      .onAppear {\n          DeviceBezel.errorCallback = { error in\n            errorMessage = error.localizedDescription\n            showErrorAlert = true\n          }\n      }\n    }\n}\n```\n\n#### UIKit: Error handling\n\n```swift\nimport UIKit\nimport BezelKit\n\nclass ViewController: UIViewController {\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    DeviceBezel.errorCallback = { [weak self] error in\n      let alert = UIAlertController(title: \"Error\",\n                                    message: error.localizedDescription,\n                                    preferredStyle: .alert)\n      alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n      self?.present(alert, animated: true, completion: nil)\n    }\n        \n    let bezelValue = CGFloat.deviceBezel\n    // Use bezelValue for your views\n  }\n}\n```\n\n## Comparison\n\nThis is a comparison between using a static, single value for all devices and how it looks when rendered compared to `BezelKit` which will adapt to each device.\n\nThis was the code when using a static, single value for all devices:\n\n```swift\nimport SwiftUI\n\nstruct ContentView: View {\n  var body: some View {\n    RoundedRectangle(cornerRadius: 60)\n      .stroke(.green, lineWidth: 20)\n      .ignoresSafeArea()\n  }\n}\n```\n\n![Comparison - Static Values](https://raw.githubusercontent.com/markbattistella/BezelKit/main/.github/data/comparison-static.jpg)\n\nIn a fixed value configuration, devices with no curved screen look odd, while this `cornerRadius` is designed for the iPhone 14 Pro Max, it looks chunky on the iPhone 14, and *good-ish* on the iPhone 14 Pro.\n\nThis was the code when using a `BezelKit`:\n\n```swift\nimport SwiftUI\nimport BezelKit\n\nstruct ContentView: View {\n  var body: some View {\n    RoundedRectangle(cornerRadius: .deviceBezel)\n      .stroke(.green, lineWidth: 20)\n      .ignoresSafeArea()\n  }\n}\n```\n\n![Comparison - BezelKit](https://raw.githubusercontent.com/markbattistella/BezelKit/main/.github/data/comparison-bezelkit.jpg)\n\nAs you can see, with no `setFallbackConfig(_:)` set, the iPhone SE (3rd generation) value is set to `0.0` and results in no curve. However, all other curved devices have a consistent look.\n\n## Things to note\n\n`BezelKit` **does not** currently support the affects from display zooming. When the generator runs, it performs all extractions on the \"Standard\" zoom level of the device.\n\nIf this was run on the \"Zoomed\" level, then the bezel radius would be different. However, since the physical device cannot change based on the zoom level, using \"Standard\" is the correct CGFloat number.\n\nThere is also no way to automate zoom levels in `xcrun simctl` so it would have to be a manual inclusion, and at this point in time (unless raised via Issues) there is no really benefit for using the zoomed value for `_displayRoundedCorner`.\n\n## Generating New Bezels\n\nFor generating new bezels please refer to the [`BezelKit - Generator`](https://github.com/markbattistella/BezelKit-Generator) repository.\n\nWhen running the script it is best to do so from the `BezelKit` directory as one of the script lines is to copy the compiled JSON into the `/Resources` directory. This will not exist from the view of the generator repo.\n\n## Apple's Apps\n\nYou can see this style of Bezel `.sheet`s in some of Apple's own apps or their system-wide cards.\n\nFor example, the Watch App on iOS or when you add a new device to the Home App:\n\n| Screenshot (raw) | With iPhone device bezel |\n|-|-|\n| ![Apple Watch iOS App](.github/data/watch-app.png) | ![Apple Watch iOS App](.github/data/watch-app-bezel.png) |\n| ![Apple Home iOS add new device card](.github/data/home-app.png) | ![Apple Home iOS add new device card](.github/data/home-app-bezel.png) |\n\n## Contributing\n\nContributions are more than welcome. If you find a bug or have an idea for an enhancement, please open an issue or provide a pull request.\n\nPlease follow the code style present in the current code base when making contributions.\n\n\u003e [!Note]\n\u003e Any pull requests need to have the title in the following format, otherwise it will be rejected.\n\u003e\n\u003e ```text\n\u003e YYYY-mm-dd - {title}\n\u003e eg. 2023-08-24 - Updated README file\n\u003e ```\n\nI like to track the day from logged request to completion, allowing sorting, and extraction of data. It helps me know how long things have been pending and provides structure.\n\n## Licence\n\n`BezelKit` is released under the MIT license. See LICENCE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkbattistella%2Fbezelkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkbattistella%2Fbezelkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkbattistella%2Fbezelkit/lists"}