在自定义 Publisher 中使用 Realm 时无法分解见证错误
Failed to demangle witness error when using Realm in a custom Publisher
我以前从未遇到过见证 table 错误,但这是我第一次尝试测试自定义发布者,如果我猜的话,我怀疑线程正在发生一些奇怪而美妙的事情基于证人姓名的损坏程度。这里完全出海,所以非常感谢您的指点(或指点!)。
自定义发布者
// MARK: Custom publisher - produces a stream of Object arrays in response to change notifcations on a given Realm collection
extension Publishers {
struct Realm<Collection: RealmCollection>: Publisher {
typealias Output = Array<Collection.Element>
typealias Failure = Never // TODO: Not true but deal with this later
let collection: Collection
init(collection: Collection) {
self.collection = collection
}
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = RealmSubscription(subscriber: subscriber, collection: collection)
subscriber.receive(subscription: subscription)
}
}
}
// MARK: Convenience accessor function to the custom publisher
extension Publishers {
static func realm<Collection: RealmCollection>(collection: Collection) -> Publishers.Realm<Collection> {
return Publishers.Realm(collection: collection)
}
}
// MARK: Custom subscription
private final class RealmSubscription<S: Subscriber, Collection: RealmCollection>: Subscription where S.Input == Array<Collection.Element> {
private var subscriber: S?
private let collection: Collection
private var notificationToken: NotificationToken?
init(subscriber: S, collection: Collection) {
self.subscriber = subscriber
self.collection = collection
self.notificationToken = collection.observe { (changes: RealmCollectionChange) in
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
print("Initial")
let _ = subscriber.receive(Array(collection.elements)) // ERROR THROWN HERE
// case .update(_, let deletions, let insertions, let modifications):
case .update(_, _, _, _):
print("Updated")
let _ = subscriber.receive(Array(collection.elements))
case .error(let error):
fatalError("\(error)")
#warning("Impl error handling - do we want to fail or log and recover?")
}
}
}
func request(_ demand: Subscribers.Demand) {
// no impl as RealmSubscriber is effectively just a sink
}
func cancel() {
print("Cancel called on RealnSubscription")
subscriber = nil
notificationToken = nil
}
}
服务class
protocol RealmServiceType {
func all<Element>(_ type: Element.Type, within realm: Realm) -> AnyPublisher<Array<Element>, Never> where Element: Object
@discardableResult
func addPatient(_ name: String, to realm: Realm) throws -> AnyPublisher<Patient, Never>
func deletePatient(_ patient: Patient, from realm: Realm)
}
extension RealmServiceType {
func all<Element>(_ type: Element.Type) -> AnyPublisher<Array<Element>, Never> where Element: Object {
print("Called \(#function)")
return all(type, within: try! Realm())
}
}
final class TestRealmService: RealmServiceType {
private let patients = [
Patient(name: "Tiddles"), Patient(name: "Fang"), Patient(name: "Phoebe"), Patient(name: "Snowy")
]
init() {
let realm = try! Realm()
guard realm.isEmpty else { return }
try! realm.write {
for p in patients {
realm.add(p)
}
}
}
func all<Element>(_ type: Element.Type, within realm: Realm) -> AnyPublisher<Array<Element>, Never> where Element: Object {
return Publishers.realm(collection: realm.objects(type).sorted(byKeyPath: "name")).eraseToAnyPublisher()
}
func addPatient(_ name: String, to realm: Realm) throws -> AnyPublisher<Patient, Never> {
let patient = Patient(name: name)
try! realm.write {
realm.add(patient)
}
return Just(patient).eraseToAnyPublisher()
}
func deletePatient(_ patient: Patient, from realm: Realm) {
try! realm.write {
realm.delete(patient)
}
}
}
测试用例
class AthenaVSTests: XCTestCase {
private var cancellables = Set<AnyCancellable>()
private var service: RealmServiceType?
override func setUp() {
service = TestRealmService()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
service = nil
cancellables.removeAll()
}
func testRealmPublisher() {
var outcome = [""]
let expectation = self.expectation(description: #function)
let expected = ["Tiddles", "Fang", "Phoebe", "Snowy"]
let _ = service?.all(Patient.self)
.sink(receiveCompletion: { _ in
expectation.fulfill() },
receiveValue: { value in
outcome += value.map { [=12=].name }
})
.store(in: &cancellables)
waitForExpectations(timeout: 2, handler: nil)
XCTAssert(outcome == expected, "Expected \(expected) Objects but got \(outcome)")
}
}
错误信息
未能从损坏的名称“10RealmSwift11RLMIteratorVyxG”中根据 'RealmSwift.Results: Sequence' 为关联类型 'Iterator' 拆除见证
2020-01-13 22:46:07.159964+0000 AthenaVS[3423:171342] 无法从损坏的名称“10RealmSwift11RLMIteratorVyxG”
中符合 'RealmSwift.Results: Sequence' 的关联类型 'Iterator' 的见证
尝试在 RealmSubscription 中的 Realm 通知观察器中执行代码时抛出错误(我已在上面的代码中标记了它),具体为:
let _ = subscriber.receive(Array(collection.elements))
想法?
这是一个转移注意力的问题,但与 Combine 完全相关,而是一些 Swift 构建问题,请尝试切换到 Carthage 而不是使用 SPM 以查看问题是否消失。
对于其他 link 可能是 relevant
我以前从未遇到过见证 table 错误,但这是我第一次尝试测试自定义发布者,如果我猜的话,我怀疑线程正在发生一些奇怪而美妙的事情基于证人姓名的损坏程度。这里完全出海,所以非常感谢您的指点(或指点!)。
自定义发布者
// MARK: Custom publisher - produces a stream of Object arrays in response to change notifcations on a given Realm collection
extension Publishers {
struct Realm<Collection: RealmCollection>: Publisher {
typealias Output = Array<Collection.Element>
typealias Failure = Never // TODO: Not true but deal with this later
let collection: Collection
init(collection: Collection) {
self.collection = collection
}
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = RealmSubscription(subscriber: subscriber, collection: collection)
subscriber.receive(subscription: subscription)
}
}
}
// MARK: Convenience accessor function to the custom publisher
extension Publishers {
static func realm<Collection: RealmCollection>(collection: Collection) -> Publishers.Realm<Collection> {
return Publishers.Realm(collection: collection)
}
}
// MARK: Custom subscription
private final class RealmSubscription<S: Subscriber, Collection: RealmCollection>: Subscription where S.Input == Array<Collection.Element> {
private var subscriber: S?
private let collection: Collection
private var notificationToken: NotificationToken?
init(subscriber: S, collection: Collection) {
self.subscriber = subscriber
self.collection = collection
self.notificationToken = collection.observe { (changes: RealmCollectionChange) in
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
print("Initial")
let _ = subscriber.receive(Array(collection.elements)) // ERROR THROWN HERE
// case .update(_, let deletions, let insertions, let modifications):
case .update(_, _, _, _):
print("Updated")
let _ = subscriber.receive(Array(collection.elements))
case .error(let error):
fatalError("\(error)")
#warning("Impl error handling - do we want to fail or log and recover?")
}
}
}
func request(_ demand: Subscribers.Demand) {
// no impl as RealmSubscriber is effectively just a sink
}
func cancel() {
print("Cancel called on RealnSubscription")
subscriber = nil
notificationToken = nil
}
}
服务class
protocol RealmServiceType {
func all<Element>(_ type: Element.Type, within realm: Realm) -> AnyPublisher<Array<Element>, Never> where Element: Object
@discardableResult
func addPatient(_ name: String, to realm: Realm) throws -> AnyPublisher<Patient, Never>
func deletePatient(_ patient: Patient, from realm: Realm)
}
extension RealmServiceType {
func all<Element>(_ type: Element.Type) -> AnyPublisher<Array<Element>, Never> where Element: Object {
print("Called \(#function)")
return all(type, within: try! Realm())
}
}
final class TestRealmService: RealmServiceType {
private let patients = [
Patient(name: "Tiddles"), Patient(name: "Fang"), Patient(name: "Phoebe"), Patient(name: "Snowy")
]
init() {
let realm = try! Realm()
guard realm.isEmpty else { return }
try! realm.write {
for p in patients {
realm.add(p)
}
}
}
func all<Element>(_ type: Element.Type, within realm: Realm) -> AnyPublisher<Array<Element>, Never> where Element: Object {
return Publishers.realm(collection: realm.objects(type).sorted(byKeyPath: "name")).eraseToAnyPublisher()
}
func addPatient(_ name: String, to realm: Realm) throws -> AnyPublisher<Patient, Never> {
let patient = Patient(name: name)
try! realm.write {
realm.add(patient)
}
return Just(patient).eraseToAnyPublisher()
}
func deletePatient(_ patient: Patient, from realm: Realm) {
try! realm.write {
realm.delete(patient)
}
}
}
测试用例
class AthenaVSTests: XCTestCase {
private var cancellables = Set<AnyCancellable>()
private var service: RealmServiceType?
override func setUp() {
service = TestRealmService()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
service = nil
cancellables.removeAll()
}
func testRealmPublisher() {
var outcome = [""]
let expectation = self.expectation(description: #function)
let expected = ["Tiddles", "Fang", "Phoebe", "Snowy"]
let _ = service?.all(Patient.self)
.sink(receiveCompletion: { _ in
expectation.fulfill() },
receiveValue: { value in
outcome += value.map { [=12=].name }
})
.store(in: &cancellables)
waitForExpectations(timeout: 2, handler: nil)
XCTAssert(outcome == expected, "Expected \(expected) Objects but got \(outcome)")
}
}
错误信息
未能从损坏的名称“10RealmSwift11RLMIteratorVyxG”中根据 'RealmSwift.Results: Sequence' 为关联类型 'Iterator' 拆除见证 2020-01-13 22:46:07.159964+0000 AthenaVS[3423:171342] 无法从损坏的名称“10RealmSwift11RLMIteratorVyxG”
中符合 'RealmSwift.Results: Sequence' 的关联类型 'Iterator' 的见证尝试在 RealmSubscription 中的 Realm 通知观察器中执行代码时抛出错误(我已在上面的代码中标记了它),具体为:
let _ = subscriber.receive(Array(collection.elements))
想法?
这是一个转移注意力的问题,但与 Combine 完全相关,而是一些 Swift 构建问题,请尝试切换到 Carthage 而不是使用 SPM 以查看问题是否消失。
对于其他 link 可能是 relevant