Search code examples
iosarraysswiftmodelrealm

Cannot convert return expression of type 'LazyFilterBidirectionalCollection>'


I am attempting to migrate a Swift 2.2 iOS app that I did not write to Swift 3. There are a bunch of functions that use Realm to obtain models and return them in an array. These all worked in Swift 2.x, but are generating the error below in Swift 3. I have also included the associated function definition.

/SubmissionDataManager.swift:184:16: Cannot convert return expression of type 'LazyFilterBidirectionalCollection>' to return type '[EntryModel]'

func validateSubmission(_ submissionId: Int) -> ([EntryModel]) {
    let realm = try! Realm()

    let submissionModel = realm.objects(SubmissionModel.self).filter({ $0.id == submissionId }).first!

    let entryModels = submissionModel.entryModels

    // Check for all entry details field which are mandatory and are empty and not hidden
    let emptyEntryModels = entryModels.filter({ $0.entryDetailArray.filter({ $0.entryDetailValue.isEmpty && $0.isMandatory && !($0.isHidden) }).count > 0 })

    return emptyEntryModels
}

I'm not sure what the actual problem is, or how I would go about fixing it. Any suggestions appreciated.


Solution

  • Swift is deferring the creation of an array for efficiency's sake. Instead of giving you an Array, it is providing you with a LazyFilterBidirectionalCollection which is a lazy Collection wrapper that includes the elements of an underlying collection that satisfy a predicate. In your case, it is the Realm elements that satisfy the closure you passed to filter. Lazy means that the values are pulled from Realm as you access them instead of all at once. A Collection is a protocol that conforms to Sequence and Indexable. A Sequence is a type that provides sequential, iterated access to its elements. So, at its most basic level, LazyFilterBidirectionalCollection is a sequence.

    There is an initializer for Array that converts a Sequence into an array:

    init<S : Sequence where S.Iterator.Element == Element>(_ s: S)
    

    Since you need to return a real array

    replace:

    return emptyEntryModels
    

    with:

    return Array(emptyEntryModels)
    

    That will make a proper array from the LazyFilterBidirectionalCollection.


    Other examples of using this Array initializer to convert a sequence into an array:

    let digits = Array(0...9)                             // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    let odds = Array(stride(from: 1, through: 9, by: 2))  // [1, 3, 5, 7, 9]