SwiftUI 规则

Swift5.1 macOS iOS watchOS Travis

完全声明式:SwiftUI 规则。

SwiftUI 规则在 AlwaysRightInstitute 的配套博客文章中进行了介绍:动态环境 ¶ SwiftUI 规则

要求

SwiftUI 规则需要能够运行 SwiftUI 的环境。即:macOS Catalina、iOS 13 或 watchOS 6。需要配合 Xcode 11 使用。

请注意,您可以在 Mojave 模拟器上运行 iOS 13/watchOS 6 应用程序,所以这也没问题。

使用软件包

您可以直接将 SwiftUIRules Xcode 项目拖到您自己的项目中,也可以使用 Swift Package Manager。

软件包 URL 是:https://github.com/DirectToSwiftUI/SwiftUIRules.git

使用 SwiftUI 规则

SwiftUI 规则在 AlwaysRightInstitute 的配套博客文章中进行了介绍:动态环境 ¶ SwiftUI 规则

声明自己的环境键

假设我们想要添加一个名为 fancyColor 的自定义环境键。

首先我们需要一个 DynamicEnvironmentKey 声明

struct FancyColorEnvironmentKey: DynamicEnvironmentKey {
  public static let defaultValue = Color.black
}

最重要的是,它指定了环境键的静态 Swift 类型(Color),并提供了一个默认值。当查询环境键时,但用户没有显式设置值时,将使用该默认值。

其次,我们需要在 DynamicEnvironmentPathes 结构体上声明一个属性

extension DynamicEnvironmentPathes {
  var fancyColor : Color {
    set { self[dynamic: FancyColorEnvironmentKey.self] = newValue }
    get { self[dynamic: FancyColorEnvironmentKey.self] }
  }
}

就这样。我们可以开始使用我们的新键了。

某个 View 使用 @Environment 属性包装器访问我们出色的新 fancyColor

struct FancyText: View {
  
  @Environment(\.fancyColor) private var color
  
  var label : String
  
  var body: some View {
    Text(label)
      .foregroundColor(color) // boooring
  }
}

和一个 View 提供它

struct MyPage: View {
  
  var body: some View {
    VStack {
      Text("Hello")
      FancyText("World")
    }
    .environment(\.fancyColor, .red)
  }
}

设置 Ruling Environment

我们建议创建一个 RuleModel.swift Swift 文件。将所有规则放在该中心位置。

// RuleModel.swift
import SwiftUIRules

let ruleModel : RuleModel = [
  \.priority == .low    => \.fancyColor <= .gray,
  \.priority == .high   => \.fancyColor <= .red,
  \.priority == .normal => \.fancyColor <= .black
]

您可以在 SwiftUI View 层次结构的任何位置挂钩规则系统,但我们再次建议在最顶层执行此操作。例如,在 Xcode 中生成的新应用程序中,您可以像这样修改生成的 ContentView

struct ContentView: View {
  private let ruleContext = RuleContext(ruleModel: ruleModel)
  
  var body: some View {
    Group {
      // your views
    }
    .environment(\.ruleContext, ruleContext)
  }
}

通常需要注入一些“根”属性

struct TodoList: View {
  let todos: [ Todo ]
  
  var body: someView {
    VStack {
      Text("Todos:")
      ForEach(todos) { todo in
        TodoView()
           // make todo available to the rule system
          .environment(\.todo, todo)
      }
    }
  }
}

TodoView 及其子视图现在可以使用规则系统派生 todo 键的环境值。

用例

哈!无穷无尽 🤓 “以规则思考”™(即声明式)是截然不同的,但它们允许您以高度解耦且真正“声明式”的方式组合您的应用程序。

它可以用于低级别,有点像 CSS。将动态环境键视为有点像 CSS 类。例如,您可以根据平台切换设置

[
  \.platform == "watch" => \.details <= "minimal",
  \.platform == "phone" => \.details <= "regular",
  \.platform == "mac" || \.platform == "pad" 
  => \.details <= "high"
]

但它也可以用于非常高的级别,例如在工作流程系统中

[
  \.task.status == "done"    => \.view <= TaskFinishedView(),
  \.task.status == "done"    => \.actions <= [],
  \.task.status == "created" => \.view <= NewTaskView(),
  \.task.status == "created" => \.actions = [ .accept, .reject ]
]

struct TaskView: View {
  @Environment(\.view) var body // body derived from rules
}

由于 SwiftUI View 也只是轻量级结构体,因此您可以构建携带它们的动态属性!

无论如何:我们对如何使用它的任何想法都感兴趣!

限制

DynamicEnvironmentKeys

当前,规则只能评估 DynamicEnvironmentKeys,它不考虑常规环境键。也就是说,您不能使用规则系统驱动例如内置的 SwiftUI lineLimit

[
  \.user.status == "VIP" => \.lineLimit <= 10,
  \.lineLimit <= 2
]

不起作用。这目前通过要求与系统一起使用的键具有 DynamicEnvironmentKey 类型来显式化。因此,您不会意外地遇到这种情况。

我们可能会将其开放给任何 EnvironmentKey,待定。

赋值中没有 KeyPath

有时可能想要这样

\.todos.count > 10 => \.person.status <= "VIP"

即,将值分配给多组件 keypath (\.person.status)。这不起作用

SwiftUI 错误

有时 SwiftUI 在导航期间或在 List 中会“丢失”其环境。 watchOS 和 macOS 似乎特别有问题,iOS 较少。如果发生这种情况,可以手动传递 ruleContext

struct MyNavLink<Destination, Content>: View {
  @Environment(\.ruleContext) var ruleContext
  ...
  var body: someView {
    NavLink(destination: destination
      // Explicitly pass along:
      .environment(\.ruleContext, ruleContext)) 
  ...
}

The Always Right InstituteZeeZide 为您带来。我们喜欢 反馈、GitHub 星星、酷炫的 合同工作,大概是您可以想到的任何形式的赞美。