0

I need some help with DelegateProxy implementation. Specifically, what is the proper way of implementing it when the delegate field has different name than simply delegate? Such as in SKPhysicsContactDelegate it is called contactDelegate. I tried defining a computed value delegate, but it did not do the trick - https://github.com/maxvol/RxSpriteKit/blob/master/Proxy/RxSKPhysicsContactDelegateProxy.swift

It fails with "DelegateProxy has no factory of <PKPhysicsWorld: 0x280b18990>. Implement DelegateProxy subclass for <PKPhysicsWorld: 0x280b18990> first." Perhaps it is not even related to the delegate field name, but this is the only difference with properly working proxies I could find.

UPDATE: Ha! I just noticed that error message says PKPhysicsWorld, not SKPhysicsWorld. So my hypothesis is that it has something to do with the fact that object in DelegateProxyFactory.createProxy is a PKPhysicsWorld instead of SKPhysicsWorld and _factories[ObjectIdentifier(mirror.subjectType)] returns nil.

Maxim Volgin
  • 3,957
  • 1
  • 23
  • 38

1 Answers1

1

The reason you are getting that error is because your registerKnownImplementations() function isn't getting run.

The following gist should work: https://gist.github.com/dtartaglia/9f1f937628504ca56dbb1aac7d91df2b

The code is also below but the gist can be kept up to date:

//
//  SKPhysicsWorld+Rx.swift
//
//  Created by Daniel Tartaglia on 21 Jan 2019.
//  Copyright © 2019 Daniel Tartaglia. MIT License.
//

import RxSwift
import SpriteKit

public
extension Reactive where Base: SKPhysicsWorld {

    var didBegin: Observable<SKPhysicsContact> {
        return Observable.create { observer in
            physicsContatctDelegatesLock.lock(); defer { physicsContatctDelegatesLock.unlock() }
            let uuid = UUID()
            if let (delegate, beginners, enders) = physicsContatctDelegates[self.base] {
                var new = beginners
                new[uuid] = observer
                physicsContatctDelegates[self.base] = (delegate, new, enders)
            }
            else {
                let delegate = PhysicsContactDelegate(for: self.base)
                self.base.contactDelegate = delegate
                physicsContatctDelegates[self.base] = (delegate, [uuid: observer], [:])
            }

            return Disposables.create {
                physicsContatctDelegatesLock.lock(); defer { physicsContatctDelegatesLock.unlock() }
                let (delegate, beginners, enders) = physicsContatctDelegates[self.base]!
                var new = beginners
                new.removeValue(forKey: uuid)
                if new.isEmpty && enders.isEmpty {
                    physicsContatctDelegates.removeValue(forKey: self.base)
                }
                else {
                    physicsContatctDelegates[self.base] = (delegate, new, enders)
                }
            }
        }
    }

    var didEnd: Observable<SKPhysicsContact> {
        return Observable.create { observer in
            physicsContatctDelegatesLock.lock(); defer { physicsContatctDelegatesLock.unlock() }
            let uuid = UUID()
            if let (delegate, beginners, enders) = physicsContatctDelegates[self.base] {
                var new = enders
                new[uuid] = observer
                physicsContatctDelegates[self.base] = (delegate, beginners, new)
            }
            else {
                let delegate = PhysicsContactDelegate(for: self.base)
                self.base.contactDelegate = delegate
                physicsContatctDelegates[self.base] = (delegate, [:], [uuid: observer])
            }

            return Disposables.create {
                physicsContatctDelegatesLock.lock(); defer { physicsContatctDelegatesLock.unlock() }
                let (delegate, beginners, enders) = physicsContatctDelegates[self.base]!
                var new = enders
                new.removeValue(forKey: uuid)
                if new.isEmpty && enders.isEmpty {
                    physicsContatctDelegates.removeValue(forKey: self.base)
                }
                else {
                    physicsContatctDelegates[self.base] = (delegate, beginners, new)
                }
            }
        }
    }
}

private
class PhysicsContactDelegate: NSObject, SKPhysicsContactDelegate {

    init(for world: SKPhysicsWorld) {
        self.world = world
        super.init()
    }

    func didBegin(_ contact: SKPhysicsContact) {
        physicsContatctDelegatesLock.lock(); defer { physicsContatctDelegatesLock.unlock() }
        let (_, beginners, _) = physicsContatctDelegates[world]!
        for each in beginners.values {
            each.onNext(contact)
        }
    }

    func didEnd(_ contact: SKPhysicsContact) {
        physicsContatctDelegatesLock.lock(); defer { physicsContatctDelegatesLock.unlock() }
        let (_, _, enders) = physicsContatctDelegates[world]!
        for each in enders.values {
            each.onNext(contact)
        }
    }

    let world: SKPhysicsWorld
}

private let physicsContatctDelegatesLock = NSRecursiveLock()
private var physicsContatctDelegates: [SKPhysicsWorld: (SKPhysicsContactDelegate, [UUID: AnyObserver<SKPhysicsContact>], [UUID: AnyObserver<SKPhysicsContact>])] = [:]
Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • Does not make any difference, unfortunately. My `registerKnownImplementations()` is getting called, but what's inside it (`self.register { ... }`) fails. – Maxim Volgin Jan 20 '19 at 15:04
  • 4.4.0; my hypothesis is that it has something to do with the fact that `object` in `DelegateProxyFactory.createProxy` is a `PKPhysicsWorld` instead of `SKPhysicsWorld` and `_factories[ObjectIdentifier(mirror.subjectType)]` returns `nil`. – Maxim Volgin Jan 21 '19 at 06:15
  • I just found this in the developer forums https://forums.developer.apple.com/thread/96994. You are right and this means you can't implement a delegate proxy. There are other ways, I'll update my answer later today. – Daniel T. Jan 21 '19 at 12:37
  • Thanks for finding that thread. Looking forward to your update! – Maxim Volgin Jan 21 '19 at 12:55
  • There you go. The old fashioned way. :-) – Daniel T. Jan 21 '19 at 16:17
  • Woah, thanks. Would it perhaps be preferable if you create a pull request for my repo? Otherwise I will have to copy & paste the bulk of your code. – Maxim Volgin Jan 21 '19 at 17:30
  • First make sure it works. The solution might need some polish. – Daniel T. Jan 21 '19 at 20:32
  • I just made a pull request. Thanks for contributing to the open source community! – Daniel T. Jan 21 '19 at 20:53
  • Perfect! I merged it into (pre-)release 0.0.3, hopefully will test it in the next few days. – Maxim Volgin Jan 22 '19 at 02:26