SwiftUI - bridging context pointer correctly

Hi there! I have followed the documentation up until the streaming. We have all the data coming in from the subscription signal and would now like to use this data by appending it to an array. The only problem is that the closure won’t allow us to access self. I did the research and found out that we need to pass the context pointer into the closure to have its properties available from within. I also found this example on the forum of how to bridge it using conventional view controllers:
let _self: DeviceDetailViewController = bridge(ptr: context!)
My question now is how I could achieve the same thing using SwiftUI’s classes because I don’t have a ViewController I could assign the bridge to.

When I try the same with our NSObject class I get the errors above.
Is there any sample code for SwiftUI I could follow to get it working? Thanks in advance.

Comments

  • Does the parent have a view controller?
    Do you want to share your code with me so I can take a look?

  • edited May 2021

    You might be able to do something like this:

    protocol TutorialDelegate: class {
        func newDataPoint(value: CGFloat)
    }
    
    class Tutorial {
        public weak var delegate: TutorialDelegate?
        var device: MetaWear?
    
        func accelStream() {
            // does some other metawear stuff
                mbl_mw_datasignal_subscribe(accel, bridge(obj: self)) { (context, data) in
                    let _self: Tutorial = bridge(ptr: context!)
                    let point: MblMwCartesianFloat = data!.pointee.valueAs()
                    _self.delegate?.newDataPoint(value: CGFloat(point.magnitude))
                }
        }
    
    }
    

    extension MyViewController: TutorialDelegate {
    
        func newDataPoint(value: CGFloat) {
           // Do some stuff with data
        }
    }
    
  • Thank you for your quick response! I really appreciate the help.
    Our project consists of SwiftUI classes only, we don't have any parent classes that use ViewControllers.
    The following is our NSObject class that imports the SDK and creates methods accessible in our SwiftUI bodies across the project:

    class ScanCode: NSObject, ObservableObject {
    
          //Instance variables
         @Published var deviceArray = [MetaWear]()
         //...
    
        func scan() {...}
        func connect() {...}
        func disconnect() {...}
        func stopStream() {...}
        func accelerometerStream() {...}
    
        func startStream()  {
    
            if deviceArray.count > 0 {
    
                for device in deviceArray {
    
                    var pattern = MblMwLedPattern()
                    mbl_mw_led_load_preset_pattern(&pattern, MBL_MW_LED_PRESET_SOLID)
                    mbl_mw_led_stop_and_clear(device.board)
                    mbl_mw_led_write_pattern(device.board, &pattern, MBL_MW_LED_COLOR_GREEN)
                    mbl_mw_led_play(device.board)
                    mbl_mw_sensor_fusion_set_mode(device.board, MBL_MW_SENSOR_FUSION_MODE_NDOF)
    
                    let signal = mbl_mw_sensor_fusion_get_data_signal(device.board, MBL_MW_SENSOR_FUSION_DATA_QUATERNION)!
                        print("\(signal)")
                    mbl_mw_datasignal_subscribe(signal, bridge(obj: self)) { (context, obj) in
                        let quaternion: MblMwQuaternion = obj!.pointee.valueAs()
    
                        print(Double(quaternion.x))
                        print(Double(quaternion.y))
                        print(Double(quaternion.z))
                        print(Double(quaternion.w))
    
                        //Append values to array
    
                    }
    
                    mbl_mw_sensor_fusion_clear_enabled_mask(device.board)
                    mbl_mw_sensor_fusion_enable_data(device.board, MBL_MW_SENSOR_FUSION_DATA_QUATERNION)
                    mbl_mw_sensor_fusion_write_config(device.board)
                    mbl_mw_sensor_fusion_start(device.board)
                }
            }
        }
    
    }
    

    I have abbreviated some methods and variables that don't contribute to the issue we are facing since they all work perfectly and were copied from the documentation.

    I tried your approach to solve the problem, and as expected we were able to bridge the context pointer but couldn't do much with it since we cannot access stored properties in extensions in swift.
    So we were able to call the method you described but ran into a dead end since we couldn't utilize any of the data.

    If we can somehow bridge the context pointer AND call a method from within the class (like it was possible with the ViewController approach) the issue would be solved.

    Thanks again for the help!

  • edited June 2021

    Why can't you do:

    let _self: ScanCode = bridge(ptr: context!)
        _self.deviceArray.append(quaternion)
    
  • That of course works thanks so much!

  • edited January 2022

    @iamjannisgrimm

    We're building a Swift SDK using Combine. It's less verbose, ditches Bolts, and requires no unsafe Swift.

    We're wrapping up a tutorial series with SwiftUI demo apps. Given your SwiftUI work, I'd love to hear your reaction to the current draft. Reading is a bit smoother in Xcode's doc browser, but the HTML version is fine. To use Xcode, double click a DocC archive here or clone the SDK package and press Control Shift CMD D to build the docs.

    For flavor, here’s a snippet logging a variant selection of sensors.

    downloadPipeline = metawear
        .publishWhenConnected()
        .first()
        .optionallyLog(configs.accelerometer)
        .optionallyLog(configs.gyroscope)
        .optionallyLog(configs.quaternion)
        .sink(receiveCompletion: { [weak self] in
            self?.displayError(ifFound: $0)
        }, receiveValue: { [weak self] in
            self?.offerDownloadCallToAction()
        })
    
    metawear.connect()
    

    Here’s a snippet that flashes the LEDs in purple or blue when pressing or releasing the MetaWear button.

    macroSub = metawear?
        .publishWhenConnected()
        .first()
        .command(.macroStartRecording(runOnStartup: true))
        .recordEvents(for: .buttonUp, { recording in
            recording.command(.ledFlash(.Presets.eight.pattern))
        })
        .recordEvents(for: .buttonDown, { recording in
            recording.command(.ledFlash(.Presets.zero.pattern))
        })
        .command(.macroStopRecordingAndGenerateIdentifier)
        .sink(receiveCompletion: { _ in }, receiveValue: { _ in })
    
    metawear?.connect()
    
Sign In or Register to comment.