Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ open class UIPredicateEditorCellConfiguration: UIContentConfiguration, Equatable
/// - isEditable: `true` if the fields in this row should be editable, `false` otherwise. **Default**: `true`
/// - indentationLevel: the custom indentation level to apply to this row. Irrespective of the value provided here, the `rowTemplate.indentationLevel` is always checked first.
/// - delegate: the content refreshing delegate which will be notified when the receiver's field or toggle values change
/// - rowMenuActionsProvider: a menu actions provider speficially used for combination rows
/// - rowMenuActionsProvider: a menu actions provider specifically used for combination rows
init(rowTemplate: UIPredicateEditorRowTemplate, traitCollection: UITraitCollection, isEditable: Bool = true, indentationLevel: Int, delegate: (any UIPredicateEditorContentRefreshing)?, rowMenuActionsProvider: (() -> [UIMenuElement])?) {
self.rowTemplate = rowTemplate
state = UICellConfigurationState(traitCollection: traitCollection)
Expand Down
34 changes: 16 additions & 18 deletions Sources/UIPredicateEditor/Public/PredicateController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,27 @@
/// Notified when the predicate of the `UIPredicateEditor`will change.
///
/// The `object` on the `Notification` will be the editor.
@available(*, deprecated, message: "Utilize `PredicateControllerDelegate`")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the assumption that you wanted to switch to a delegate in place of using Notifications. I can easily remove the deprecation if that wasn't the intent.

static let predicateWillChange = Notification.Name(rawValue: "UIPredicateEditor.predicateWillChange")

/// Notified when the predicate of the `UIPredicateEditor` changes.
///
/// The `object` on the `Notification` will be the editor.
@available(*, deprecated, message: "Utilize `PredicateControllerDelegate`")
static let predicateDidChange = Notification.Name(rawValue: "UIPredicateEditor.predicateDidChange")
}

/// Concrete final class that manages predicates, row templates, updating and notifying of predicate changes to its managing view.
///
/// The `UIPRedicateEditorViewController` class uses it internally for its predicate operations.
/// The `UIPredicateEditorViewController` class uses it internally for its predicate operations.
///
/// You may choose to write your own view and use the `PredicateController` as its driving model.
@MainActor
@Observable
public final class PredicateController {
/// The delegate which will receive updates as the controller modified the `predicate`.
public var delegate: PredicateControllerDelegate?

/// contains the predicate evaluated by the editor.
///
/// If one or more parts cannot be queried from the row templates, the property evaluates to `nil`.
Expand Down Expand Up @@ -68,7 +73,7 @@
/// Each row will have its own predicate which is used to form the predicate on the `UIPredicateEditor`.
public var requiredRowTemplates: [UIPredicateEditorRowTemplate] = []

/// Created on-demand everytime as the formatting dictionary may change during runtime
/// Created on-demand every time as the formatting dictionary may change during runtime
var formattingHelper: FormattingDictionaryHelper {
FormattingDictionaryHelper(formattingDictionary: formattingDictionary ?? [:])
}
Expand Down Expand Up @@ -144,17 +149,13 @@
/// The receiver internally updates the derived predicate and notifies subscribers.
/// - Parameter logicalType: logical type of the predicate.
public func updatePredicate(for _: NSCompoundPredicate.LogicalType) {
Task { @MainActor in
notifyPredicateWillChange()
}
notifyPredicateWillChange()

// We assume the first row is the root container if available.
// If we have no rows, we have no predicate.
guard !requiredRowTemplates.isEmpty else {
guard !requiredRowTemplates.isEmpty, !rowTemplates.isEmpty else {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional check here for the rowTemplates. When this was empty the access below of rowTemplates[0] would throw an index out of bounds error.

predicate = nil
Task { @MainActor in
notifyPredicateDidChange()
}
notifyPredicateDidChange()
return
}

Expand All @@ -163,11 +164,8 @@
buildRecursivePredicate(from: $0)
}

predicate = NSCompoundPredicate(type: self.rowTemplates[0].logicalType, subpredicates: predicates)

Task { @MainActor in
notifyPredicateDidChange()
}
predicate = NSCompoundPredicate(type: rowTemplates[0].logicalType, subpredicates: predicates)
notifyPredicateDidChange()
}

/// Add a new row template on the receiver for the given LHS expression title.
Expand Down Expand Up @@ -291,14 +289,14 @@

// MARK: Notify

@MainActor func notifyPredicateWillChange() {
// @TODO: Refactor to call delegate
func notifyPredicateWillChange() {
delegate?.predicateWillChangeForPredicateController(self)
Comment on lines +292 to +293

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the class was already @MainActor isolated, (and the delegate is also @MainActor annotated), it didn't seem necessary to repeat the annotations here and use the detached Task { @MainActor in ... } in the updatePredicate(for:) methods above.

I'm still coming up to speed on the Swift 6 concurrency changes, so I might be missing something.

NotificationCenter.default.post(name: .predicateWillChange, object: self)

Check warning on line 294 in Sources/UIPredicateEditor/Public/PredicateController.swift

View workflow job for this annotation

GitHub Actions / iOS

'predicateWillChange' is deprecated: Utilize `PredicateControllerDelegate`

Check warning on line 294 in Sources/UIPredicateEditor/Public/PredicateController.swift

View workflow job for this annotation

GitHub Actions / MacCatalyst

'predicateWillChange' is deprecated: Utilize `PredicateControllerDelegate`
}

@MainActor func notifyPredicateDidChange() {
// @TODO: Refactor to call delegate
func notifyPredicateDidChange() {
delegate?.predicateDidChangeForPredicateController(self)
NotificationCenter.default.post(name: .predicateDidChange, object: self)

Check warning on line 299 in Sources/UIPredicateEditor/Public/PredicateController.swift

View workflow job for this annotation

GitHub Actions / iOS

'predicateDidChange' is deprecated: Utilize `PredicateControllerDelegate`

Check warning on line 299 in Sources/UIPredicateEditor/Public/PredicateController.swift

View workflow job for this annotation

GitHub Actions / MacCatalyst

'predicateDidChange' is deprecated: Utilize `PredicateControllerDelegate`
}

// MARK: Internal
Expand Down
22 changes: 22 additions & 0 deletions Sources/UIPredicateEditor/Public/PredicateControllerDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#if os(iOS)
import Foundation

/// Optional methods implemented by parties interested in updates to a `PredicateController`
///
/// - note: Not marked `@objc`, so conforming types do not need to also conform to `NSObjectProtocol`.
/// The default implementation makes it so the implementer is only required to respond to 'didChange'
/// events.
@MainActor public protocol PredicateControllerDelegate {
/// Informs the receiver that a predicate is about to change.
func predicateWillChangeForPredicateController(_ predicateController: PredicateController)

/// Informs the receiver that a predicate has been changed.
func predicateDidChangeForPredicateController(_ predicateController: PredicateController)
}

// MARK: - Default Implementation

public extension PredicateControllerDelegate {
func predicateWillChangeForPredicateController(_: PredicateController) {}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import CoreData

/// A template that describes available predicates and how to display them.
///
/// By default, a noncompound row template has three views: a popup (or static text field) on the left, a popup or static text field for operators, and either a popup or other view on the right. You can subclass `UIPredicateEditorRowTemplate` to create a row template with different numbers or types of views.
/// By default, a non-compound row template has three views: a popup (or static text field) on the left, a popup or static text field for operators, and either a popup or other view on the right. You can subclass `UIPredicateEditorRowTemplate` to create a row template with different numbers or types of views.
@MainActor open class UIPredicateEditorRowTemplate: NSObject {

/// An array of ``NSExpression`` objects that represent the left side of a predicate.
Expand Down Expand Up @@ -77,7 +77,7 @@ import CoreData
/// For values greater than zero, the row should be indented in the presenting view. Values lower than 0 should be treated as 0.
public var indentationLevel: Int = 0

/// wehn the row template is setup as a sub-predicate of a ``NSCompoundPredicate``, this ID will match the value of the parent template row.
/// when the row template is setup as a sub-predicate of a ``NSCompoundPredicate``, this ID will match the value of the parent template row.
///
/// The ID will be common across all siblings. The parent will always point to an ``NSComparisonPredicate``.
public var parentTemplateID: UUID?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import UIKit

/// Concrete view controller for managing and editing predicates in a user interface.
///
/// It's `open` by default, encourgaging subclassing, but can be used as is.
/// It's `open` by default, encouraging subclassing, but can be used as is.
///
/// Similar to its `NSPredicateEditor` counterpart, it directly queries rows to be displayed based on its `objectValue`, an instance of `NSPredicate`, and matching rows from `rowTemplates`, and array of `UIPredicateEditorRowTemplate`.
open class UIPredicateEditorViewController: UICollectionViewController {
Expand Down Expand Up @@ -79,15 +79,15 @@ open class UIPredicateEditorViewController: UICollectionViewController {
/// set to `false` once the view appears and the initial predicate is setup.
private var isLoading: Bool = true

/// this is created on-demand everytime as the formatting dictionary may change during runtime
/// this is created on-demand every time as the formatting dictionary may change during runtime
var formattingHelper: FormattingDictionaryHelper {
FormattingDictionaryHelper(formattingDictionary: formattingDictionary ?? [:])
}

// MARK: Init

public init(predicate: NSPredicate, rowTemplates: [UIPredicateEditorRowTemplate], layout: UICollectionViewLayout) {
assert(!rowTemplates.isEmpty, "Initialize the UIPredicateEditor with atleast one row template")
assert(!rowTemplates.isEmpty, "Initialize the UIPredicateEditor with at least one row template")

let compoundTypeRow = rowTemplates.first(where: { !$0.compoundTypes.isEmpty })

Expand Down Expand Up @@ -382,7 +382,8 @@ extension UIPredicateEditorViewController {

private func addRowTemplate(for leftExpressionTitle: String, parentRowTemplate: UIPredicateEditorRowTemplate? = nil) {
if predicateController.addRowTemplate(for: leftExpressionTitle, for: parentRowTemplate),
let logicalType = (parentRowTemplate ?? rowTemplates.first)?.logicalType {
let logicalType = (parentRowTemplate ?? rowTemplates.first)?.logicalType
{
predicateController.updatePredicate(for: logicalType)
}
}
Expand Down
Loading