I'm trying to work with the CoreBluetooth module, but part of my code doesn't run - swift

I'm attempting to use my Mac as a bluetooth peripheral using the CoreBluetooth module. I've looked at the docs and various examples of it, but my code still doesn't work. I put print statements throughout it, and for some reason the only print statements which show up in my output are "1" and "1.5". This is in an XCode 11.6 playground running on Mac OS Catalina 10.15.6, if that helps.
import AppKit
import PlaygroundSupport
import CoreBluetooth
print("1")
let myCBUUID = CBUUID(string:"2FC62EDD-EFED-457A-A88E-6E9BC1B8D7AF")
let properties: CBCharacteristicProperties = [.notify, .read, .write]
let permissions: CBAttributePermissions = [.readable, .writeable]
let characteristic = CBMutableCharacteristic(type:myCBUUID,properties: properties, value:nil, permissions: permissions)
let myCBService = CBMutableService(type:myCBUUID,primary: true)
myCBService.characteristics = [characteristic]
print("1.5")
class Peripheral: CBPeripheralManager, CBPeripheralManagerDelegate
{
var peripheralManager : CBPeripheralManager!
convenience init(delegate: CBPeripheralManagerDelegate?,
queue: DispatchQueue?){
print("1.75")
self.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: [CBPeripheralManagerOptionShowPowerAlertKey: true])
peripheralManager.add(myCBService)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
{
print("2")
print("state: \(peripheral.state)")
}
func Advertise(){
print("hello its me")
peripheralManager.startAdvertising([CBAdvertisementDataLocalNameKey : "My Peripheral", CBAdvertisementDataLocalNameKey : myCBUUID])
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: NSError?)
{
if let error = error
{
print("Failed due to error: \(error)")
return
}
print("Success")
}
}
let myPeripheral = Peripheral.init()
myPeripheral.Advertise()
(this is my first time posting, sorry if i messed anything up and feel free to make suggestions in the comments if i left something out)

Related

IKScannerDeviceViewDelegate never called

Probably a dumb question but I cannot figure how make it work. When I press scan button in my IKScannerDeviceView scan works but its delegate never gets called.
I put a breakpoint on line
print("Did scan to: \(url.path)")
Scan file appears in selected folder without problems but it never stops on breakpoint.
Am I missing something?
Just for the sake of completeness I'm using Xcode 12.2 on macOS 11.0.1
Here is my code:
import Cocoa
import ImageCaptureCore
import Quartz
class ViewController: NSViewController {
#IBOutlet weak var scannerView: IKScannerDeviceView!
var deviceBrowser:ICDeviceBrowser!
override func viewDidLoad() {
super.viewDidLoad()
self.scannerView.delegate = self
self.scannerView.mode = .advanced
self.scannerView.transferMode = .fileBased
self.deviceBrowser = ICDeviceBrowser()
self.deviceBrowser.delegate = self
self.deviceBrowser.browsedDeviceTypeMask = ICDeviceTypeMask(rawValue:
ICDeviceLocationTypeMask.local.rawValue |
ICDeviceLocationTypeMask.shared.rawValue |
ICDeviceLocationTypeMask.bonjour.rawValue |
ICDeviceLocationTypeMask.remote.rawValue |
ICDeviceLocationTypeMask.bluetooth.rawValue |
ICDeviceTypeMask.scanner.rawValue)!
self.deviceBrowser.start()
}
}
extension ViewController : IKScannerDeviceViewDelegate {
func scannerDeviceView(_ scannerDeviceView: IKScannerDeviceView!, didScanTo url: URL!, error: Error!) {
print("Did scan to: \(url.path)")
}
}
extension ViewController: ICDeviceBrowserDelegate {
func deviceBrowser(_ browser: ICDeviceBrowser, didAdd device: ICDevice, moreComing: Bool) {
if (device.type.rawValue & ICDeviceTypeMask.scanner.rawValue) == ICDeviceType.scanner.rawValue {
self.scannerView.scannerDevice = (device as! ICScannerDevice)
}
}
func deviceBrowser(_ browser: ICDeviceBrowser, didRemove device: ICDevice, moreGoing: Bool) {
device.requestCloseSession()
}
func didRemoveDevice(device: ICDevice) {
device.requestCloseSession()
}
func device(device: ICDevice, didEncounterError error: NSError?) {
print("Error")
print(error?.description ?? "----")
}
}
Apparently the following methods are not called:
func scannerDeviceView(IKScannerDeviceView!, didScanTo: ICScannerBandData!, scanInfo: [AnyHashable : Any]!, error: Error!)
func scannerDeviceView(IKScannerDeviceView!, didScanTo: URL!, error: Error!)
Implement the following method instead:
func scannerDeviceView(IKScannerDeviceView!, didScanTo: URL!, fileData: Data!, error: Error!)

How to pass potential error between methods

I'm trying to wrap Core Bluetooth Peripheral methods for use in React Native. It's a counterpart for already finished android code, so the API is set.
When I'm calling CBPeripheralManager.addService, I need to fulfill or reject a promise, handed from the javascript side.
The problem is, Core Bluetooth doesn't offer a callback for the method, it seems to expect private func peripheralManager(_ peripheral: CBPeripheralManager, didAddService service: CBService, error: Error?)
I'm new to iOS and Swift so this behavior seems strange to me. Any ideas how can I wrap the function so I can handle the error reporting properly?
Thanks
class BLE: NSObject, CBPeripheralManagerDelegate {
var advertising: Bool = false
var servicesMap = Dictionary<String, CBMutableService>()
var manager: CBPeripheralManager!
override init() {
super.init()
manager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
}
func addService(promise, serviceUUID) {
let serviceUUID = CBUUID(string: uuid)
let service = CBMutableService(type: serviceUUID, primary: true)
manager.add(service)
}
private func peripheralManager(_ peripheral: CBPeripheralManager, didAddService service: CBService, error: Error?) {
if let error = error {
// this should reject the addService promise
return
}
// this should fulfill the promise
}
}
It's unclear what the type of promise is, but you'll need to store it somewhere, and then fulfill it later. For example, you might add a property:
var pendingServices: [CBUUID: Promise] = [:]
(I don't know what you're promise type really is here)
Then you'd store it in addService:
assert(pendingServices[serviceUUID] == nil)
pendingServices[serviceUUID] = promise
And later in (the correct; see my comment) delegate method, you'd deal with it:
if let promise = pendingServices.removeValue(forKey: service.uuid) {
promise.fulfill() // Or whatever you do with it
}

ReplayKit: RPScreenRecorder.shared().startCapture() NOT WORKING

ReplayKit has really been frustrating me recently. For some reason
RPScreenRecorder.shared().startCapture(handler: { (sample, bufferType, error) in
does not actually work when I call it because I have a print() statement inside it and it is never called.
My code in the ViewController is:
import UIKit
import AVFoundation
import SpriteKit
import ReplayKit
import AVKit
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, RPPreviewViewControllerDelegate {
var assetWriter:AVAssetWriter!
var videoInput:AVAssetWriterInput!
func startRecording(withFileName fileName: String) {
if #available(iOS 11.0, *)
{
assetWriter = try! AVAssetWriter(outputURL: fileURL, fileType:
AVFileType.mp4)
let videoOutputSettings: Dictionary<String, Any> = [
AVVideoCodecKey : AVVideoCodecType.h264,
AVVideoWidthKey : UIScreen.main.bounds.size.width,
AVVideoHeightKey : UIScreen.main.bounds.size.height
];
videoInput = AVAssetWriterInput (mediaType: AVMediaType.video, outputSettings: videoOutputSettings)
videoInput.expectsMediaDataInRealTime = true
assetWriter.add(videoInput)
print("HERE")
RPScreenRecorder.shared().startCapture(handler: { (sample, bufferType, error) in
print("RECORDING")
}
}
}
func stopRecording(handler: #escaping (Error?) -> Void)
{
if #available(iOS 11.0, *)
{
RPScreenRecorder.shared().stopCapture
{ (error) in
handler(error)
self.assetWriter.finishWriting
{
print("STOPPED")
}
}
}
}
"HERE" is printed, but not "RECORDING"
[p.s. sorry for bad formatting in code, I'm sure you'll understand :)]
I have also tried a different method:
let recorder = RPScreenRecorder.shared()
recorder.startRecording{ [unowned self] (error) in
guard error == nil else {
print("There was an error starting the recording.")
return
}
print("Started Recording Successfully")
}
and to stop the recording...
recorder.stopRecording { [unowned self] (preview, error) in
print("Stopped recording")
guard preview != nil else {
print("Preview controller is not available.")
return
}
onGoingScene = true
preview?.previewControllerDelegate = self
self.present(preview!, animated: true, completion: nil)
}
This method does not stop when I call the recorder.stopRecording() function, "Stopped recording" is never called.
Can someone please help me because this is really frustrating me, how can you PROPERLY use ReplayKit to record your screen in iOS 11? I have searched all over the internet and none of the methods work for me, I don't why. P.S. I have the necessary permission keys in my Info.plist.
Thanks
A huge reminder that ReplayKit doesn't work in simulator. I wasted hours on the exact same issue until realized that ReplayKit will never trigger startCapture handler because it never records in simulator.
Well there are quite few possible causes for this issue.
Some of them are here:
Your Replay kit Shared Recorder might be crashed, For that you can restart your device and check again.
There might be printable issue in your replay kit. For that kindly conform to the RPScreenRecorderDelegateProtocol and add Recording Changes
screenRecorder:didStopRecordingWithPreviewViewController:error:
method to your class and check if any error shows up in this method.

Alamofire background working on simulator but not on device

As the title mentions I've set up a backgroundURL with Alamofire. It works like a charm in simulator but on my device doesn't. I'm sure I'm missing something here since I'm not that experienced with URL.
Here's the code I have so far:
class NetworkManager {
static let shared = NetworkManager()
private lazy var backgroundManager: Alamofire.SessionManager = {
let bundleIdentifier = MyStruct.identifier
return Alamofire.SessionManager(configuration: URLSessionConfiguration.background(withIdentifier: bundleIdentifier))
}()
var backgroundCompletionHandler: (() -> Void)? {
get{
return backgroundManager.backgroundCompletionHandler
}
set{
backgroundManager.backgroundCompletionHandler = newValue
}
}
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: #escaping () -> Void) {
NetworkManager.shared.backgroundCompletionHandler = completionHandler
}
In my ViewController:
func populateArrays(){
Alamofire.request("http://www.aps.anl.gov/Accelerator_Systems_Division/Accelerator_Operations_Physics/sddsStatus/mainStatus.sdds.gz").responseData { response in
switch response.result{
case .success:
print("Validation Successful")
case .failure(let error):
print(error.localizedDescription)
}
if let data = response.result.value{
Solved it. For anyone else that has this problem you need to add the following code to your appDelegate.
func applicationDidEnterBackground(_ application: UIApplication) {
var bgTask = 0
var app = UIApplication.shared
bgTask = app.beginBackgroundTask(expirationHandler: {() -> Void in
app.endBackgroundTask(bgTask)
})
It seems to me that you are not using the background manager you've created. Instead of
Alamofire.request("http://www.aps.anl.gov...")
which calls the default (not background) session manager, you should use:
backgroundManager.request("http://www.aps.anl.gov...")
Which Jon Shier mentioned in the comments by the way.

How can I detect is the Bluetooth of the user's iPhone Off or On?

I am trying to detect is the Bluetooth of the user's iPhone On or Off. If it is Off I want to send a notification to the user to turn it On.
So far I have done this:
import CoreBluetooth
class ViewController: UIViewController, CLLocationManagerDelegate,AVCaptureMetadataOutputObjectsDelegate,CBManager {
var myBTManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
}
func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!) {
print(#function)
if peripheral.state == CBManagerState.poweredOn {
print("Broadcasting...")
// myBTManager!.startAdvertising(_broadcastBeaconDict)
} else if peripheral.state == CBManagerState.poweredOff {
print("Stopped")
myBTManager!.stopAdvertising()
} else if peripheral.state == CBManagerState.unsupported {
print("Unsupported")
} else if peripheral.state == CBManagerState.unauthorized {
print("This option is not allowed by your application")
}
}
But as You can see from the picture, something is wrong.
Would you please help me how to fix this issue, I am new to swift and CoreBluetooth technology. I am also using Reachability for detecting the Wi-Fi connection, so if it also works for Bluetooth, I would prefer to use Reachability then.
You should be implementing the protocol CBPeripheralManagerDelegate, so replace CBManager in your class definition line with CBPeripheralManagerDelegate.
In Swift 3, the signature of peripheralManagerDidUpdateState is now:
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
You can't initialize the CBPeripheralManager at class creation time since self is only available after the class has been initialized. Instead, make your property:
var myBTManager: CBPeripheralManager?
and initialize it in viewDidLoad:
override func viewDidLoad() {
...
myBTManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
...
}
You can use the CBCentralMangerDelegate method:
public func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
if central.state == .poweredOn {
//Bluetooth is on
} else if central.state == .poweredOff {
//Bluetooth is off
}
}