{"id":32143270,"url":"https://github.com/directtoswift/swiftuirules","last_synced_at":"2025-10-21T07:55:45.524Z","repository":{"id":63908394,"uuid":"205560702","full_name":"DirectToSwift/SwiftUIRules","owner":"DirectToSwift","description":"Dynamic, Rule based @EnvironmentKeys for SwiftUI","archived":false,"fork":false,"pushed_at":"2019-09-20T14:41:54.000Z","size":69,"stargazers_count":57,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-10-01T01:50:07.416Z","etag":null,"topics":["declarative-programming","swiftui","swiftui-rules"],"latest_commit_sha":null,"homepage":"http://www.alwaysrightinstitute.com/swiftuirules/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DirectToSwift.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-31T15:17:05.000Z","updated_at":"2024-08-29T11:28:28.000Z","dependencies_parsed_at":"2022-11-28T22:57:41.087Z","dependency_job_id":null,"html_url":"https://github.com/DirectToSwift/SwiftUIRules","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/DirectToSwift/SwiftUIRules","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DirectToSwift%2FSwiftUIRules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DirectToSwift%2FSwiftUIRules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DirectToSwift%2FSwiftUIRules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DirectToSwift%2FSwiftUIRules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DirectToSwift","download_url":"https://codeload.github.com/DirectToSwift/SwiftUIRules/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DirectToSwift%2FSwiftUIRules/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280225807,"owners_count":26293888,"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-10-21T02:00:06.614Z","response_time":58,"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":["declarative-programming","swiftui","swiftui-rules"],"created_at":"2025-10-21T07:55:43.743Z","updated_at":"2025-10-21T07:55:45.519Z","avatar_url":"https://github.com/DirectToSwift.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch2\u003eSwiftUI Rules\n  \u003cimg src=\"https://zeezide.com/img/SwiftUIRules/SwiftUIRulesIcon.svg\"\n       align=\"right\" width=\"128\" height=\"128\" /\u003e\n\u003c/h2\u003e\n\n![Swift5.1](https://img.shields.io/badge/swift-5.1-blue.svg)\n![macOS](https://img.shields.io/badge/os-macOS-green.svg?style=flat)\n![iOS](https://img.shields.io/badge/os-iOS-green.svg?style=flat)\n![watchOS](https://img.shields.io/badge/os-watchOS-green.svg?style=flat)\n![Travis](https://api.travis-ci.org/DirectToSwiftUI/SwiftUIRules.svg?branch=develop\u0026style=flat)\n\n_Going fully declarative_: SwiftUI Rules.\n\nSwiftUI Rules is covered in the companion AlwaysRightInstitute\nblog post:\n[Dynamic Environments ¶ SwiftUI Rules](http://www.alwaysrightinstitute.com/swiftuirules/).\n\n## Requirements\n\nSwiftUI Rules requires an environment capable to run SwiftUI.\nThat is: macOS Catalina, iOS 13 or watchOS 6.\nIn combination w/ Xcode 11.\n\nNote that you can run iOS 13/watchOS 6 apps on Mojave in the emulator,\nso that is fine as well.\n\n## Using the Package\n\nYou can either just drag the SwiftUIRules Xcode project into your own\nproject,\nor you can use Swift Package Manager.\n\nThe package URL is:\n[https://github.com/DirectToSwiftUI/SwiftUIRules.git\n](https://github.com/DirectToSwiftUI/SwiftUIRules.git).\n\n## Using SwiftUI Rules\n\nSwiftUI Rules is covered in the companion AlwaysRightInstitute\nblog post:\n[Dynamic Environments ¶ SwiftUI Rules](http://www.alwaysrightinstitute.com/swiftuirules/).\n\n### Declaring Own Environment Keys\n\nLet's say we want to add an own\nenvironment key called `fancyColor`.\n\nFirst thing we need is an \n[`DynamicEnvironmentKey`](https://github.com/DirectToSwiftUI/SwiftUIRules/blob/develop/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentKey.swift#L17) \ndeclaration:\n```swift\nstruct FancyColorEnvironmentKey: DynamicEnvironmentKey {\n  public static let defaultValue = Color.black\n}\n```\nMost importantly this specifies the static Swift type of the environment key\n(`Color`)\nand it provides a default value.\nThat value is used when the environment key is queried, \nbut no value has been explicitly set by the user.\n\nSecond we need to declare a property on the\n[DynamicEnvironmentPathes](https://github.com/DirectToSwiftUI/SwiftUIRules/blob/develop/Sources/SwiftUIRules/DynamicEnvironment/DynamicEnvironmentPathes.swift#L19)\nstruct:\n```swift\nextension DynamicEnvironmentPathes {\n  var fancyColor : Color {\n    set { self[dynamic: FancyColorEnvironmentKey.self] = newValue }\n    get { self[dynamic: FancyColorEnvironmentKey.self] }\n  }\n}\n```\nThat's it. We can start using our new key.\n\nSome View accessing our splendid new `fancyColor`\nusing the \n[@Environment](https://developer.apple.com/documentation/swiftui/environment)\nproperty wrapper:\n```swift\nstruct FancyText: View {\n  \n  @Environment(\\.fancyColor) private var color\n  \n  var label : String\n  \n  var body: some View {\n    Text(label)\n      .foregroundColor(color) // boooring\n  }\n}\n```\nand a View providing it:\n\n```swift\nstruct MyPage: View {\n  \n  var body: some View {\n    VStack {\n      Text(\"Hello\")\n      FancyText(\"World\")\n    }\n    .environment(\\.fancyColor, .red)\n  }\n}\n```\n\n### Setting Up a Ruling Environment\n\nWe recommend creating a `RuleModel.swift` Swift file. Put all your\nrules in that central location:\n```swift\n// RuleModel.swift\nimport SwiftUIRules\n\nlet ruleModel : RuleModel = [\n  \\.priority == .low    =\u003e \\.fancyColor \u003c= .gray,\n  \\.priority == .high   =\u003e \\.fancyColor \u003c= .red,\n  \\.priority == .normal =\u003e \\.fancyColor \u003c= .black\n]\n```\n\nYou can hookup the rule system at any place in the SwiftUI View hierarchy,\nbut we again recommend to do that at the very top.\nFor example in a fresh application generated in Xcode, you could modify\nthe generated `ContentView` like so:\n```swift\nstruct ContentView: View {\n  private let ruleContext = RuleContext(ruleModel: ruleModel)\n  \n  var body: some View {\n    Group {\n      // your views\n    }\n    .environment(\\.ruleContext, ruleContext)\n  }\n}\n```\n\nQuite often some “root” properties need to be injected:\n```swift\nstruct TodoList: View {\n  let todos: [ Todo ]\n  \n  var body: someView {\n    VStack {\n      Text(\"Todos:\")\n      ForEach(todos) { todo in\n        TodoView()\n           // make todo available to the rule system\n          .environment(\\.todo, todo)\n      }\n    }\n  }\n}\n```\n`TodoView` and child views of that can now derive environment values of\nthe `todo` key using the rule system.\n\n### Use Cases\n\nHa! Endless 🤓 It is quite different to “Think In Rules”™\n(a.k.a. declaratively),\nbut they allow you to compose your application in a highly decoupled\nand actually “declarative” ways.\n\nIt can be used low level, kinda like CSS. \nConsider dynamic environment keys a little like CSS classes.\nE.g. you could switch settings based on the platform:\n```swift\n[\n  \\.platform == \"watch\" =\u003e \\.details \u003c= \"minimal\",\n  \\.platform == \"phone\" =\u003e \\.details \u003c= \"regular\",\n  \\.platform == \"mac\" || \\.platform == \"pad\" \n  =\u003e \\.details \u003c= \"high\"\n]\n```\n\nBut it can also be used at a very high level,\nfor example in a workflow system:\n```swift\n[\n  \\.task.status == \"done\"    =\u003e \\.view \u003c= TaskFinishedView(),\n  \\.task.status == \"done\"    =\u003e \\.actions \u003c= [],\n  \\.task.status == \"created\" =\u003e \\.view \u003c= NewTaskView(),\n  \\.task.status == \"created\" =\u003e \\.actions = [ .accept, .reject ]\n]\n\nstruct TaskView: View {\n  @Environment(\\.view) var body // body derived from rules\n}\n```\n\nSince SwiftUI Views are also just lightweight structs,\nyou can build dynamic properties which carry them!\n\nIn any case: We are interested in any idea how to use it!\n\n\n### Limitations\n\n#### Only `DynamicEnvironmentKey`s\n\nCurrently rules can only evaluate `DynamicEnvironmentKey`s,\nit doesn't take regular environment keys into account.\nThat is, you can't drive for example the builtin SwiftUI `lineLimit`\nusing the rulesystem.\n```swift\n[\n  \\.user.status == \"VIP\" =\u003e \\.lineLimit \u003c= 10,\n  \\.lineLimit \u003c= 2\n]\n```\n**Does not work**. This is currently made explicit by requiring keys which\nare used w/ the system to have the `DynamicEnvironmentKey` type.\nSO you can't accidentially run into this.\n\nWe may open it up to any `EnvironmentKey`, TBD.\n\n#### No KeyPath'es in Assignments\n\nSometimes one might want this:\n```swift\n\\.todos.count \u003e 10 =\u003e \\.person.status \u003c= \"VIP\"\n```\nI.e. assign a value to a multi component keypath (`\\.person.status`).\nThat **does not work**.\n\n#### SwiftUI Bugs\n\nSometimes SwiftUI “looses” its environment during navigation or in List's.\nwatchOS and macOS seem to be particularily problematic, iOS less so.\nIf that happens, one can pass on the `ruleContext` manually:\n```swift\nstruct MyNavLink\u003cDestination, Content\u003e: View {\n  @Environment(\\.ruleContext) var ruleContext\n  ...\n  var body: someView {\n    NavLink(destination: destination\n      // Explicitly pass along:\n      .environment(\\.ruleContext, ruleContext)) \n  ...\n}\n```\n\n\n## Who\n\nBrought to you by\n[The Always Right Institute](http://www.alwaysrightinstitute.com)\nand\n[ZeeZide](http://zeezide.de).\nWe like\n[feedback](https://twitter.com/ar_institute),\nGitHub stars,\ncool [contract work](http://zeezide.com/en/services/services.html),\npresumably any form of praise you can think of.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdirecttoswift%2Fswiftuirules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdirecttoswift%2Fswiftuirules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdirecttoswift%2Fswiftuirules/lists"}