UIView position updates based on device motion via RxSwift I've created a simple function to work as a level, where the dot on screen is centered when the device is completely flat. It changes position based on device orientation. I use RxSwift to update the position of the dot. I'm relatively new to RxSwift, so I'm worried that I'm not using it correctly or to its full value.
I also don't understand capturing self all that well, so please feel free to analyze that as well.
Any input is appreciated!
class ViewController: UIViewController {

    @IBOutlet var levelDot: UIView!
    var positiveMax: (x: CGFloat, y: CGFloat)!
    
    override func viewDidLoad() {
        levelDot.center = self.view.center
        super.viewDidLoad()
        positiveMax = (view.frame.maxX, view.frame.maxY)
        initGryo()
    }

    fileprivate func initGryo() {
        let pitch = PublishSubject&lt;Double&gt;()
        let roll = PublishSubject&lt;Double&gt;()
        let motion = CMMotionManager()
        guard motion.isDeviceMotionAvailable else { return }
        motion.deviceMotionUpdateInterval = 1/60
        motion.startDeviceMotionUpdates()
        let gyroTimer = Timer(fire: Date(), interval: 1/60, repeats: true, block: { timer in
            if let attitude = motion.deviceMotion?.attitude {
                let yModifier: CGFloat = 1.3 // modifier is to make small changes seem bigger on the screen
                
                _ = pitch.map( { (self.positiveMax!.y + CGFloat($0)*self.positiveMax!.y * yModifier) })
                    .map( {$0 / 2} )
                    .subscribe(onNext: { centerY in
                        
//prevent dot from moving off screen
                        guard centerY &lt; self.positiveMax.y else { 
                            self.levelDot.center.y = self.positiveMax.y
                            return
                        }
                        guard centerY &gt; 0 else {
                            self.levelDot.center.y = 0
                            return
                        }
//dot position -- does this capture self? would making levelDot a weak var prevent this?
                        self.levelDot.center.y = centerY
                        
                    })
                pitch.onNext(attitude.pitch)
                
                let xModifier: CGFloat = 1.15 // modifier is to make small changes seem bigger on the screen
                _ = roll.map( { (self.positiveMax!.x + CGFloat($0)*self.positiveMax!.x*xModifier) })
                    .map({ $0 / 2 })
                    .subscribe(onNext: { centerX in
//prevent dot from moving off screen
                        guard centerX &lt; self.positiveMax.x else {
                            self.levelDot.center.x = self.positiveMax.x
                            return
                        }
                        guard centerX &gt; 0 else {
                            self.levelDot.center.x = 0
                            return
                        }
//dot position 
                        self.levelDot.center.x = centerX
                    })
                roll.onNext(attitude.roll)
            }
        })
        RunLoop.current.add(gyroTimer, forMode: .default)
        
    }
    
}
You are correct that you are not using it correctly. For example Subjects are only needed to convert non-rx code into Rx code or to handle cycles. The fact that you have two of them is problematic.
When working on an Observable sequence, start with the effect you want to achieve the figure out what causes the effect to change. In this case, the effect is the updating of the center of the levelDot view.
The center of a view doesn't have a reactive wrapper on it in RxCocoa, so the first step is to make that:
extension Reactive where Base: UIView {
    var center: Binder&lt;CGPoint&gt; {
        return Binder(base) { view, point in
            view.center = point
        }
    }
}

With the above, you will now be able to .bind(to: levelDot.rx.center). In order to do that you need an Observable&lt;CGPoint&gt; that is emitted every time the CMMotionManager updates. CoreMotion is also not in the RxCocoa library by default, but the tools are available in RxCocoa to wrap it.
extension Reactive where Base: CMMotionManager {
    func startDeviceMotionUpdates(to queue: OperationQueue) -&gt; Observable&lt;CMDeviceMotion&gt; {
        return Observable.create { [base] observer in
            base.startDeviceMotionUpdates(to: queue, withHandler: handler(observer: observer))
            return Disposables.create { [base] in
                base.stopDeviceMotionUpdates()
            }
        }
    }
}

func handler&lt;T&gt;(observer: AnyObserver&lt;T&gt;) -&gt; (T?, Error?) -&gt; Void {
    return { data, error in
        if let data = data {
            observer.onNext(data)
        }
        else {
            observer.onError(error ?? RxError.unknown)
        }
    }
}

(I moved the handler into its own function because it can be used for many of the other CoreMotion handler callbacks.)
With the above, you can now call myCoreMotionManager.rx.startDeviceMotionUpdates(on:) with whatever queue you want to use. It will create an Observable. When that observable is subscribed to, it will call the supplied closure which calls the manager's startDeviceMotionUpdates(to:withHandler:) method. The handler will send onNext events to the server whenever the device motion updates. When the observable is unsubscribed to, the disposable will turn off the updates.
Now, it's just a matter of mapping from one to the other which can be done with a pure function. Since you have outside data that is needed in that function, you need a function factory:
func point(limiatedBy positiveMax: CGPoint) -&gt; (CMDeviceMotion) -&gt; CGPoint {
    return { deviceMotion in
        let attitude = deviceMotion.attitude
        let x = max(0, min(positiveMax.x, positiveMax.x + CGFloat(attitude.roll) * positiveMax.x * 1.15 / 2))
        let y = max(0, min(positiveMax.y, positiveMax.y + CGFloat(attitude.pitch) * positiveMax.y * 1.3 / 2))
        return CGPoint(x: x, y: y)
    }
}

Lastly, you forgot the DisposeBag to unwind everything when the view controller goes out of scope...
With all of the above available, your view controller is quite simple. It almost writes itself and you might want to try writing it before you examine the code below:
class ViewController: UIViewController {

    @IBOutlet var levelDot: UIView!
    var positiveMax: CGPoint = CGPoint.zero
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        levelDot.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
        positiveMax = CGPoint(x: view.bounds.maxX, y: view.bounds.maxY)
        initGryo()
    }

    fileprivate func initGryo() {
        let motion = CMMotionManager()
        guard motion.isDeviceMotionAvailable else { return }
        motion.deviceMotionUpdateInterval = 1/60

        motion.rx.startDeviceMotionUpdates(to: OperationQueue.main)
            .map(point(limiatedBy: positiveMax))
            .bind(to: levelDot.rx.center)
            .disposed(by: disposeBag)
    }
}

The other critique you asked for involves capturing self... You should never capture self in a .map(_:) or filter for that matter. Just don't do it.