Home > database >  Best practices for combining @published arrays in real time?
Best practices for combining @published arrays in real time?

Time:03-18

So I'm writing code that can find nearby users using both locally (MultiPeers and Bluetooth Low Energy) and through network (Using a real-time database to find nearby users by their location

    class ViewModel: ObservableObject{

    @Published nearbyMultipeers: [User]  = []
    @Published nearbyBluetoothUsers: [User]  = []
    @Published nearbyGeoUsers: [User]  = []


    // This gets the nearby users by GeoLocation and updates the nearbyGeoUsers array 
    func getNearbyUsersByGeoLocation(){ /* ... */ } 
   
   // This will loop through all of the nearby users obtained via multipeer and grab their user data from the database and append it to the nearbyMultipeers array 
    func getUsersFromPeers(nearbyPeers: [Peer])( /* ... */ )

    

  
   


}

Now these lists will constantly update (as multipeers only works when the app is in foreground and naturally you will move in and out of the range of nearby users).

The issue that that there will be duplicate data at times, nearbyBluetoothUsers may contain some nearbyMultipeers, nearbyGeoUsers may contain some nearbyBluetoothUsers etc. I need a way to display a list of all of these users in real-time without displaying duplicate data.

For simplicity let's say I'm displaying them in a list like so

struct NearbyUsersView: View {

       // This observable object contains information on the nearby peers //(multipeers)
       // This is how I get the nearby peers 
       @ObservableObject var multipeerDataSource: MultipeerDataSource

       var body: some View {

           VStack{

              // Ideally I would display them in a scrollable list of some sort, this is 
                  // just to illustrate my issue 

                  ForEach(viewModel.$allUsersExcludingDuplicates){. user in 
                            
                           Text(user.name) 
     
                    }

         }
           .onAppear{
 
                      viewModel.getNearbyUsersByGeoLocation()
                      

}
           .onChange(of: multipeerDataSource.$nearbyPeers) { nearbyPeers

            // this array contains the nearby peers (users) 
           // We have to actually convert it to a `User`, or fetch the user data because //the objects here won't be the actual data it may just contain the user Id or some sort // so we have to grab the actual data 
            viewModel.getUsersFromPeers(nearbyPeers)

} 



    }

}

I omitted grabbing via bluetooth since it isn't necessary to understand the problem.

Now the only thing I can think of in the NearbyUsersView is to do

ForEach((viewModel.nearByMultipeers   viewModel.nearbyBluetoothUsers   viewModel.nearbyGeoUsers).removeDuplicates()) { user in  /* ... */ }

But something tells me I won't have expected results

CodePudding user response:

You could simply use a computed variable in your ViewModel, assuming that User conforms to Equatable like this:

public var nearbyUsers: Set<User> {
   Set(nearbyMultipeers).union(Set(nearbyBluetoothUsers).union(Set(nearbyGeoUsers)))
}

This converts your arrays to sets, and creates one set by multiple unions. Sets can't have duplicates. If you need it as an array, you could do this:

public var nearbyUsers: [User] {
   Array(Set(nearbyMultipeers).union(Set(nearbyBluetoothUsers).union(Set(nearbyGeoUsers))))
}

Lastly, if User conforms to Comparable, you could return a sorted array like this:

public var nearbyUsers: [User] {
   Array(Set(nearbyMultipeers).union(Set(nearbyBluetoothUsers).union(Set(nearbyGeoUsers)))).sorted()
}
  • Related