Поэтому я пишу код, который может находить ближайших пользователей как локально (MultiPeers и Bluetooth Low Energy), так и через сеть (используя базу данных в реальном времени для поиска ближайших пользователей по их местоположению).
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])( /* ... */ )
}
Теперь эти списки будут постоянно обновляться (поскольку мультипиры работают только тогда, когда приложение находится на переднем плане, и, естественно, вы будете входить и выходить из зоны действия ближайших пользователей).
Проблема в том, что время от времени будут дублироваться данные, nearbyBluetoothUsers
может содержать некоторые nearbyMultipeers
, nearbyGeoUsers
могут содержать некоторые nearbyBluetoothUsers
и т. д. Мне нужен способ отобразить список всех этих пользователей в режиме реального времени без отображения повторяющихся данных.
Для простоты предположим, что я показываю их в виде списка
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)
}
}
}
Я пропустил захват через bluetooth, так как это не нужно для понимания проблемы.
Теперь единственное, что я могу придумать в NearbyUsersView
, это сделать
ForEach((viewModel.nearByMultipeers + viewModel.nearbyBluetoothUsers + viewModel.nearbyGeoUsers).removeDuplicates()) { user in /* ... */ }
Но что-то мне подсказывает, что я не получу ожидаемых результатов
Вы можете просто использовать вычисляемую переменную в своем ViewModel
, предполагая, что User
соответствует Equatable
следующим образом:
public var nearbyUsers: Set<User> {
Set(nearbyMultipeers).union(Set(nearbyBluetoothUsers).union(Set(nearbyGeoUsers)))
}
Это преобразует ваши массивы в наборы и создает один набор несколькими объединениями. Наборы не могут иметь дубликатов. Если вам это нужно как массив, вы можете сделать это:
public var nearbyUsers: [User] {
Array(Set(nearbyMultipeers).union(Set(nearbyBluetoothUsers).union(Set(nearbyGeoUsers))))
}
Наконец, если User
соответствует Comparable
, вы можете вернуть отсортированный массив следующим образом:
public var nearbyUsers: [User] {
Array(Set(nearbyMultipeers).union(Set(nearbyBluetoothUsers).union(Set(nearbyGeoUsers)))).sorted()
}
Трудно не видеть
User
, но если предположить, что этоEquatable
, вы можете просто использоватьSet
, чтобы убедиться, что вы не возвращаете дубликаты при объединении массивов.