Sync local profiles consistently (#1051)

Rather than redoing ProfileManager.observeLocal() altogether:

- Keep the existing profiles subscription (localSubscription)
- Reload ALL local profiles on NE notifications

The reload is "heavy" because each profile save causes a reload of ALL
profiles, but it's the most reliable approach and in the end, it only
takes 1-2msec. It can be improved later.

Partially reverts #1049, because the app did not sync when a VPN
configuration was deleted from the OS settings.
This commit is contained in:
Davide 2025-01-04 23:42:10 +01:00 committed by GitHub
parent 236df80950
commit 2f67bcbbf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 8 additions and 32 deletions

View File

@ -41,7 +41,7 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source", "location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
"state" : { "state" : {
"revision" : "6c98ac490edace667f2587ffaf57a5b6a467aa52" "revision" : "8ced87ec9ea088603798be820ab8e2f1e71c2196"
} }
}, },
{ {

View File

@ -62,7 +62,7 @@ let package = Package(
], ],
dependencies: [ dependencies: [
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.14.0"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.14.0"),
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "6c98ac490edace667f2587ffaf57a5b6a467aa52"), .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "8ced87ec9ea088603798be820ab8e2f1e71c2196"),
// .package(path: "../../passepartoutkit-source"), // .package(path: "../../passepartoutkit-source"),
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "1.0.0"), // .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "1.0.0"),
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "12c8b9166ba2bf98b63d3ebc5b561ac2ac5a2f86"), .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "12c8b9166ba2bf98b63d3ebc5b561ac2ac5a2f86"),

View File

@ -45,7 +45,6 @@ public final class NEProfileRepository: ProfileRepository {
repository repository
.managersPublisher .managersPublisher
.dropFirst()
.sink { [weak self] in .sink { [weak self] in
self?.onUpdatedManagers($0) self?.onUpdatedManagers($0)
} }
@ -72,26 +71,14 @@ public final class NEProfileRepository: ProfileRepository {
public func saveProfile(_ profile: Profile) async throws { public func saveProfile(_ profile: Profile) async throws {
try await repository.save(profile, forConnecting: false, options: nil, title: title) try await repository.save(profile, forConnecting: false, options: nil, title: title)
if let index = profilesSubject.value.firstIndex(where: { $0.id == profile.id }) {
profilesSubject.value[index] = profile
} else {
profilesSubject.value.append(profile)
}
} }
public func removeProfiles(withIds profileIds: [Profile.ID]) async throws { public func removeProfiles(withIds profileIds: [Profile.ID]) async throws {
guard !profileIds.isEmpty else { guard !profileIds.isEmpty else {
return return
} }
var removedIds: Set<Profile.ID> = []
defer {
profilesSubject.value.removeAll {
removedIds.contains($0.id)
}
}
for id in profileIds { for id in profileIds {
try await repository.remove(profileId: id) try await repository.remove(profileId: id)
removedIds.insert(id)
} }
} }
@ -102,25 +89,14 @@ public final class NEProfileRepository: ProfileRepository {
private extension NEProfileRepository { private extension NEProfileRepository {
func onUpdatedManagers(_ managers: [Profile.ID: NETunnelProviderManager]) { func onUpdatedManagers(_ managers: [Profile.ID: NETunnelProviderManager]) {
let profiles = profilesSubject let profiles = managers.values.compactMap {
.value do {
.filter { return try repository.profile(from: $0)
managers.keys.contains($0.id) } catch {
pp_log(.App.profiles, .error, "Unable to decode profile from NE manager '\($0.localizedDescription ?? "")': \(error)")
return nil
} }
let removedProfilesDescription = profilesSubject
.value
.filter {
!managers.keys.contains($0.id)
} }
.map {
"\($0.name)(\($0.id)"
}
if !removedProfilesDescription.isEmpty {
pp_log(.App.profiles, .info, "Sync profiles removed externally: \(removedProfilesDescription)")
}
profilesSubject.send(profiles) profilesSubject.send(profiles)
} }
} }