@Diffable
宏提供了一种有效的方法来计算类型的两个实例之间的差异,通过自动生成代码来比较属性。它确保您只专注于类型的高级设计,同时利用宏来实现高级功能。
要使用 @Diffable
宏,目标类型必须
class
、struct
或 enum
。Equatable
协议。该宏将为类型中所有 Equatable
属性生成优化的差异计算实现。
应用后,该宏会生成一个 computeDifference 方法,该方法计算两个 Config 实例之间的差异,并返回一个强类型结果,指示已更改的属性。
@Diffable
public struct Config: Equatable {
public var name: String
public var id: UUID
}
// expandedSource
public struct Config: Equatable {
public var name: String
public var id: UUID
public struct Difference: OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
static let name = Difference(rawValue: 1 << 0)
static let id = Difference(rawValue: 1 << 1)
}
public func computeDifference(from other: Self) -> Difference {
var difference: Difference = []
var currentCopy: Self? = self
var otherCopy: Self? = other
if currentCopy?.name != otherCopy?.name {
difference.insert(.name)
}
if currentCopy?.id != otherCopy?.id {
difference.insert(.id)
}
currentCopy = nil
otherCopy = nil
return difference
}
}
// usage
let configOne = Config(name: "HEssam", id: UUID())
let configTwo = Config(name: "Alfred", id: UUID())
let differences = configOne.computeDifference(from: configTwo)
/// A custom UIView that displays a person's name and age.
/// This view uses a diffable configuration to detect and apply changes to its properties efficiently.
final class PersonView: UIView {
// MARK: - UI Elements
/// Label to display the person's name.
private let nameLabel: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .headline)
return label
}()
/// Label to display the person's age.
private let ageLabel: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .subheadline)
return label
}()
// MARK: - Properties
/// The current configuration of the view.
private var configuration: Configuration
// MARK: - Initializers
/// Initializes the view with the given configuration.
///
/// - Parameter configuration: The initial configuration of the view.
init(configuration: Configuration) {
self.configuration = configuration
super.init(frame: .zero)
}
@available(*, unavailable, message: "init(frame:) is not supported. Use init(configuration:) instead.")
override init(frame: CGRect) {
fatalError("init(frame:) has not been implemented")
}
@available(*, unavailable, message: "init(coder:) is not supported. Use init(configuration:) instead.")
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Configuration Update
/// Updates the view's configuration and applies changes to the UI.
///
/// - Parameter configuration: The new configuration to apply.
func updateConfiguration(to configuration: Configuration) {
// Compute the differences between the old and new configurations.
let differences = self.configuration.computeDifference(from: configuration)
// Iterate through potential differences and update the UI for changed properties.
for i in 0 ..< Int.bitWidth {
let bit = 1 << i
let difference = Configuration.Difference(rawValue: bit)
if differences.contains(difference) {
switch difference {
case .age:
// Update the age label if the age has changed.
self.ageLabel.text = "\(configuration.age)"
case .name:
// Update the name label if the name has changed.
self.nameLabel.text = configuration.name
default:
break
}
}
}
// Update the stored configuration.
self.configuration = configuration
}
// MARK: - Configuration Struct
/// Represents the configuration of the `PersonView`.
/// This struct uses the `@Diffable` macro to enable efficient change detection.
@Diffable
struct Configuration: Equatable {
/// The person's name.
let name: String
/// The person's age.
let age: Int
}
}
DiffableMacroError
枚举提供了详细的错误情况,以帮助开发人员调试应用宏时的问题。
我们热烈欢迎您为 BuildableMacro 项目做出贡献!无论是修复错误、改进文档还是添加新功能,我们都感谢您的帮助。以下是如何贡献
为了明确我们对成员的期望,我们采用了 Contributor Covenant 定义的行为准则。 该文档在许多开源社区中使用。 更多信息请参见 行为准则。
请查看 LICENSE 了解详细信息。