Core Motion Swift Closure issue - ios

I'm trying to convert an old game application built in obj-c to the new swift code. i'm having some issues understanding swift closures and how to use them for example in the"startAccelerometerUpdatesToQueue" method.
i have initialized the motion manager in this way
motionManager!.accelerometerUpdateInterval = (1/40)
then in the viewdidload of my view controller
var queue:NSOperationQueue
motionManager?.startAccelerometerUpdatesToQueue(queue, withHandler: {(accelerometerData : CMAccelerometerData, error : NSError) in
})
the "startAccelerometerUpdatesToQueue" is giving me an error and i'm pretty sure i didn't understand the correct closure syntax.
Any ideas?

Actually, you just got the signature wrong – the arguments to your closure need to be optionals (since they are passed from Objective-C, they could be nil). Because of that, the arguments you provide don't match an existing method signature, and because of that you get an error.
Take a look at the iOS 8 API docs, they also provide Swift signatures:
func startAccelerometerUpdatesToQueue(_ queue: NSOperationQueue!,
withHandler handler: CMAccelerometerHandler!)
and CMAccelerometerHandler is defined as
typealias CMAccelerometerHandler = (CMAccelerometerData!, NSError!) -> Void
Thus, your call should be:
motionManager?.startAccelerometerUpdatesToQueue(queue, withHandler: {(accelerometerData : CMAccelerometerData!, error : NSError!) in
})
And as with any function/method that takes a closure as it's last argument, you can leave it out of the argument list and write it after the call (trailing closure syntax – this example also leaves out the types, as they can be inferred, but that is optional):
motionManager?.startAccelerometerUpdatesToQueue(queue) { accelerometerData, error in
}

CMMotionManager
object is the gateway to the motion services provided by iOS. These services provide an app with accelerometer data, rotation-rate data, magnetometer data, and other device-motion data such as attitude. These types of data originate with a device’s accelerometers and (on some models) its magnetometer and gyroscope.
Handling Motion Updates at Specified Intervals
To receive motion data at specific intervals, the app calls a “start” method that takes an operation queue (instance of NSOperationQueue) and a block handler of a specific type for processing those updates. The motion data is passed into the block handler. The frequency of updates is determined by the value of an “interval” property.
Accelerometer.
Set the accelerometerUpdateInterval property to specify an update interval. Call the startAccelerometerUpdatesToQueue:withHandler: method, passing in a block of type CMAccelerometerHandler. Accelerometer data is passed into the block as CMAccelerometerData objects.
Gyroscope.
Magnetometer.
Device motion.
The interval, in seconds, for providing accelerometer updates to the block handler.
Declaration
SWIFT
var accelerometerUpdateInterval: NSTimeInterval
Discussion
The system supplies accelerometer updates to the block handler specified in startAccelerometerUpdatesToQueue:withHandler: at regular intervals determined by the value of this property.
The interval units are in seconds. The value of this property is capped to minimum and maximum values; the maximum value is determined by the maximum frequency supported by the hardware. If your app is sensitive to the intervals of acceleration data, it should always check the timestamps of the delivered CMAccelerometerData instances to determine the true update interval.
Availability
Available in iOS 4.0 and later.

import UIKit
import CoreMotion
class ViewController: UIViewController {
let motionManager = CMMotionManager()
var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
motionManager.startAccelerometerUpdates()
motionManager.startGyroUpdates()
motionManager.startMagnetometerUpdates()
motionManager.startDeviceMotionUpdates()
timer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(ViewController.update), userInfo: nil, repeats: true)
}
#objc func update() {
if let accelerometerData = motionManager.accelerometerData {
print(accelerometerData)
}
if let gyroData = motionManager.gyroData {
print(gyroData)
}
if let magnetometerData = motionManager.magnetometerData {
print(magnetometerData)
}
if let deviceMotion = motionManager.deviceMotion {
print(deviceMotion)
}
}
}

Related

iOS CoreMotion.MotionThread EXC_BAD_ACCESS is thrown after stopDeviceMotionUpdates() is called

I have a view controller that uses CoreMotion to monitor the device's Attitude.
Here is the handler that is used in the call to startDeviceMotionUpdates():
/**
* Receives device motion updates from Core Motion and uses the attitude of the device to update
* the position of the attitude tracker inside the bubble level view.
*/
private func handleAttitude(deviceMotion: CMDeviceMotion?, error: Error?) {
guard let attitude = deviceMotion?.attitude else {
GLog.Error(message: "Could not get device attitude.")
return
}
// Calculate the current attitude vector
let roll = attitude.roll
let pitch = attitude.pitch - optimalAngle
let magnitude = sqrt(roll * roll + pitch * pitch)
// Drawing can only happen on the main thread
DispatchQueue.main.async {
[weak self] in
guard let weakSelf = self else {
GLog.Log("could not get weak self")
return
}
// Move the bubble in the attitude tracker to match the current attitude
weakSelf.bubblePosX.constant = CGFloat(roll * weakSelf.attitudeScalar)
weakSelf.bubblePosY.constant = CGFloat(pitch * weakSelf.attitudeScalar)
// Set the border color based on the current attitude.
if magnitude < weakSelf.yellowThreshold {
weakSelf.attitudeView.layer.borderColor = weakSelf.green.cgColor
} else if magnitude < weakSelf.redThreshold {
weakSelf.attitudeView.layer.borderColor = weakSelf.yellow.cgColor
} else {
weakSelf.attitudeView.layer.borderColor = weakSelf.red.cgColor
}
// Do the actual drawing
weakSelf.view.layoutIfNeeded()
}
}
I added [weak self] to see if it would fix things, but it has not. This crash is not easy to reproduce.
When I am done the with VC that uses CoreMotion, I call stopDeviceMotionUpdates() in the VC's viewWillDisappear() method. This VC is the only class in the app that imports CoreMotion.
However, when I arrive in the next VC, occasionally I see EXC_BAD_ACCESS getting thrown on a co m.apple.CoreMotion.MotionThread.
Anybody know why CoreMotion would spawn a thread after the VC that used it has been dismissed? I've verified that the VC is no longer in memory when the crash happens. And yes, the two VCs I'm dealing with are presented modally.
I've examined the memory graph, and when the crash happens, these CoreMotion objects are being reported:
And:
I don't know if those objects should still be in memory after the instance of the CoreMotionManager has been deallocated or not. According to the memory graph, there is no instance of CoreMotionManager in memory.
The VC that imports CoreMotion also imports ARKit. Not sure if some crazy interaction between CoreMotion and ARKit is the problem.
There does seem to be something going on between the main thread and the MotionThread(14):
I'm not sure what to make of the main thread stack trace.
Sometimes when the CoreMotion VC is dismissed, I've noticed that there is a lag in the memory it uses getting released. The release always happens eventually.
Thanks to anybody who can help!
We have a ARSCNView member. We were not calling sceneView.session.pause() when we dismissed the VC that used the sceneView member. One line of code. That was it.
Are you passing the function handleAttitude direct to startDeviceMotionUpdates
as in startDeviceMotionUpdates(to:someQueue, withHandler:handleAttitude)
That will set up a retain cycle between your VC and CMMotionManager
Try
singletonMM.startDeviceMotionUpdates(to:someQueue) { [weak self] motion,error in
self?.handleAttitude(motion,error)
}
To prevent a strong ref to your VC.

CoreBluetooth Peripheral takes a long time to write value to Characteristic

I am writing an app that needs to write a byte value to a CBPeripheral using iOS CoreBluetooth. I am able to read values, and the write is successful and triggers the didWriteValueFor delegate like it should. Basically, it does work, BUT, it takes a long time (about 3 seconds and reports of over 10). When I test the write process in another app (nRF Connect), the write is almost instantaneous. Here's the code I am using to write to the Characteristic:
func setConfigurationValue(_ i:UInt8){
guard peripheral != nil, configurationStateCharacteristic != nil else { return }
var vint:UInt8 = i
let intData = Data(bytes: &vint, count: MemoryLayout<UInt8>.size)
isWriting = true
peripheral?.writeValue(intData, for: configurationStateCharacteristic!, type: CBCharacteristicWriteType.withResponse)
// Notify any views that they should show a progress overlay
NotificationName.showProgressOverlay.post(userInfo: ["message":"Device configuring".localized], object: self)
}
Note, that peripheral is a stored reference to the CBPeripheral object, and configurationStateCharacteristic is a reference to the CBCharacteristic I am writing to. This code does work, but what is causing the peripheral BLE device to take so long in writing the data and sending a response?
Any ideas?

How to update variable based on an external event in Swift?

I am using a Particle Core to get the temperature from my room. The temperature is accessed through the cloud, which is being constantly updated in a variable. This is how I access the variable and display it:
func updateTemp(){
let seconds = 3.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.myPhoton?.getVariable("tempF", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let _ = error {
print("Failed reading temperature from device")
}
else {
if let larry = result as? Int {
self.temp.text="\(larry)˚"
self.truth++ //Once a value has been found, update the count.
}
}
})
})
}
override func viewDidLoad() {
sparkStart()
}
override func viewDidLayoutSubviews() {
updateTemp()
NSTimer.scheduledTimerWithTimeInterval(100.0, target: self, selector: "updateTemp", userInfo: nil, repeats: true) //Gaurantees that the app is updated every 100 seconds. That way we have a fresh temperature often.
//Stop the spinning once a value has been found
if truth == 1{
activity.stopAnimating()
activity.removeFromSuperview()
}
}
Since this is my Particle Core detecting the temperature from environment, the temperature variable is constantly changing. However, when I use NSTimer, the code does not get updated in the time specified. Instead, it begins by updating based on the specified time, but then the time starts decreases exponentially and the variable is updated every 0.001 seconds or so. Any thoughts?
Im assuming what we see is not the full code. In your viewDidLayoutSubviews function, you call updateTemp twice. Once explicitly and once via timer callback.
Your updateTemp function schedules the network call in the main run loop, that's where the timer is also running. The dispatch_after function queues the execution of the readout updates one after the other. I am now assuming, that something in your display code causes repeated triggers of viewDidLayoutSubviews, each of which schedules two new updates etc. Even if the assumption is false (there are a couple of other possibilities due to network code being slow and the timer also running in the main run loop), I am guessing if you drop the explicit call to updateTemp you'll lose the "exponential" and should be fine.
In general, as the web call is largely asynchronous, you could just use the timer and call your sensor directly or if you feel GCD has an important performance advantage switch to dispatch_async and apply for the next available queue with each call via calling dispatch_get_global_queue

Gathering Accelerometer data in background

I'm writing an app, that has to do some calculations every time the phone moves. I've read every question here, but couldn't get the Accelerometer to gather data in the background (after the user navigates away from the app). I've set the Location updates flag in the Plist.info. This is the code I'm using:
let motionManager = CMMotionManager()
func startMotionUpdates() {
var timestamps = [NSDate]()
if motionManager.accelerometerAvailable {
motionManager.accelerometerUpdateInterval = 1
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue(), withHandler: { (data: CMAccelerometerData?, error: NSError?) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let time = NSDate(timeIntervalSince1970: data!.timestamp)
timestamps.append(time)
print(timestamps)
})
})
}
}
I tried every combination out there, I've tried using CMDeviceMotion, I've tried using CLLocationManager.startUpdatingLocation() to simulate background activity, but nothing works. Does anybody have any ideas?
You cannot be woken up every time the accelerometer changes. You can be woken up whenever the location of the device changes significantly (at least several meters) using CLLocationManager, and that's what "location" means in the background modes.
To track finer-grained motion information, you need to ask the system to start recording the data using CMSensorRecorder, and then later you can ask for the data and compute what you want from it. But you won't be allowed to run in the background continuously watching every jiggle of the device. That would eat too much battery.
See also CMPedometer which addresses certain use cases more directly.

Why am I not getting any accelerometer update?

I am trying to get accelerometer updates with CoreMotion and Swift, here is what I placed in my viewDidLoad :
override func viewDidLoad() {
super.viewDidLoad()
let motionManager = CMMotionManager()
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue()) {
(info:CMAccelerometerData!,error:NSError!) in
if error != nil {
println(error)
}
else {
println("OK")
}
}
}
The problem is that it looks like my closure never gets called (I don't have anything in the console), do you know why?
The problem is that the variable motionManager, to which your CMMotionManager instance is assigned, is declared as a local variable (in the body of the function viewDidLoad), which means that it goes out of existence when the function finishes executing. Therefore its lifetime is about a 10000th of a second.
Well, that is not long enough for your CMMotionManager to obtain very many updates!

Resources