0

I have a User object

@objc(User)
public class User: NSManagedObject {
    @NSManaged public var firstname: String
    @NSManaged public var lastname: String
    @NSManaged public var country: String
    @NSManaged public var friends: NSSet // of User objects

    var full: String {
        firstname + " " + lastname
    }

    var friendsArray: [User] {
        friends.allObjects as? [User] ?? []
    }
}

and at some point I want to map a large array of users (80k objects) to an array of View models

struct ItemViewModel: Hashable {
    let id: UUID
    let friendsName: [String] 
}

Without lazy it takes a long time, so I have opted for the usag of lazy:

func prepareViewModel(users: [User]) -> [ItemViewModel] {
    users.map { user in
        let friendsName = user.friendsArray.lazy.filter{["en", "fr"].contains($0.country)}.map(\.full)
        return ItemViewModel(id: UUID(), friendsName: friendsName)
    }
}

But I get an error:

Cannot convert value of type 'LazyMapSequence<LazyFilterSequence<LazySequence<[User]>.Elements>.Elements, String>' 
(aka 'LazyMapSequence<LazyFilterSequence<Array<User>>, String>') to expected argument type '[String]'

It makes sense because now the friends names array will be processed lazily later. I have tried to convert the view model struct to hold:

struct ItemViewModel: Hashable {
    let id: UUID
    let friendsName: LazyMapSequence<LazyFilterSequence<[User]>, String>
}

But now it's not Hashable is there a way to keep the auto-conformance to Hashable when using LazyMapSequence<LazyFilterSequence<[User]>, String> as type for ItemViewModel and any tips on how to improve performance of logic

iOSGeek
  • 5,115
  • 9
  • 46
  • 74
  • How is lazy sequence even relevant here? It doesn’t make time consuming things go faster. It sounds to me like what you want is memoization, i.e, don’t convert a user to a view model until you have to, and remember the result so you don’t have to do it again. – matt May 30 '20 at 13:59
  • @matt the hope is that `user.friendsArray.lazy.filter{["en", "fr"].contains($0.country)}.map(\.full)` should be executed only when we access the property `friendsName` in the viewmodel. Ideally yes, we should convert a user to a view model until I have to. But I need to populate the collectionView with the diffable data source – iOSGeek May 30 '20 at 14:21
  • Then laziness is useless. You just need to bite the bullet and convert them all. If it takes a long time you could do it on a background thread. But why does it take a long time? Ask yourself that. – matt May 30 '20 at 14:23
  • @matt actually the laziness helps. When using lazy it takes 5s to map but without it's 12s and without the friends mapping it takes 3s (commenting the line and return and empty array). But I needed to conform the Hashable manually. But maybe the laziness is not enough or not the best option here. The only solution that I have on mind is: 1. Implement a pagination for the collection view and map to viewmodels by batches. 2. Skip the usage of viewmodel and use the managed objects directly. Any other suggestion ? – iOSGeek May 30 '20 at 14:43
  • "Implement a pagination for the collection view and map to viewmodels by batches" Right, that's called _prefetching_. It's an option built in to UICollectionView, and that's what I meant about memoization. – matt May 30 '20 at 14:46
  • @matt as far as I understand the problem with `prefetching ` is that we need to provide the full data first and then prefetch the missing data of those items. I don't see the benefit of using the prefetching over providing the `User` object directly and map it to view model while populating the collection cell `cell.configure(with: viewModel)` – iOSGeek May 30 '20 at 15:54

0 Answers0