forked from shadowfacts/Tusker
Fix duplicate saved instances not being uniqued correctly
This commit is contained in:
parent
48662ef1f3
commit
9dd966f639
|
@ -288,6 +288,7 @@
|
||||||
D6C99FCB24FADC91005C74D3 /* MainComposeTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C99FCA24FADC91005C74D3 /* MainComposeTextView.swift */; };
|
D6C99FCB24FADC91005C74D3 /* MainComposeTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C99FCA24FADC91005C74D3 /* MainComposeTextView.swift */; };
|
||||||
D6CA6A92249FAD8900AD45C1 /* AudioSessionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */; };
|
D6CA6A92249FAD8900AD45C1 /* AudioSessionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */; };
|
||||||
D6CA6A94249FADE700AD45C1 /* GalleryPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */; };
|
D6CA6A94249FADE700AD45C1 /* GalleryPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */; };
|
||||||
|
D6CA8CDA2962231F0050C433 /* ArrayUniqueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */; };
|
||||||
D6D12B2F2925D66500D528E1 /* TimelineGapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */; };
|
D6D12B2F2925D66500D528E1 /* TimelineGapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */; };
|
||||||
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */; };
|
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */; };
|
||||||
D6D12B58292D5B2C00D528E1 /* StatusActionAccountListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B57292D5B2C00D528E1 /* StatusActionAccountListViewController.swift */; };
|
D6D12B58292D5B2C00D528E1 /* StatusActionAccountListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B57292D5B2C00D528E1 /* StatusActionAccountListViewController.swift */; };
|
||||||
|
@ -672,6 +673,7 @@
|
||||||
D6C99FCA24FADC91005C74D3 /* MainComposeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainComposeTextView.swift; sourceTree = "<group>"; };
|
D6C99FCA24FADC91005C74D3 /* MainComposeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainComposeTextView.swift; sourceTree = "<group>"; };
|
||||||
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSessionHelper.swift; sourceTree = "<group>"; };
|
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSessionHelper.swift; sourceTree = "<group>"; };
|
||||||
D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryPlayerViewController.swift; sourceTree = "<group>"; };
|
D6CA6A93249FADE700AD45C1 /* GalleryPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryPlayerViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUniqueTests.swift; sourceTree = "<group>"; };
|
||||||
D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineGapCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineGapCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6D12B57292D5B2C00D528E1 /* StatusActionAccountListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListViewController.swift; sourceTree = "<group>"; };
|
D6D12B57292D5B2C00D528E1 /* StatusActionAccountListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1485,6 +1487,7 @@
|
||||||
D6E426AC25334DA500C02E1C /* FuzzyMatcherTests.swift */,
|
D6E426AC25334DA500C02E1C /* FuzzyMatcherTests.swift */,
|
||||||
D6114E1627F8BB210080E273 /* VersionTests.swift */,
|
D6114E1627F8BB210080E273 /* VersionTests.swift */,
|
||||||
D61F75A029396DE200C0B37F /* SemiCaseSensitiveComparatorTests.swift */,
|
D61F75A029396DE200C0B37F /* SemiCaseSensitiveComparatorTests.swift */,
|
||||||
|
D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */,
|
||||||
D6D4DDE6212518A200E1C4BB /* Info.plist */,
|
D6D4DDE6212518A200E1C4BB /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = TuskerTests;
|
path = TuskerTests;
|
||||||
|
@ -2136,6 +2139,7 @@
|
||||||
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */,
|
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */,
|
||||||
D6E426AD25334DA500C02E1C /* FuzzyMatcherTests.swift in Sources */,
|
D6E426AD25334DA500C02E1C /* FuzzyMatcherTests.swift in Sources */,
|
||||||
D6114E1727F8BB210080E273 /* VersionTests.swift in Sources */,
|
D6114E1727F8BB210080E273 /* VersionTests.swift in Sources */,
|
||||||
|
D6CA8CDA2962231F0050C433 /* ArrayUniqueTests.swift in Sources */,
|
||||||
D6D4DDE5212518A200E1C4BB /* TuskerTests.swift in Sources */,
|
D6D4DDE5212518A200E1C4BB /* TuskerTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -8,16 +8,31 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Array where Element: Hashable {
|
extension Array {
|
||||||
func uniques() -> [Element] {
|
func uniques<ID: Hashable>(by identify: (Element) -> ID) -> [Element] {
|
||||||
var buffer = [Element]()
|
var uniques = Set<Hashed<Element, ID>>()
|
||||||
var added = Set<Element>()
|
|
||||||
for elem in self {
|
for elem in self {
|
||||||
if !added.contains(elem) {
|
uniques.insert(Hashed(element: elem, id: identify(elem)))
|
||||||
buffer.append(elem)
|
|
||||||
added.insert(elem)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return buffer
|
return uniques.map(\.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Array where Element: Hashable {
|
||||||
|
func uniques() -> [Element] {
|
||||||
|
return uniques(by: { $0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct Hashed<Element, ID: Hashable>: Hashable {
|
||||||
|
let element: Element
|
||||||
|
let id: ID
|
||||||
|
|
||||||
|
static func ==(lhs: Self, rhs: Self) -> Bool {
|
||||||
|
return lhs.id == rhs.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,7 +216,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate, Collect
|
||||||
let req = SavedInstance.fetchRequest(account: mastodonController.accountInfo!)
|
let req = SavedInstance.fetchRequest(account: mastodonController.accountInfo!)
|
||||||
req.sortDescriptors = [NSSortDescriptor(key: "url.host", ascending: true)]
|
req.sortDescriptors = [NSSortDescriptor(key: "url.host", ascending: true)]
|
||||||
do {
|
do {
|
||||||
return try mastodonController.persistentContainer.viewContext.fetch(req).uniques()
|
return try mastodonController.persistentContainer.viewContext.fetch(req).uniques(by: \.url)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ class MainSidebarViewController: UIViewController {
|
||||||
let req = SavedInstance.fetchRequest(account: mastodonController.accountInfo!)
|
let req = SavedInstance.fetchRequest(account: mastodonController.accountInfo!)
|
||||||
req.sortDescriptors = [NSSortDescriptor(key: "url.host", ascending: true)]
|
req.sortDescriptors = [NSSortDescriptor(key: "url.host", ascending: true)]
|
||||||
do {
|
do {
|
||||||
return try mastodonController.persistentContainer.viewContext.fetch(req).uniques()
|
return try mastodonController.persistentContainer.viewContext.fetch(req).uniques(by: \.url)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// ArrayUniqueTests.swift
|
||||||
|
// TuskerTests
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/1/23.
|
||||||
|
// Copyright © 2023 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import Tusker
|
||||||
|
|
||||||
|
final class ArrayUniqueTests: XCTestCase {
|
||||||
|
|
||||||
|
func testUniquesBy() {
|
||||||
|
let a = Test(string: "test")
|
||||||
|
let b = Test(string: "test")
|
||||||
|
XCTAssertNotEqual(a.id, b.id)
|
||||||
|
XCTAssertNotEqual(a.hashValue, b.hashValue)
|
||||||
|
XCTAssertEqual([a, b].uniques(by: \.string), [a])
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test: NSObject {
|
||||||
|
let id = UUID()
|
||||||
|
let string: String
|
||||||
|
|
||||||
|
init(string: String) {
|
||||||
|
self.string = string
|
||||||
|
}
|
||||||
|
|
||||||
|
override func isEqual(_ object: Any?) -> Bool {
|
||||||
|
guard let other = object as? Self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return id == other.id && string == other.string
|
||||||
|
}
|
||||||
|
|
||||||
|
override var hash: Int {
|
||||||
|
var hasher = Hasher()
|
||||||
|
hasher.combine(id)
|
||||||
|
hasher.combine(string)
|
||||||
|
return hasher.finalize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue