Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash on SectionedResults on @Projected property with keypath resolution #8403

Open
andreabou opened this issue Oct 25, 2023 · 1 comment
Open

Comments

@andreabou
Copy link

andreabou commented Oct 25, 2023

How frequently does the bug occur?

Always

Description

When creating SectionedResults of a Class Projection with a @Projected property that performs keypath resolution or transformation, Realm raises an exception:

Expected behavior

Creating SectionedResults on a Projection should respect the projection's properties. The SectionedResults accept the class projection's key paths to section results. For example:

realm
    .objects(MyProjection.self)
    .sectioned(by: \.keyPath) <- this works

However, sorting on the same key path does not:

realm
    .objects(MyProjection.self)
    .sectioned(
        by: \.keyPath, 
        sortDescriptors: [.init(keyPath: "keyPath")]
    ) <- does not work; must be the Root class' key path, which makes no sense

Because Realm passes the sorting over to the Root of the projection.

SectionedResults should accept a Class Projection's key paths for sorting, since it does accept them for sectioning and sectioning demands sorting first by the same key.

Stacktrace & log output

On `RealmCollection.swift`
====

    public func sectioned<Key: _Persistable, O: ObjectBase>(by keyPath: KeyPath<Element, Key>,
                                                            sortDescriptors: [SortDescriptor]) -> SectionedResults<Key, Element> where Element: Projection<O> {
        guard let sortDescriptor = sortDescriptors.first else {
            throwRealmException("Can not section Results with empty sortDescriptor parameter.")
        }
        let keyPathString = _name(for: keyPath)
        if keyPathString != sortDescriptor.keyPath {
            throwRealmException("The section key path must match the primary sort descriptor.") <- thrown
        }
        return sectioned(sortDescriptors: sortDescriptors, { $0[keyPath: keyPath] })
    }

Can you reproduce the bug?

Always

Reproduction Steps

private extension String {
    var firstLetter: String {
        guard let first = first else { return "" }
        return String(first).folding(options: .diacriticInsensitive, locale: .current)
    }
}

class Thing: Object {
    @Persisted var name: String
}

class ThingProjection: Projection<Thing> {
    @Projected(\Thing.name.firstLetter) var firstLetter
}

let realm = try! Realm()
realm.beginWriteTransaction()
realm.add(Thing(value: ["name": "a thing"]))
try! realm.commitWrite()

realm.objects(ThingProjection.self).sectioned(by: \.firstLetter, sortDescriptors: [.init(keyPath: "firstLetter")])

Version

10.42.1

What Atlas Services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

iOS 17.0

Build environment

ProductName:		macOS
ProductVersion:		14.0
BuildVersion:		23A344

/Applications/Xcode.app/Contents/Developer
Xcode 15.0
Build version 15A240d

/Users/web/.rbenv/shims/pod
1.12.1


/bin/bash
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin23)

carthage not found
(not in use here)

/usr/bin/git
git version 2.39.3 (Apple Git-145)
@andreabou
Copy link
Author

My two cents:

One potential solution could be to perform the sectioned sorting on the collection, instead of passing it over to the Root class, which I believe would be less performant, but adheres to the principles of the SectionedResults.

Another potential solution could be to create a "virtual" table for the projection and delete it once all observers to the table are released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant