I have a problem to retrieve and display all the information about the QR Code in Swift 4.
I used a QR Code generator with text extension in which I added
{"number":"+33688888888","amount":"50"}
in my function to call and display information
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
{
if metadataObjects != nil && metadataObjects.count != 0
{
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if metadataObj.type == AVMetadataObject.ObjectType.qr
{
let info = HPPayMoneySuccessModel(titlePage: "Payment", imageInfo: "confirm_checked2", titleDescription: "Congratulations your transaction has been successfully completed", numberString: metadataObj.stringValue!, amountString: metadataObj.stringValue!, buttonTerminate: "OK")
let segueViewController = HPPayMoneySuccessViewController.init(nibName: "HPPayMoneySuccessViewController", bundle: nil, payMoneySuccessViewModel: info)
self.navigationController?.pushViewController(segueViewController, animated: true)
self.session.stopRunning()
}
}
}
He gets me the information as a string like {"number":"+33688888888","amount":"50"} but I just want +33688888888 in numberString and 50 in amountString
Please help me.
You need
guard let stringValue = metadataObj.stringValue else { return }
if let res = try? JSONSerialization.jsonObject(with:Data(stringValue.utf8), options: []) as? [String:String] ,let fin = res {
guard let number = fin["number"] , let amount = fin["amount"] else { return }
print(number)
print(amount)
}
OR
if let res = try? JSONDecoder().decode(Root.self, from: Data(stringValue.utf8)) {
print(res.number)
print(res.amount)
}
struct Root : Decodable {
let number,amount:String
}
Related
I have a chat app use Firestore, when a new document or -> (message), added to the Firestore my collectionView reload all the data over again and the app freezes for a second until all the data reload, I don't know much about collectionViews but I think if am not wrong this happens because of the collectionView trying to calculate the size for each cell this is the debugger message
The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fbd974b3c40>, and it is attached to <UICollectionView: 0x7fbd97930600; frame = (0 61; 414 663); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600002c5df20>; layer = <CALayer: 0x6000023f9640>; contentOffset: {-5, -5}; contentSize: {404, 8}; adjustedContentInset: {5, 5, 5, 5}; layout: <UICollectionViewFlowLayout: 0x7fbd974b3c40>; dataSource: <DELEVARE___ديليفري.DriverChat: 0x7fbd97843600>>.
2021-01-16 13:54:43.232052+0200 DELEVARE - ديليفري[51104:999159] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
this debugger message gets called repeated with every cell loaded to collectionView, Here is my collectionView How to solve UICollectionViewFlowLayoutBreakForInvalidSizes
I don't know how to insert only the new appended item to the array in collectionView, I want this just to react like a normal chat app.
I have tried this
func getMessages(){
guard let uid = Auth.auth().currentUser?.uid else {return}
let firestore = Firestore.firestore()
let doc = firestore.collection("طلبات قيد العمل").whereField("driverUid", isEqualTo: uid)
doc.getDocuments { (query, err) in
if err != nil {
print(err?.localizedDescription ?? "")
}
for document in query!.documents{
let chatDoc = document.reference.collection("المحادثات")
chatDoc.addSnapshotListener { (querysnap, err) in
if err != nil {
print(err?.localizedDescription ?? "")
}
self.messages = []
for snap in querysnap!.documents{
let data = snap.data()
guard let message = data["message"] as? String else {return}
guard let senderid = data["senderID"] as? String else {return}
guard let timestamp = data["date"] as? Timestamp else {return}
let date = Date(timeIntervalSince1970: TimeInterval(timestamp.seconds))
if senderid == Auth.auth().currentUser?.uid{
let drivermessages = driverMessages(image: UIImage(), text: message, date: date, isSender: true, driverid: senderid)
// this line of code is doing the same as collectionView.reloadData()
self.collectionView?.performBatchUpdates({
let indexPath = IndexPath(row: self.comments.count, section: 0)
self.messages.append(drivermessages)
self.collectionView?.insertItems(at: [indexPath])
}, completion: nil)
}else {
guard let message2 = data["message"] as? String else {return}
guard let senderid2 = data["senderID"] as? String else {return}
guard let timestamp2 = data["date"] as? Timestamp else {return}
guard let img = self.profileImg else {return}
let date2 = Date(timeIntervalSince1970: TimeInterval(timestamp2.seconds))
let usermessages = driverMessages(image: img, text: message2, date: date2, isSender: false, driverid: senderid2)
self.messages.append(usermessages)
self.collectionView?.performBatchUpdates({
let indexPath = IndexPath(row: self.comments.count, section: 0)
self.messages.append(usermessages)
self.collectionView?.insertItems(at: [indexPath])
}, completion: nil)
}
}
}
}
}
}
this is my code
func getMessages(){
guard let uid = Auth.auth().currentUser?.uid else {return}
let firestore = Firestore.firestore()
let doc = firestore.collection("طلبات قيد العمل").whereField("driverUid", isEqualTo: uid)
doc.getDocuments { (query, err) in
if err != nil {
print(err?.localizedDescription ?? "")
}
for document in query!.documents{
let chatDoc = document.reference.collection("المحادثات")
chatDoc.addSnapshotListener { (querysnap, err) in
if err != nil {
print(err?.localizedDescription ?? "")
}
self.messages = []
for snap in querysnap!.documents{
let data = snap.data()
guard let message = data["message"] as? String else {return}
guard let senderid = data["senderID"] as? String else {return}
guard let timestamp = data["date"] as? Timestamp else {return}
let date = Date(timeIntervalSince1970: TimeInterval(timestamp.seconds))
if senderid == Auth.auth().currentUser?.uid{
let drivermessages = driverMessages(image: UIImage(), text: message, date: date, isSender: true, driverid: senderid)
self.messages.append(drivermessages)
}else {
guard let message2 = data["message"] as? String else {return}
guard let senderid2 = data["senderID"] as? String else {return}
guard let timestamp2 = data["date"] as? Timestamp else {return}
guard let img = self.profileImg else {return}
let date2 = Date(timeIntervalSince1970: TimeInterval(timestamp2.seconds))
let usermessages = driverMessages(image: img, text: message2, date: date2, isSender: false, driverid: senderid2)
self.messages.append(usermessages)
}
DispatchQueue.main.async {
self.collectionView.reloadData()
let item = self.messages.count - 1
let insertionIndexPath = IndexPath(item: item, section: 0)
self.collectionView.scrollToItem(at: insertionIndexPath, at: .bottom, animated: true)
}
}
}
}
}
}
I am very new to coding myself.
However it seems to me something that could help you would be to have an .onSnapshot listener looking for change of type added.
https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/DocumentChange.Type
I found this article very helpful for the the CRUD application I am working on:
https://bezkoder.com/react-hooks-firestore/
Part of it shows how to listen for changes of various types.
I was able to implement that into my app to return only the added document rather than the entire collection, and then append that to my array.
tutorialsRef.onSnapshot(function(snapshot) {
snapshot.docChanges().forEach(function(change) {
if (change.type === "added") {
console.log("New tutorial: ", change.doc.data());
}
if (change.type === "modified") {
console.log("Modified tutorial: ", change.doc.data());
}
if (change.type === "removed") {
console.log("Removed tutorial: ", change.doc.data());
}
});
});
I want to load all data from firebase, then show the data to the table view. But now, I can't show all the data to the table view. It is because call the finishLoading(realm) method is faster than the for loop get all the data. How can I do some show all data when for loop is finish in swift. I have to use the Closure, however the second of the loop is later than this "self.finishLoading(realm: realm)"
I have to try to add the DispatchGroup(), however, the leave() when having an error of EXC_BAD_INSTRUCTION. Can I put the leave() in the closure? How can I fix it?
func loopAllProduct(userId: String, finishLoadWhenErr:Bool, storedClosure: #escaping (DocumentSnapshot) -> Void){
let storage = Storage.storage()
let db = Firestore.firestore()
let userDocRef = db.collection("Users").document(userId).collection("Product")
userDocRef.getDocuments{(document, error) in
if let err = error {
print("Error getting documents: \(err)")
} else {
for document in document!.documents {
storedClosure(document)
}
}
}
}
func downloadData() {
let startTime = Date()
while updating {
let diffTime = Date(timeIntervalSinceReferenceDate: startTime.timeIntervalSinceReferenceDate)
if (diffTime.timeIntervalSinceNow < -5){
self.stopAnimating()
self.refreshControl?.endRefreshing()
print("Update Timeout")
return
}
}
updating = true
let storage = Storage.storage()
let db = Firestore.firestore()
let productLoading = NSMutableArray()
let realm = try! Realm()
print("all posts")
let group = DispatchGroup()
let addPosts: (DocumentSnapshot)->Void = {(document) in
try! realm.write {
if let resuls = self.realmResults {
realm.delete(resuls);
}
}
let product = Product()
product.id = document.documentID
product.userID = document.data()?["UserID"] as? String
product.userName = document.data()?["UserName"] as? String
product.descrition = document.data()?["Descrition"] as? String
product.postTime = document.data()?["PostTime"] as? Date
product.price = document.data()?["Price"] as? Double ?? 0.0
product.stat = (document.data()?["stat"] as? Int)!
product.productName = document.data()?["ProductName"] as? String
let productId = document.documentID
productLoading.add(productId)
try! realm.write {
realm.add(product)
}
group.leave()
}
let userDocRef = db.collection("Users")
userDocRef.getDocuments{(document, error) in
for document in document!.documents {
group.enter()
self.loopAllProduct(userId:document.documentID , finishLoadWhenErr: true, storedClosure: addPosts)
}
}
group.notify(queue: DispatchQueue.main) {
self.finishLoading(realm: realm)
}
}
Working with CoreML and trying to execute two models with using the camera as a feed for image recognition. However, I can't seem to allow VNCoreMLRequest to run two models at one. Any suggestions on how to run two models on this request?
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
var fitness_identifer = ""
var fitness_confidence = 0
guard let model_one = try? VNCoreMLModel(for: imagenet_ut().model) else { return }
guard let model_two = try? VNCoreMLModel(for: ut_legs2().model) else { return }
let request = VNCoreMLRequest(model: [model_one, model_two]) { (finishedRequest, error) in
guard let results = finishedRequest.results as? [VNClassificationObservation] else { return }
guard let Observation = results.first else { return }
DispatchQueue.main.async(execute: {
fitness_identifer = Observation.identifier
fitness_confidence = Int(Observation.confidence * 100)
self.label.text = "\(Int(fitness_confidence))% it's a \(fitness_identifer)"
})
}
guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
// executes request
try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
}
Here is the error when I tried to add both models as an array (when I have just the one it works):
Contextual type 'VNCoreMLModel' cannot be used with array literal
why not run two separate requests in an AsyncGroup:
let request1 = VNCoreMLRequest(model: model_one) { (finishedRequest, error) in
//...
}
let request2 = VNCoreMLRequest(model: model_two) { (finishedRequest, error) in
//...
}
//...
let group = AsyncGroup()
group.background {
// Run on background queue
try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request1])
}
group.background {
// Run on background queue
try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request2])
}
group.wait()
// Both operations completed here
I want to scan QR code fetched from photos gallery. This link has something similar but didn’t helped much.
I’ve implemented scan QR functionality using camera successfully. Below is code for this :
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)
{
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
// lblMesage.text = QRCaptureFailedMessage
return
}
// Get the metadata object.
metadataObj = metadataObjects[0] as? AVMetadataMachineReadableCodeObject
// Here we use filter method to check if the type of metadataObj is supported
// Instead of hardcoding the AVMetadataObjectTypeQRCode, we check if the type
// can be found in the array of supported bar codes.
if supportedBarCodes.contains(metadataObj!.type) {
// if metadataObj.type == AVMetadataObjectTypeQRCode {
// If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj!.stringValue != nil {
. . .
}
}
}
func scanQRFromGallery(qrcodeImg : UIImage) {
let detector:CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])!
let ciImage:CIImage = CIImage(image:qrcodeImg)!
var qrCodeLink=""
let features=detector.features(in: ciImage)
for feature in features as! [CIQRCodeFeature] {
qrCodeLink += feature.messageString!
}
if qrCodeLink=="" {
print("qrCodeLink is empty")
}
else{
print("message: \(qrCodeLink)")
}
}
Any help would be appreciated.
You can use this code to scan the code from the gallery photo. For additional information about the code:
import UIKit
import AVFoundation
class QRScanner: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard
let qrcodeImg = info[UIImagePickerController.InfoKey.originalImage.rawValue] as? UIImage,
let detector: CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]),
let ciImage: CIImage = CIImage(image:qrcodeImg),
let features = detector.features(in: ciImage) as? [CIQRCodeFeature]
else {
print("Something went wrong")
return
}
var qrCodeLink = ""
features.forEach { feature in
if let messageString = feature.messageString {
qrCodeLink += messageString
}
}
if qrCodeLink.isEmpty {
print("qrCodeLink is empty!")
} else {
print("message: \(qrCodeLink)")
}
self.dismiss(animated: true, completion: nil)
}
}
You can read this article:
Scan QR Code From Gallery Swift
Hello guys I taking this course online and there is a similar function that looks like this.
typealias DownloadCompleted = () -> ()
var pokemonId = 0
func fetchPokemon(url: String, completed: #escaping DownloadCompleted) {
let context = coreData.persistentContainer.viewContext
let url = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: url) { (data, repsonse, error) in
if error != nil {
print(error!)
}
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
let jsonArray = jsonResult.value(forKey: "results") as! [[String: Any]]
for pokemonData in jsonArray {
self.pokemonId += 1
if self.pokemonId > 721 {
self.coreData.saveContext()
return
}
guard let name = pokemonData["name"] as? String else {
return
}
let pokemon = Pokemon(context: context)
pokemon.name = name
pokemon.id = self.pokemonId
print("Name: \(pokemon.name) Id:\(self.pokemonId)")
}
guard let nextURL = jsonResult.value(forKey: "next") as? String else {
return
}
DispatchQueue.main.async {
self.fetchPokemon(url: nextURL, completed: {
})
}
completed()
} catch let err {
print(err.localizedDescription)
}
}
task.resume()
}
I'm trying to set an array equal to the 721 Pokemon once the function is finished parsing the data. I really don't understand how the completed() works. I want the completed to get called once it finished parsing the 721. However in my case it gets called right after the first 20 Pokemon. Can someone please help me understand how the completed() works.
Would appreciate any help, links or articles about how it works. Thanks :)
i am not sure if i understood your question correctly but if you want completed() block to be executed once parsing is done for 721 Pokemon.
Call completed() block in for loop when condition >= 721 Try changing your code like below
for pokemonData in jsonArray {
self.pokemonId += 1
if self.pokemonId > 721 {
self.coreData.saveContext()
completed()
return
}
guard let name = pokemonData["name"] as? String else {
return
}
let pokemon = Pokemon(context: context)
pokemon.name = name
pokemon.id = self.pokemonId
print("Name: \(pokemon.name) Id:\(self.pokemonId)")
}