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()
}