CarPlay-connected CLLocation speed is higher than without CarPlay connection on some car models - gps

The speed property of CLLocation that gets returned in the CLLocationManager's didUpdateLocations method seems to be higher on some car models when CarPlay gets connected.
This doesn't seem related to CarPlay Navigation support in the app, as a demo-project can reproduce the issue without CarPlay support.
The specific car models where this is confirmed to occur are:
Volvo XC90
Ford transit with Sync 3
Renault Megane IV model 2018 with Rlink2 and latest firmware
Opel Astra station (2018)
Ford Focus 2017
Opel Astra (model K, 2017) — Gets half of GPS speed
An example of the difference:
Car Speedometer: 120km/h
GPS Speed from didUpdateLocations: 115km/h
GPS Speed from didUpdateLocations with CarPlay connected on one of those car models: 130km/h
Here you can find a demo-project (using NL locale) that reproduces the issue:
https://github.com/flitsmeister/carplay-speed-problem
Relevant part:
override private init() {
super.init()
// These settings mimick the settings we have in our production app
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates = true
locationManager.activityType = .automotiveNavigation
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
}
func startLocating() {
locationManager.startUpdatingLocation()
}
func stopLocating() {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard lastLocation = locations.first else {
return
}
print(lastLocation.speed) // This property is higher when connected to CarPlay
}

Related

Audio session .ended isn't called with two AVPlayers

Here are steps to reproduce:
Activate AVAudioSession with .playback category.
Register for AVAudioSession.interruptionNotification
Create two AVPlayers and start them
Interrupt playback by calling Siri/receiving a call by Skype, Cellular and etc.
Expected behavior:
Receiving notification of the audio session interruption with .began state at the start and .ended at the end. Also, as a side effect, Siri doesn't respond to commands.
Real behavior:
Only .began notification is called.
To bring back .ended notification (which is used to continue playback) remove one player.
Question: how to handle the audio session interruption with more than 1 AVPlayer running?
Here I created a simple demo project: https://github.com/denis-obukhov/AVAudioSessionBug
Tested on iOS 14.4
import UIKit
import AVFoundation
class ViewController: UIViewController {
private let player1: AVPlayer? = {
$0.volume = 0.5
return $0
}(AVPlayer())
private let player2: AVPlayer? = {
$0.volume = 0.5
return $0 // return nil for any player to bring back .ended interruption notification
}(AVPlayer())
override func viewDidLoad() {
super.viewDidLoad()
registerObservers()
startAudioSession()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
player1?.replaceCurrentItem(with: makePlayerItem(named: "music1"))
player2?.replaceCurrentItem(with: makePlayerItem(named: "music2"))
[player1, player2].forEach { $0?.play() }
}
private func makePlayerItem(named name: String) -> AVPlayerItem {
let fileURL = Bundle.main.url(
forResource: name,
withExtension: "mp3"
)!
return AVPlayerItem(url: fileURL)
}
private func registerObservers() {
NotificationCenter.default.addObserver(
self, selector: #selector(handleInterruption(_:)),
name: AVAudioSession.interruptionNotification,
object: nil
)
}
private func startAudioSession() {
try? AVAudioSession.sharedInstance().setCategory(.playback)
try? AVAudioSession.sharedInstance().setActive(true)
}
#objc private func handleInterruption(_ notification: Notification) {
print("GOT INTERRUPTION")
guard
let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue)
else {
return
}
switch type {
case .began:
print("Interruption BEGAN")
[player1, player2].forEach { $0?.pause() }
case .ended:
// This part isn't called if more than 1 player is playing
print("Interruption ENDED")
[player1, player2].forEach { $0?.play() }
#unknown default:
print("Unknown value")
}
}
}
I just ran into the same issue, and it was driving me crazy for a few days. I'm using two AVQueuePlayer (a subclass of AVPlayer) to play two sets of audio sounds on top of each other, and I get the AVAudioSession.interruptionNotification value of .began when there is an incoming call, but there is no .ended notification when the call ends.
That said, I've found that for some reason, .ended is reliably sent if you instead use two instances of AVAudioPlayer. It also works with one instance of AVAudioPlayer mixed with another instance of AVQueuePlayer. But for some reason using two instances of AVQueuePlayer (or AVPlayer) seems to break it.
Did you ever find a solution for this? For my purposes I need queuing of tracks so I must use AVQueuePlayer, so I'll probably file a bug report with Apple.

bluetooth background mode IOS when the screen is locked

I would like to implement background bluetooth scanning on IOS. When the application goes in background mode it calls TestCentralManagerDelegate which implements DiscoveredPeripheral function. It is triggered when a new bluetooth peripheral device is detected. If a new bluetooth device is detected the application read the manufacture data which is presented in Dictionary advertisementData (as argument of DiscoveredPeripheral function). The manufacture data are obtained by calling ManufactureData = advertisementData["kCBAdvDataManufacturerData"].ToString(). The discovering of the manufacture data was tested on two different iPhones 5s and 6 with the same iOS 12.1. When the application goes in background mode, I locked the screen.
In the case of iPhone 5s, I observed that ManufactureData was found each time
the DiscoveredPeripheral function is triggered. This fact is not true for iPhone 6, each time I got ManufactureData = null. It is worth mentioning that the manufacture data are received in both cases if the screen is not locked.
I do not understand why the iPhone 6 does not find ManufactureData, meanwhile the iPhone 5s does. I would accept the fact that phones have different operating systems and this implies different responses, but in my case this is not the case. I will appreciate any help for better understanding aforementioned problem.
Here is code I am using Xamarin.iOS.
public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber RSSI)
{
try
{
central.StopScan();
if (peripheral == null || advertisementData == null)
{
central.ScanForPeripherals(cbuuids);
return;
}
string ManufactureData;
if (advertisementData.ContainsKey(new NSString("kCBAdvDataManufacturerData")))
{
ManufactureData = advertisementData["kCBAdvDataManufacturerData"].ToString();
}
else
{
ManufactureData = null;
CrossLocalNotifications.Current.Show("no advertising data", "no advertising data", 10);
central.ScanForPeripherals(cbuuids);
return;
}
central.ScanForPeripherals(cbuuids);
}
catch
{
central.ScanForPeripherals(cbuuids);
}
}

AVFoundation capturing video with custom resolution

I'm writing application on OS X, which will capture frames from camera.
Is it possible to set capture setting using AVCaptureDevice.activeFormat property? I had tried this, but it didn't work (session preset overrides it).
I found that on IOS it is possible with setting SessionPreset in AVCaptureSession to AVCaptureSessionPresetInputPriority.
The main purpose is to choose more detailed video resolutions than presets.
Updated: April 08, 2020.
In macOS (unlike iOS), a capture session can automatically configure the capture format after you make changes. To prevent automatic changes to the capture format use lockForConfiguration() method. Then call the beginConfiguration() method, set properties (choose one preset out of a dozen, for instance AVCaptureSessionPresetiFrame960x540) and after that call the commitConfiguration() method. In the end you need to put unlockForConfiguration() after changing a device properties.
Or follow these steps:
Call lockForConfiguration() to acquire access to the device’s config properties.
Change the device’s activeFormat property (as mentioned above & below).
Begin capture with the session’s startRunning() method.
Unlock the device with the unlockForConfiguration().
startRunning() and stopRunning() methods must be invoked to start and stop the flow of your data from the inputs to the outputs, respectively.
You must also call lockForConfiguration() before calling the AVCaptureSession method startRunning(), or the session's preset will override the selected active format on the capture device.
However, you might hold onto a lock, without releasing that lock, if you require the device properties to remain unchanged.
Here are details in developer's documentation lockForConfiguration().
If you attempt to set the active format to one not present in the accessible formats, will throw an invalidArgumentException.
Also, there's an explanation how to change properties: macOS AVFoundation Video Capture
In AVCaptureDevice there are two properties. formats and activeFormat. format will return an NSArrary of AVCaptureDeviceFormat with contains all formats exposed by cam. You select any one format from this list and set it to activeFormat. Make sure that you set the format after you receive the exclusive access to the devlce by calling AVCaptureDevice lockForConfigration. After you set the format release the lock with AVCaptureDevice unlockForConfigration. Then start the AVCaptureSession which will give you the video frames of the format you set.
AVCaptureFormat is a wraper for CMFormatDescription. CMVideoFotmatDescription is the concreete subclass of CMFormatDescription. Use CMVideoFormatDescriptionGetDimentions() to get the width and height in the set format. Use CMFormatDescriptionGetMediaSubType() to get the video codec. For raw fotmats video codec mostly is yuvs or vuy2. For compressed formats its h264, dmb1(mjpeg) and many more.
Here's a macOS code snippet written in Swift:
import Cocoa
import AVFoundation
class ViewController: NSViewController,
AVCaptureVideoDataOutputSampleBufferDelegate {
override func viewDidAppear() {
super.viewDidAppear()
setupCameraSession()
view.layer?.addSublayer(previewLayer)
cameraSession.startRunning()
}
lazy var cameraSession: AVCaptureSession = {
let session = AVCaptureSession()
session.sessionPreset = AVCaptureSession.Preset.hd1280x720
return session
}()
lazy var previewLayer: AVCaptureVideoPreviewLayer = {
let preview = AVCaptureVideoPreviewLayer(session: self.cameraSession)
preview.bounds = CGRect(x: 0,
y: 0,
width: self.view.bounds.width,
height: self.view.bounds.height)
preview.position = CGPoint(x: self.view.bounds.midX,
y: self.view.bounds.midY)
preview.videoGravity = AVLayerVideoGravity.resize
return preview
}()
func setupCameraSession() {
let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
do {
let deviceInput = try AVCaptureDeviceInput(device: captureDevice!)
guard let camera = AVCaptureDevice.default(for: .video)
else { return }
// acquire exclusive access to the device’s properties
try camera.lockForConfiguration()
cameraSession.beginConfiguration()
camera.focusMode = .continuousAutoFocus
camera.flashMode = .on
camera.whiteBalanceMode = .continuousAutoWhiteBalance
if (cameraSession.canAddInput(deviceInput) == true) {
cameraSession.addInput(deviceInput)
}
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) :
NSNumber(value: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange as UInt32)] as [String : Any]
dataOutput.alwaysDiscardsLateVideoFrames = true
if (cameraSession.canAddOutput(dataOutput) == true) {
cameraSession.addOutput(dataOutput)
}
let preset: AVCaptureSession.Preset = .hd4K3840x2160
cameraSession.sessionPreset = preset
cameraSession.commitConfiguration()
camera.unlockForConfiguration()
let queue = DispatchQueue(label: "blah.blah.blah")
dataOutput.setSampleBufferDelegate(self, queue: queue)
} catch let error as NSError {
NSLog("\(error.localizedDescription)")
}
}
}
And here's a code snippet written in Objective-C setting min and max fps:
myCamera = NULL;
if ( NULL != myCamera ) {
if ( [ myCamera lockForConfiguration: NULL ] ) {
[ myCamera setActiveVideoMinFrameDuration: CMTimeMake( 1, 12 ) ];
[ myCamera setActiveVideoMaxFrameDuration: CMTimeMake( 1, 25 ) ];
[ myCamera unlockForConfiguration ];
}
}
return ( NULL != myCamera );

Adjusting screen contrast programmatically in Cocoa application

I'm trying to adjust the screen contrast in Objective C for a Cocoa application using kIODisplayContrastKey. I saw a post about adjusting screen brightness here:
Programmatically change Mac display brightness
- (void) setBrightnessTo: (float) level
{
io_iterator_t iterator;
kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IODisplayConnect"),
&iterator);
// If we were successful
if (result == kIOReturnSuccess)
{
io_object_t service;
while ((service = IOIteratorNext(iterator)))
{
IODisplaySetFloatParameter(service, kNilOptions, CFSTR(kIODisplayBrightnessKey), level);
// Let the object go
IOObjectRelease(service);
return;
}
}
}
Code by Alex King from the link above.
And that code worked. So I tried to do the same for contrast by using a different key (kIODisplayContrastKey), but that doesn't seem to work. Does anybody have an idea if that's possible?
I'm using 10.9.3

NSFetchedResultsController with relationship not updating

Let's say I have two entities, Employee and Department. A department has a to-many relationship with an employee, many employees can be in each department but each employee only belongs to one department. I want to display all of the employees in a table view sorted by data that is a property of the department they belong to using an NSFetchedResultsController. The problem is that I want my table to update when a department object receives changes just like it does if the regular properties of employee change, but the NSFetchedResultsController doesn't seem to track related objects. I've gotten passed this issue partially by doing the following:
for (Employee* employee in department.employees) {
[employee willChangeValueForKey:#"dept"];
}
/* Make Changes to department object */
for (Employee* employee in department.employees) {
[employee didChangeValueForKey:#"dept"];
}
This is obviously not ideal but it does cause the employee based FRC delegate method didChangeObject to get called. The real problem I have left now is in the sorting a FRC that is tracking employee objects:
NSEntityDescription *employee = [NSEntityDescription entityForName:#"Employee" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"department.someProperty" ascending:NO];
This works great and sorts the employees correctly the first time it's called, the problem is that when I make changes to some property of a department that should change the sorting of my employee table, nothing happens. Is there any nice way to have my employee FRC track changes in a relationship? Particularly I just need some way to have it update the sorting when the sort is based on a related property. I've looked through some similar questions but wasn't able to find a satisfactory solution.
The NSFetchedResultsController is only designed to watch one entity at a time. Your setup, while it makes sense, it a bit beyond what the NSFetchedResultsController is currently capable of watching on its own.
My recommendation would be to set up your own watcher. You can base it off the ZSContextWatcher I have set up on GitHub, or you can make it even more straightforward.
Basically, you want to watch for NSManagedObjectContextDidSaveNotification postings and then reload your table when one fire that contains your department entity.
I would also recommend filing a rdar with Apple and asking for the NSFetchedResultsController to be improved.
Swift
Because the NSFetchedResultsController is designed for one entity at a time, you have to listen to the NSManagedObjectContextObjectsDidChangeNotification in order to be notified about all entity relationship changes.
Here is an example:
//UITableViewController
//...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChangeHandler(notification:)), name: .NSManagedObjectContextObjectsDidChange, object: mainManagedContext)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .NSManagedObjectContextObjectsDidChange, object: mainManagedContext)
}
#objc fileprivate func managedObjectsDidChangeHandler(notification: NSNotification) {
tableView.reloadData()
}
//...
This is a known limitation of NSFetchedResultsController: it only monitors the changes of you entity's properties, not of its relationships' properties. But your use case is totally valid, and here is how to get over it.
Working Principle
After navigating a lot of possible solutions, now I just create two NSFetchedResultsController: the initial one (in your case, Employee), and another one to monitor the entities in the said relationship (Department). Then, when a Department instance is updated in the way it should update your Employee FRC, I just fake a change of the instances of affiliated Employee using the NSFetchedResultsControllerDelegate protocol. Note that the monitored Department property must be part of the NSSortDescriptors of its NSFetchedResultsController for this to work.
Example code
In your example if would work this way:
In your view controller:
var employeesFetchedResultsController:NSFetchedResultsController!
var departmentsFetchedResultsController:NSFetchedResultsController!
Also make sure you declare conformance to NSFetchedResultsControllerDelegate in the class declaration.
In viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
// [...]
employeesFetchedResultsController = newEmployeesFetchedResultsController()
departmentsFetchedResultsController = newDepartmentsFetchedResultsController()
// [...]
}
In the departmentsFetchedResultsController creation:
func newDepartmentsFetchedResultsController() {
// [specify predicate, fetchRequest, etc. as usual ]
let monitoredPropertySortDescriptor:NSSortDescriptor = NSSortDescriptor(key: "monitored_property", ascending: true)
request.sortDescriptors = [monitoredPropertySortDescriptor]
// continue with performFetch, etc
}
In the NSFetchedResultsControllerDelegate methods:
That's where the magic operates:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if controller == departmentsFetchedResultsController {
switch(type){
case .insert, .delete, .update:
managedObjectContext.performAndWait {
let department = anObject as! Department
for employee in (department.employees ?? []) {
// we fake modifying each Employee so the FRC will refresh itself.
let employee = employee as! Employee // pure type casting
employee.department = department
}
}
break
default:
break
}
}
}
This fake update of the department of each impacted employee will trigger the proper update of employeesFetchedResultsController as expected.
SwiftUI
I haven't seen posts that directly addressed this issue in SwiftUI. After trying solutions outlined in many posts, and trying to avoid writing custom controllers, the single factor that made it work in SwiftUI—which was part of the previous post from harrouet (thank you!)—is:
Make use of a FetchRequest on Employee.
If you care about, say, the employee count per department, the fake relationship updates did not make a difference in SwiftUI. Neither did any willChangeValue or didChangeValue statements. Actually, willChangeValue caused crashes on my end.
Here's a setup that worked:
import CoreData
struct SomeView: View {
#FetchRequest var departments: FetchedResults<Department>
// The following is only used to capture department relationship changes
#FetchRequest var employees: FetchedResults<Employee>
var body: some View {
List {
ForEach(departments) { department in
DepartmentView(department: department,
// Required: pass some dependency on employees to trigger view updates
totalEmployeeCount: employees.count)
}
}
//.id(employees.count) does not trigger view updates
}
}
struct DepartmentView: View {
var department: Department
// Not used, but necessary for the department view to be refreshed upon employee updates
var totalEmployeeCount: Int
var body: some View {
// The department's employee count will be refreshed when, say,
// a new employee is created and added to the department
Text("\(department) has \(department.employees.count) employee(s)")
}
}
I don't know if this fixes all the potential issues with CoreData relationships not propagating to views, and it may present efficiency issues if the number of employees is very large, but it worked for me.
An alternative that also worked for establishing the right employee count without grabbing all employees—which may address the efficiency issue of the above code snippet—is to create a view dependency on a NSFetchRequestResultType.countResultType type of FetchRequest:
// Somewhere in a DataManager:
import CoreData
final class DataManager {
static let shared = DataManager()
let persistenceController: PersistenceController
let context: NSManagedObjectContext!
init(persistenceController: PersistenceController = .shared) {
self.persistenceController = persistenceController
self.context = persistenceController.container.viewContext
}
func employeeCount() -> Int {
var count: Int = 0
context.performAndWait {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Employee")
fetchRequest.predicate = nil
fetchRequest.resultType = NSFetchRequestResultType.countResultType
do {
count = try context.count(for: fetchRequest)
} catch {
fatalError("error \(error)")
}
}
return count
}
}
And the main View becomes:
import CoreData
struct SomeView: View {
#FetchRequest var departments: FetchedResults<Department>
// No #FetchRequest for all employees
var dataManager = DataManager.shared
var body: some View {
List {
ForEach(departments) { department in
DepartmentView(department: department,
// Required: pass some dependency on employees to trigger view updates
totalEmployeeCount: dataManager.employeeCount())
}
}
//.id(dataManager.employeeCount()) does not trigger view updates
}
}
// DepartmentView stays the same.
Again, this may not resolve all possible relationship dependencies, but it gives hope that view updates can be prompted by considering various types of FetchRequest dependencies within the SwiftUI views.
A note that DataManager needs NOT be an ObservableObject being observed in the View for this to work.