Core NFC Payload Error - ios

I would like to scan the NDEF data from my NFC-Tag (NXP NTAG213) using the Core NFC framework introduced in iOS 11.
I succeeded to read the Payload within the tag:
TNF=1, Payload Type=<54>, Payload ID=<>, Payload=<026a61e3 8193e382 93e381ab e381a1e3 81af0a>
I'd like to extract the payload part, so here's what I've tried:
print("payload.payload")
but error occurred.
Here's my source code:
import UIKit
import CoreNFC
class ViewController: UIViewController, NFCNDEFReaderSessionDelegate{
var payloadData = "HaveNoData"
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
print("エラーは発生しませんでした")
for message in messages{
for payload in message.records {
print (payload)
payloadData = String(describing: payload.payload)
}
}
print(payloadData)
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
print("Error: \(error.localizedDescription)")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let session:NFCNDEFReaderSession = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
session.begin()
}
#IBAction func launch(_ sender: Any) {
let session:NFCNDEFReaderSession = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
session.begin()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

CoreNFC does not support parsing NFC NDEF message payload which is complex. I created an open source parser VYNFCKit to parse payload. Check my tutorial https://medium.com/#vinceyuan/reading-and-parsing-nfc-tag-on-ios-11-60f4bc7a11ea

I'm not sure if you can use describing: on payload.payload here. But I believe the old-fashion String from NSData would work. (Not a daily Swift coder so not sure how it handles this these days with Swift 3/4).
In Objective-C, I can do it with:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
or in your case NSUTF8StringEncoding for the Japanese string.
Here's a sample project I made (in Objective-C).

Related

How to resolve (com.apple.ShazamKit error 202.) when using ShazamKit

I've been trying to add Shazam matching to my app using the new ShazamKit. I've used Apple's sample code found here and adapted it slightly.
import ShazamKit
import AVFAudio
import Combine
#available(iOS 15.0, *)
class ShazamMatcher: NSObject, ObservableObject, SHSessionDelegate {
// MARK: - Properties
#Published var result: SHMatch?
#Published var isRecording = false
private var isInitialSetupDone = false
private var session: SHSession?
private let audioEngine = AVAudioEngine()
// MARK: - Actions
func match() throws {
result = nil
session = SHSession()
session?.delegate = self
try doInitialSetupIfNeeded()
AVAudioSession.sharedInstance().requestRecordPermission { [weak self] success in
guard success, let self = self else {
return
}
try? self.audioEngine.start()
self.isRecording = true
}
}
func stopMatching() {
audioEngine.stop()
isRecording = false
}
// MARK: - Setup
private func doInitialSetupIfNeeded() throws {
guard !isInitialSetupDone else {
return
}
let audioFormat = AVAudioFormat(
standardFormatWithSampleRate: audioEngine.inputNode.outputFormat(forBus: 0).sampleRate,
channels: 1
)
audioEngine.inputNode.installTap(onBus: 0, bufferSize: 2048, format: audioFormat) { [weak session] buffer, audioTime in
session?.matchStreamingBuffer(buffer, at: audioTime)
}
try AVAudioSession.sharedInstance().setCategory(.record)
isInitialSetupDone = true
}
// MARK: - SHSessionDelegate
func session(_ session: SHSession, didFind match: SHMatch) {
// Handle match here
}
func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) {
// Handle error here
}
}
However, when calling match(), the delegate eventually reports an error The operation couldn’t be completed. (com.apple.ShazamKit error 202.)
I've added a new key using my bundle identifier for the ShazamKit services and downloaded the .p8 file. Do I need this file and if so, how?
Has anybody been able to resolve this error?
I've found a solution. First, apparently the inter-app audio entitlement has to be enabled.
Second, it seems like you need a SHSignatureGenerator as well (I though it would be enough to call matchStreamingBuffer
Here's code that works:
https://github.com/heysaik/ShazamKit-Demo/blob/main/Shazam/ViewController.swift

How to execute a function from second ViewController after popViewController?

I am an Android developer trying to port my app to iOS and this is my first time working with Swift and Xcode, or anything Mac in general. The app is to scan for a QR code, if the QR code is invalid then I'd like it to go back to the previous screen and display a toast. I've already made a function that displays the toast stating the QR code was invalid.
func found(code: String) {
print(code)
// convert JSON to object
do {
let qrMessage = try JSONDecoder().decode(QrMessage.self, from: code.data(using: .utf8)!)
print("QrMessage object - device name: " + qrMessage.pcName)
}
catch let error {
print(error)
_ = navigationController?.popViewController(animated: true)
// call ShowInvalidQrToast() from the new VC that is now on top of the ViewController stack
}
EDIT:
I managed to figure out a solution using the NotificationCenter from the first answer in this thread: Calling function from another ViewController in swift. More info in my answer below. If you think there is a better way of doing this, please post your own answer.
My solution using NotificationCenter:
In ConnectViewController
override func viewDidLoad() {
super.viewDidLoad()
...
NotificationCenter.default.addObserver(self, selector: #selector(showInvalidQrCodeToast(_:)), name: Notification.Name(rawValue: "showInvalidQrCodeToast"), object: nil)
}
#objc func showInvalidQrCodeToast(_ notification: Notification) {
self.view.makeToast("Invalid QR code")
}
In ScannerViewController
func found(code: String) {
print(code)
// convert JSON to object
do {
let qrMessage = try JSONDecoder().decode(QrMessage.self, from: code.data(using: .utf8)!)
print("QrMessage object - device name: " + qrMessage.pcName)
}
catch let error {
print(error)
_ = navigationController?.popViewController(animated: true)
NotificationCenter.default.post(name: Notification.Name(rawValue: "showInvalidQrCodeToast"), object: nil)
}

How to fix "websocket is disconnected: Invalid HTTP upgrade" error using Starscream

I want to connect an iPhone to a web server running on my Macbook using MAMP and Starscream Library https://github.com/daltoniam/Starscream
Installed using Cocoapods and set up the web server with this URL:localhost:8888
But I'm having a lot of headaches because the code doesn't work... I followed all the guides online, the official documentation and so on but none of them helped me...
Whenever I run the code, the console shows this message: websocket is disconnected: Invalid HTTP upgrade
So maybe it's my server that's broken? I tried using instead ws://echo.websocket.org/ and it worked!!! So I thought... Because it finally showed me: websocket is connected
But at the same time it wasn't!!! In fact the method socket.isConnected gave me false... After that if I called socket.disconnect() it doesn't
work either, no messages show up.
import UIKit
import Starscream
class ViewController: UIViewController, WebSocketDelegate {
let socket = WebSocket(url: URL(string:"ws://echo.websocket.org/")!)//websocket is connected
/*
let socket = WebSocket(url: URL(string: "ws://localhost:8888/")!) //websocket is disconnected: Invalid HTTP upgrade
*/
override func viewDidLoad() {
super.viewDidLoad()
socket.delegate = self
socket.connect()
if socket.isConnected {
print("hi1") //it doesn't get printed
}
if !socket.isConnected {
print("hi2") //it gets printed
}
}
func websocketDidConnect(socket: WebSocketClient) {
print("websocket is connected")
}
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
if let e = error as? WSError {
print("websocket is disconnected: \(e.message)")
} else if let e = error {
print("websocket is disconnected: \(e.localizedDescription)")
} else {
print("websocket disconnected")
}
}
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
print("Received text: \(text)")
}
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
print("Received data: \(data.count)")
}
}
So I finally understood the problem: Apache doesn't support Web Sockets by default... that's why I got the error message. Then the code works with "ws://echo.websocket.org/". You just have to put all socket.x functions outside viewDidLoad() to make them work, for example socket.disconnect in a Disconnect Button, socket.write in a sendButton etc.
For example:
let socket = WebSocket(url: URL(string: "ws://echo.websocket.org/")!)
override func viewDidLoad() {
super.viewDidLoad()
socket.delegate = self
socket.connect()
}
#IBAction func sendButtonPressed(_ sender: UIButton) {
socket.write(string: textField.text!)
}

How to transfer a UIImage using Watch Connectivity

How can I transfer an UIImage over WatchConnecitivity from the iPhone to the Apple Watch with no user interaction on the phone, and only loads because the watch calls for it programmatically. I need this because the image processing to create the UIImage uses logic unavailable in the Watchkit API, so it must be created from the phone. I have seem some examples of Watch Connectivity using:
func startSession() {
session?.delegate = self
session?.activateSession()
}
However, I am new to watch kit and am confused on how to use this session manager, particularly to go from the watch to the device instead of the other way around like I see in apple documentation. Can someone please provide an example of how to do this on both the watch and the phone to call for a UIImage on the phone from the watch?
To transfer files between your iPhone and your Apple Watch you should use Watch Connectivity, using WCSession to handle the communication properly. You can send an UIImage as NSData using the didReceiveMessageData delegate method of the WCSessionDelegate.
The first thing you should know is convert your UIImage to NSData and viceversa. You can use for this the following code:
If PNG images
let image = UIImage(named: "nameOfYourImage.jpg")
let data = UIImagePNGRepresentation(image)
If JPG images
let image = UIImage(named: "nameOfYourImage.jpg")
let data = UIImageJPEGRepresentation(image, 1.0)
Then you can use the WCSession to send the message like in the following way:
ViewController.swift
class ViewController: UIViewController, WCSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if WCSession.isSupported() {
WCSession.defaultSession().delegate = self
WCSession.defaultSession().activateSession()
}
let image = UIImage(named: "index.jpg")!
let data = UIImageJPEGRepresentation(image, 1.0)
WCSession.defaultSession().sendMessageData(data!, replyHandler: { (data) -> Void in
// handle the response from the device
}) { (error) -> Void in
print("error: \(error.localizedDescription)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
InterfaceController.swift
import WatchKit
import Foundation
import WatchConnectivity
class InterfaceController: WKInterfaceController, WCSessionDelegate {
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
if WCSession.isSupported() {
WCSession.defaultSession().delegate = self
WCSession.defaultSession().activateSession()
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
func session(session: WCSession, didReceiveMessageData messageData: NSData, replyHandler: (NSData) -> Void) {
guard let image = UIImage(data: messageData) else {
return
}
// throw to the main queue to upate properly
dispatch_async(dispatch_get_main_queue()) { [weak self] in
// update your UI here
}
replyHandler(messageData)
}
}
In the above code when you open the ViewController it sends the UIImage, the above example is only for learning purposes, you have to handle it in a more proper way regarding the complexity of your project.
I hope this help you.
you have to transfer your image as NSData and then decode it on watch's side. You can have a look at my blog post where I covered similar case.
http://eluss.github.io/AppleWatch_iPhone_data_transfer/
This is not the way of doing it with session as I was not aware of it back then, but maybe it will help you get the whole logic.
In InterfaceController, if you use WCSessionDelegate, remember to extend method activationDidCompleteWith:
func session(_ session: WCSession, activationDidCompleteWith
activationState: WCSessionActivationState, error: Error?) {
}

xcode 6 beta EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0) in swift

I am creating a basic iOS app that sends an HTTP "GET" request on a specified URL and printing that information into a textfield.
I receive no errors but I get a EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0) error in the operationQueue thread. I am new to iPhone development and Swift and even after doing research I'm not sure what this means.
My code is below. My view only contains a button that performs the get method on touch down and a textfield.
import UIKit
import Foundation
class ViewController: UIViewController {
#IBOutlet var textField : UITextField
#IBOutlet var button : UIButton
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func get(sender: AnyObject) {
var url : String = "http://localhost:8080/Booknds/v1/Summary/1"
var request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
//send request
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData! , error: NSError?) -> Void in
//get data from URL in dictionary form
let jsonResult : NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
// process jsonResult
if jsonResult != nil {
self.textField.text = "\(String(jsonResult.description))"
} else {
self.textField.text = "data: \(data), response: \(response), error: \(error)"// couldn't load JSON, look at error
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I had the same error message running simple code using the latest Beta. On another machine I'm running an earlier version and the same code works fine - it's in fact the same playground file, stored on Dropbox.

Resources