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!)
Related
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)
I have this function in the Watchkit Extension's InterfaceController...
func createAndSendCSV(data: Array<String>) {
var csvText = "time, data\n"
let fileName = "\(startTime).csv"
let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
for row in data {
csvText += row
}
do {
try csvText.write(to: path!, atomically: true, encoding: String.Encoding.utf8)
WCSession.default.transferFile(path!, metadata: ["time": startTime])
} catch {
print("Failed to create file")
print("\(error)")
}
}
that is called when the user stops the app on the watch. I know the function is triggered because I can add a print statement in the do catch showing that it was successful.
On ViewController I have the below which isn't much beyond making sure the session is activated and then trying to work with the file that was sent. I don't see any errors but I also don't see my print test message.
I'm trying to use a CSV file to transfer data as I can't send via sendMessage due to size of message. Anyone know why it might not be working?
import UIKit
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if (WCSession.isSupported()) {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func session(_ session: WCSession, didReceive file: WCSessionFile) {
DispatchQueue.main.async() {
do {
print("test")
let contents = try String(contentsOf: file.fileURL, encoding: .utf8)
self.sendData(data: contents)
} catch {
print("File Read Error for file \(String(describing: file.metadata))")
}
}
}
func sendData(data: String) {
print(data)
}
// Not used but needs to exist
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
}
func sessionDidBecomeInactive(_ session: WCSession) {
}
func sessionDidDeactivate(_ session: WCSession) {
}
}
The method you are using transferFile(_:metadata:) runs asynchronously and gets throttled by the system to accommodate Apple Watch’s limits on performance and power. You should be able to check the status of the
outstandingFileTransfers property to ensure you are setting up the transfer correctly, but beyond that you may need to rethink how you are sharing data between the two devices.
I have wrote a very basic Xcode project that contains 3 targets:
- iOS target
- WatchKit app
- WatchKit extension
First of all, i do not understand why Xcode creates a second target (extension) for WatchKit app ? It seems that WatchKit app contains storyboard, and WatchKit extension contains swift code (controllers). Is there a particular reason for Xcode to design and split 2 targets instead of one single ?
Look at this very basic piece of code:
iOS controller:
override func viewDidLoad()
{
super.viewDidLoad()
if WCSession.isSupported()
{
let session = WCSession.default()
session.delegate = self
session.activate()
}
}
#IBAction func on_btn_tap(_ sender: Any)
{
if WCSession.isSupported()
{
let session = WCSession.default()
session.sendMessage(["mykey": "myvalue"], replyHandler: { (response) -> Void in
NSLog("OK")
}, errorHandler: { (error) -> Void in
NSLog("Error)
})
}
}
On watch extension (InterfaceController.swift):
override func awake(withContext context: Any?)
{
super.awake(withContext: context)
if WCSession.isSupported()
{
let session = WCSession.default()
session.delegate = self
session.activate()
}
}
extension InterfaceController: WCSessionDelegate
{
func session(_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?)
{
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void)
{
self.btn.setBackgroundColor(UIColor.yellow)
}
}
As you certainly understand, i have a button on my iOS App. When i tap on this button, i send a message to Watch App and this app will change a button color.
There is a delay of about 5-6 seconds between the button tap on the iPhone and the color change. Do you know why ?
In the other communication side (watch to iPhone), it is worst (10-15 seconds)
Thanks
Since you are updating your UI you need to wrap it in a DispatchQueue, like this:
DispatchQueue.main.async {
self.btn.setBackgroundColor(UIColor.yellow)
}
These delegate callbacks are not on the main thread and you should never update your UI from any other thread than the main thread. Wrapping it like this results in much faster updating of your UI and safer code.
I'm working on programming with Swift for the first time, and in doing so I'm following along with this tutorial. Unfortunately it looks like the tutorial is a little outdated and most of the code is throwing Buildtime errors. The most reoccurring error is the NSURLSession has been renamed to URLSession. I've tried letting Swift fix it, but in many cases it just starts throwing warnings.I'm also getting a Value type HomeModel has no member'parseJSON' error as well as a NSDat is not implicitly convertible to data error. From what I can tell, it looks like the NSURL is no longer used, but I'm not sure about the other two. Seeing how this is the first Swift project I've worked on, I'm not sure how to fix these. Can someone provide some insight on how to fix these mistakes?
here is the code:
import Foundation
protocol HomeModelProtocal: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, NSURLSessionDataDelegate {
//properties
weak var delegate: HomeModelProtocal!
var data : NSMutableData = NSMutableData()
let urlPath: String = "http://testurl.com/service.php" //this will be changed to the path where service.php lives
func downloadItems() {
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data);
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
}
Several basic types have dropped the "NS" prefix in Swift 3.0. Earlier in swift 2.2, we used to have NSUserDefaults, NSURLSession, NSFileManager etc. Now, most of them dropped their prefix "NS" and changed to UserDefaults, URLSession, FileManager etc.
Your code contains a lot of types with 'NS' prefix. By simply removing it, your code can be converted to Swift 3. Your converted code looks like as shown below:
protocol HomeModelProtocal: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, URLSessionDataDelegate {
//properties
weak var delegate: HomeModelProtocal!
var data : Data = Data()
let urlPath: String = "http://testurl.com/service.php" //this will be changed to the path where service.php lives
func downloadItems() {
let url: URL = URL(string: urlPath)!
var session: URLSession!
let configuration = URLSessionConfiguration.default
session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTask(with: url)
task.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.data.append(data);
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON() // This class doesn't have a function parseJSON(). So, it's giving you an error like this
}
}
}
Also, I don't see any function called parseJSON() in your class. I believe you have to add it.
I'd like to make an app that is able to discover and connect to peers on the local network, so i've decided to implement it in swift, using the bonjour framework.
However, i can't make Bonjour work using Swift, and I can't figure out why. Here is the code I use to test this service :
import Foundation
let BM_DOMAIN = "local"
let BM_TYPE = "_helloworld._tcp."
let BM_NAME = "hello"
let BM_PORT : CInt = 6543
/// Netservice
let nsns = NSNetService(domain: BM_DOMAIN,
type: BM_TYPE, name: BM_NAME, port: BM_PORT)
let nsnsdel = BMNSDelegate() //see bellow
nsns.delegate = nsnsdel
nsns.publish()
/// Net service browser.
let nsb = NSNetServiceBrowser()
let nsbdel = BMBrowserDelegate() //see bellow
nsb.delegate = nsbdel
nsb.searchForServicesOfType(BM_TYPE, inDomain: BM_DOMAIN)
println("press enter")
// this prevents the app from quitting instantly.
NSFileHandle.fileHandleWithStandardInput().availableData
The delegates are glue code that simply prints every call to the console.
class BMNSDelegate : NSObject, NSNetServiceDelegate {
func netServiceWillPublish(sender: NSNetService!) {
println("netServiceWillPublish:sender");
}
// .....and so on for the 8 other methods.....
}
class BMBrowserDelegate : NSObject, NSNetServiceBrowserDelegate {
func netServiceBrowserWillSearch(aNetServiceBrowser: NSNetServiceBrowser!){
println("netServiceBrowserWillSearch")
}
// .....and so on for the 6 other methods.....
}
Here is the output of this sample code :
netServiceWillPublish:sender
netServiceBrowserWillSearch
press enter
If I use Bonjour browser, I can see that the service is correctly published. However the callbacks in both delegates are not called, beside the **WillPublish ones :-(
After intense debugging (and reading on stackoverflow), I can't figure why it does not work. Any ideas ?
(I'm using Mac OS X 10.9.3, and xcode 6.0 beta build 6A215l)
Without your full code, it may be difficult to know, for sure, what your issue is. I suspect that you declared your variables/constants local to a function. When they went out of scope, the references to the service went out of scope. That's why you tried a blocking call requesting input from STDIN (to keep things stuck there). According to Apple documentation, netService and netServiceBrowser both implicitly associate with the default run loop, so you don't need to explicitly do that either. Explicitly associating with a run loop causes the program to get stuck, which is not what you want. This code creates the following output
netServiceWillPublish:<NSNetService 0x14522e00> local _helloworld._tcp. hello
netServiceBrowserWillSearch
netServiceDidPublish:<NSNetService 0x14522e00> local. _helloworld._tcp. hello
netServiceDidFindService
and without being blocked or in a run loop that prevents the program from proceeding normally. In AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var nsns:NSNetService?
var nsnsdel:BMNSDelegate?
var nsb:NSNetServiceBrowser?
var nsbdel:BMBrowserDelegate?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let BM_DOMAIN = "local"
let BM_TYPE = "_helloworld._tcp."
let BM_NAME = "hello"
let BM_PORT : CInt = 6543
/// Netservice
nsns = NSNetService(domain: BM_DOMAIN,
type: BM_TYPE, name: BM_NAME, port: BM_PORT)
nsnsdel = BMNSDelegate() //see bellow
nsns?.delegate = nsnsdel
nsns?.publish()
/// Net service browser.
nsb = NSNetServiceBrowser()
nsbdel = BMBrowserDelegate() //see bellow
nsb?.delegate = nsbdel
nsb?.searchForServicesOfType(BM_TYPE, inDomain: BM_DOMAIN)
//println("press enter")
// this prevents the app from quitting instantly.
// NSRunLoop.currentRunLoop().run()
// NSFileHandle.fileHandleWithStandardInput().availableData
return true
}
and the delegate callbacks elsewhere...
class BMNSDelegate : NSObject, NSNetServiceDelegate {
func netServiceWillPublish(sender: NSNetService!) {
println("netServiceWillPublish:\(sender)");
}
func netService(sender: NSNetService, didNotPublish errorDict: [NSObject : AnyObject]) {
println("didNotPublish:\(sender)");
}
func netServiceDidPublish(sender: NSNetService) {
println("netServiceDidPublish:\(sender)");
}
func netServiceWillResolve(sender: NSNetService) {
println("netServiceWillResolve:\(sender)");
}
func netService(sender: NSNetService, didNotResolve errorDict: [NSObject : AnyObject]) {
println("netServiceDidNotResolve:\(sender)");
}
func netServiceDidResolveAddress(sender: NSNetService) {
println("netServiceDidResolve:\(sender)");
}
func netService(sender: NSNetService, didUpdateTXTRecordData data: NSData) {
println("netServiceDidUpdateTXTRecordData:\(sender)");
}
func netServiceDidStop(sender: NSNetService) {
println("netServiceDidStopService:\(sender)");
}
func netService(sender: NSNetService,
didAcceptConnectionWithInputStream inputStream: NSInputStream,
outputStream stream: NSOutputStream) {
println("netServiceDidAcceptConnection:\(sender)");
}
}
class BMBrowserDelegate : NSObject, NSNetServiceBrowserDelegate {
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didFindDomain domainName: String,
moreComing moreDomainsComing: Bool) {
println("netServiceDidFindDomain")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didRemoveDomain domainName: String,
moreComing moreDomainsComing: Bool) {
println("netServiceDidRemoveDomain")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didFindService netService: NSNetService,
moreComing moreServicesComing: Bool) {
println("netServiceDidFindService")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didRemoveService netService: NSNetService,
moreComing moreServicesComing: Bool) {
println("netServiceDidRemoveService")
}
func netServiceBrowserWillSearch(aNetServiceBrowser: NSNetServiceBrowser!){
println("netServiceBrowserWillSearch")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didNotSearch errorInfo: [NSObject : AnyObject]) {
println("netServiceDidNotSearch")
}
func netServiceBrowserDidStopSearch(netServiceBrowser: NSNetServiceBrowser) {
println("netServiceDidStopSearch")
}
}
NSNetServiceBrowser needs a runloop to execute. Instead of reading from stdin, call NSRunLoop.currentRunLoop().run().