I have a project need to create qr code generator and i want to show more than one value when customer insert their information.
My Qr Code generator have some information field to let customer insert their infomation, but i added these field and test out, it only show one value, and i don' know why other values didn't show, maybe i just missed some part in the code, i don't know where is the problem, any expert knows what happened?
Example of the Code:
import Foundation
import SwiftUI
import CoreImage.CIFilterBuiltins
struct GenerateQRCode: View {
#State var first = "D3"
#State var id = "abcafewrqr
#Binding var time: Date"
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var dateFormatter: DateFormatter {
let df = DateFormatter()
df.dateFormat = "HH:mm"
return df
}
var body: some View {
NavigationView{
Image(uiImage: GenerateTime(first: first, id: id, time: time))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}.navigationBarBackButtonHidden(true)
}
func GenerateTime(first: String, id: String, time: Date)-> UIImage {
let str = dateFormatter.string(from: time)
let all = first + id + str
let com = all.data(using: .utf8)
filter.setValue(com, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
Update:
func GenerateTime(first: String, id: String, time: Date)-> UIImage {
let str = dateFormatter.string(from: time)
let timeData = str.data(using: .utf8)
let idData = id.data(using: .utf8)
let firstData = first.data(using: .utf8)
let com = firstData, idData, TimeData //<--Here Updated
filter.setValue(com, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
CombineString like above what i did, is it right?
Do you mean you get only one data from the 3 dates you've set in the QR code generator when scanning the resulting QR code?
If yes, then here is your problem:
filter.setValue(firstData, forKey: "inputMessage")
filter.setValue(idData, forKey: "inputMessage")
filter.setValue(timeData, forKey: "inputMessage")
You are suppose to set inputMessage once. Each subsequent setValue call will overwrite the previous.
You should format the 3 dates into one String and set inputMessage only once.
Related
Swift (v 5/5.1) newbie here, having a hard time with Codables...hoping to get some advise from the experts here.
Okay, I have a simple dictionary from struct where the key is a string. I want to store the dictionary in UserDefaults (and later retrieve). There are some quite similar questions here, but these are mainly addressing nested struct's.
First attempt (error handling removed for simplicity):
public struct PriceStruct:Codable {
var myPrice: Double
var myTime: TimeInterval
var selected: Bool
var direction: Int
var myHigh, myLow: Double
enum CodingKeys: String, CodingKey {
case myPrice = "myPrice"
case myTime = "myTime"
case selected = "selected"
case direction = "direction"
case myHigh = "myHigh"
case myLow = "myLow"
}
}
var myPrices: [String: PriceStruct] = [:]
// [fill myPrices with some data...]
func savePrices() {
// error: Attempt to set a non-property-list object
UserDefaults.standard.set(myPrices, forKey: "prices")
}
func loadPrices() {
// obviously this doesn't work either
let myPrices = UserDefaults.standard.data(forKey: "prices")
}
While I assumed from the documentation, that UserDefaults is capable of storing dictionaries, it doesn't - at least for me.
Next thing I tried was using JSONEncoder like this:
// this time with prior JSON encoding
func savePrices() {
// this works
let json = try! JSONEncoder().encode(myPrices)
UserDefaults.standard.set(json as Data, forKey: "prices")
}
func loadPrices() {
// this doesn't work
let json = UserDefaults.standard.data(forKey: "prices")
let decoder = JSONDecoder()
let decoded = try! decoder.decode(PriceStruct.self, from json!)
}
Unfortunately I'm getting an error when trying to load data back from UserDefaults:
Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "myPrice", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"myPrice\", intValue: nil) (\"myPrice\").", underlyingError: nil))
Other variants I tried is converting the encoded JSON to an UTF8 encoded string and storing/retrieving this one:
func savePrices() {
// this works too
let json = try! JSONEncoder().encode(myPrices)
UserDefaults.standard.set(String(data: json, encoding: .utf8), forKey: "prices")
}
func loadPrices() {
// and this doesn't work either
let json = UserDefaults.standard.string(forKey: "prices")!.data(using: .utf8)
}
So, from the error raised, CodingKeys seems to be the root of the problem. I tried to switch over using NSKeyedArchiver and NSKeyedUnarchiver` with no success.
I'm really wondering if there is a simple/universal solution to save/load a Dictionary in UserDefaults?
All your comments and suggestions are appreciated. Thanks!
I tried with the below code in my project that will work for me.
User Model
public protocol UserModel: Codable, PrimaryKey {
var id: String { get }
var firstName: String? { get }
var lastName: String? { get }
var userName: String? { get }
var emails: [String] { get }
}
public struct User: UserModel {
public let id: String
public let firstName: String?
public let lastName: String?
public let userName: String?
public let emails: [String]
public enum CodingKeys: String, CodingKey {
case id = "Id"
case firstName = "FirstName"
case lastName = "LastName"
case userName = "UserName"
case emails = "Emails"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
self.id = try container.decode(String.self, forKey: .id)
self.firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
self.lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
self.userName = try container.decodeIfPresent(String.self, forKey: .userName)
self.emails = try container.decodeIfPresent([String].self, forKey: .emails) ?? []
}
catch let error {
debugPrint(error)
throw error
}
}
}
I have stored in userDefault using below way
User Data Class
class UserData: NSObject
{
let userDefaultKey = "user_information"
var userData: User?
func getDictionary() -> [String: Data]
{
var dicInfo = [String: Data]()
do
{
let _userData = try JSONEncoder().encode(userData)
dicInfo["userData_default"] = _userData
}catch let error{
print("error while save data in User Default: \(error.localizedDescription)")
}
return dicInfo
}
func saveToDefault()
{
let userDefault = UserDefaults.standard
userDefault.set(getDictionary(), forKey: userDefaultKey)
userDefault.synchronize()
}
func loadFromDefault()
{
let userDefault = UserDefaults.standard
if let dicInfo = userDefault.object(forKey: userDefaultKey) as? [String: Data]
{
update(dicInfo)
}
}
func update(_ dictionaryInfo: [String: Data])
{
do
{
if let _userData_data = dictionaryInfo["userData_default"]
{
if let _userData = try? JSONDecoder().decode(User.self, from: _userData_data) {
userData = _userData
}
}
saveToDefault()
}catch let error{
print("error while load From Default data in User Default: \(error.localizedDescription)")
}
}
}
Hope this will help you.
The goal here is to retrieve all the documents of a Firestore Database, then putting the data of those retrieved documents into a Table View. I'm using Xcode and Firebase.
For example, if I had a Firestore database with this data and documents:
Document 1
Name: Bob
Email: bob#gmail.com
Phone: 408-111-1234
Document 2
Name: Joe
Email: joe#yahoo.com
Phone: 408-338-4321
I would like to have it all in a table view, maybe something like this:
Bob - bob#gmail.com - 408-111-1234
Joe - joe#yahoo.com - 408-338-4321
So far, I have this set up:
TableViewController:
class PeopleViewController: UITableViewController {
#IBOutlet var table: UITableView!
var peopleArray = [] as [Array<Any>]
private var document: [DocumentSnapshot] = []
override func viewDidLoad() {
super.viewDidLoad()
table.tableFooterView = UIView(frame: CGRect.zero)
self.table.delegate = self
self.table.dataSource = self
loadData()
}
And the loadData() func:
func loadData() {
FirebaseFirestore.root.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let name = data["name"] as? String ?? ""
let email = data["email"] as? String ?? ""
let phone = data["phone"] as? String ?? ""
let newPerson = ["name": name, "email": email, "phone": phone]
self.peopleArray.append(newPerson)
print(self.peopleArray)
}
self.table.reloadData()
}
}
}
}
The first thing wrong here is that there is this error:
Cannot convert value of type '[String : String]' to expected argument type '[Any]'
When I try to append a newPerson in the PeopleArray. I have searched this up, but none of the answers matched this specific error. The second thing is that I have no clue how to even start adding the data from the PeopleArray into the Table View.
I would love some help for both the error and the Table View. Anything is appreciated!
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
After doing what Vadian recommended, I am stuck with a new error.
attempt to insert row 2 into section 0, but there are only 1 rows in section 0 after the update
This is the updated func loadData:
func loadData() {
FirebaseFirestore.root.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
self.table.beginUpdates()
let data = document.data()
let name = data["name"] as? String ?? ""
let zip = data["zip"] as? String ?? ""
let sport = data["sport"] as? String ?? ""
let newPerson = Person(name: name, zip: zip, sport: sport)
self.people.append(newPerson)
let newIndexPath = IndexPath(row: self.people.count, section: 0)
self.table.insertRows(at: [newIndexPath], with: .automatic)
self.table.endUpdates()
}
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
}
}
I've read about this on another question about how the rows are off by 1, but even when I let indexPath equal to people.count+1 or people.count+2, the error persists. What am I doing wrong?
First of all the syntax
var peopleArray = [] as [Array<Any>]
is bad practice. If you want to declare an empty array write
var peopleArray : [Array<Any>] = []
or
var peopleArray = [Array<Any>]()
Second of all your array is a nested array which is not intended. You mean
var peopleArray = Array<Any>()
which is more descriptive using the alternative syntax
var peopleArray = [Any]()
Third of all as your data is clearly a more specific type than [Any] declare the array
var peopleArray = [[String:String]]()
This fixes the error.
Fourth of all the recommended data source is a custom struct
struct Person {
let name, email, phone: String
}
In this case declare the array (we know that people is plural so the suffix array is redundant).
var people = [Person]()
And populate it (you might reload the table view on the main thread)
for document in snapshot.documents {
let data = document.data()
let name = data["name"] as? String ?? ""
let email = data["email"] as? String ?? ""
let phone = data["phone"] as? String ?? ""
let newPerson = Person(name: name, email: email, phone: phone)
self.people.append(newPerson)
}
DispatchQueue.main.async {
self.table.reloadData()
}
I researched a lot and fixed the edited part in the question I posted. A simple -1 in the insert rows fixed the problem.
let newIndexPath = IndexPath(row: self.people.count-1, section: 0)
Vadian also helped me with the original problem, which was using
var peopleArray = [[String:String]]()
instead of the other incorrect format. I was then able to find how to insert rows by just simply using the table.insertrows function.
(I'm just posting this for a simplified answer for anyone else experiencing problems).
Good evening,
I am trying to call data to post onto a card very similar to tinder. When I run my code, everything works and I see the print statement in the console. However the card view shows a default image with default text.
I was wondering if anyone has encountered this issue and could possibly help explain what I am doing wrong.
fileprivate func fetchUsersFromDatabase() {
Database.database().reference().child("JobPost").observeSingleEvent(of: .value, with: {(Snapshot) in
if let eachDict = Snapshot.value as? NSDictionary{
for each in eachDict{
//I think the issue is caused by the let post = poster
let post = Poster(dictionary: Snapshot.value as! [String : Any])
self.cardViewModels.append(post.toCardViewModel())
print(each.value )
}
}
self.setupDummyCards()
}, withCancel: {(Err) in
})
}
// the struct is in an extension file.
struct Poster: ProducesCardViewModel{
var jobName : String?
var price: Int?
var postName: String?
var ImageUrl1: String?
var uid: String?
init(dictionary: [String: Any]) {
self.price = dictionary["cost"] as? Int
self.jobName = dictionary["category"] as? String
self.postName = dictionary["description"] as? String ?? ""
self.ImageUrl1 = dictionary["JobImageUrl"] as? String ?? ""
self.uid = dictionary["fromId"] as? String ?? ""
}
func toCardViewModel() -> CardViewModel {
let attributedText = NSMutableAttributedString(string: jobName ?? "", attributes: [.font: UIFont.systemFont(ofSize: 32, weight: .heavy)])
let priceString = price != nil ? "\(price!)" : "N\\A"
attributedText.append(NSAttributedString(string: " \(priceString)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .regular)]))
let jobString = jobName != nil ? jobName! : "Not available"
attributedText.append(NSAttributedString(string: "\n\(jobString)", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)]))
return CardViewModel(imageNames: [ImageUrl1 ?? "" ], attributedString: attributedText, textAlignment: .left)
}
}
Example
// toCardViewModel
import UIKit
protocol ProducesCardViewModel {
func toCardViewModel() -> CardViewModel
}
class CardViewModel {
let JobimageName: [String]
let attributedString: NSAttributedString
let textAlignment: NSTextAlignment
init(imageNames: [String], attributedString: NSAttributedString, textAlignment: NSTextAlignment) {
self.JobimageName = imageNames
self.attributedString = attributedString
self.textAlignment = textAlignment
}
fileprivate var imageIndex = 0 {
didSet {
let imageName = JobimageName[imageIndex]
let image = UIImage(named: imageName)
imageIndexObserver?(imageIndex, image)
}
}
var imageIndexObserver: ((Int, UIImage?) -> ())?
func advanceToNextPhoto() {
imageIndex = min(imageIndex + 1, JobimageName.count - 1)
}
func goToPreviousPhoto() {
imageIndex = max(0, imageIndex - 1)
}
}
Thank you in advance!
// P.S I previously posted this question without lack of sufficient detail. I decided to just re post it with the quality material. I really appreciate your time.
I have figured out an answer and it was quite obvious to me. I noticed that
print(each.value )
would print the value, so I just substituted
let post = Poster(dictionary: Snapshot.value as! [String : Any])
to
let post = Poster(dictionary: each.value as! [String : Any])
and everything started to work just fine!
I downloaded this library and my goal is to get images links from web service as Json then show them.
here is how to do it by a random link from the Internet
let alamofireSource = [AlamofireSource(urlString:
"https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080")!,
AlamofireSource(urlString: "https://images.unsplash.com/photo-1447746249824-
4be4e1b76d66?w=1080")!,
AlamofireSource(urlString:
"https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
What I thought I should do is :
1- do a call request to my api
2- save images URLs in an array
3- loop through the array and have the variable in [AlamofireSource(urlString:
"variable from the array")!
Is this the right way or is there any better way even without using this library ?
Update
var Pics = [JSON]()
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
#IBOutlet var slideshow: ImageSlideshow!
override func viewWillAppear(_ animated: Bool) {
let url = "http://localhost:8000/api/hello"
Alamofire.request(url).responseJSON { response in
if let value: AnyObject = response.result.value as AnyObject? {
//Handle the results as JSON
let json = JSON(value)
for (key, subJson) in json["pic"] {
self.Pics.append(subJson) as? String
}
print(self.Pics)
}
}
}
let kingfisherSource = [KingfisherSource(urlString: Pics)!] // error
override func viewDidLoad() {
super.viewDidLoad()
slideshow.backgroundColor = UIColor.white
slideshow.slideshowInterval = 5.0
slideshow.pageControlPosition = PageControlPosition.underScrollView
slideshow.pageControl.currentPageIndicatorTintColor = UIColor.lightGray
slideshow.pageControl.pageIndicatorTintColor = UIColor.black
slideshow.contentScaleMode = UIViewContentMode.scaleAspectFill
slideshow.currentPageChanged = { page in
print("current page:", page)
}
Note:
this is the format of the json data
[
"http://localhost:8000/images/1490104055.jpg",
"http://localhost:8000/images/1490104055.jpg",
"http://localhost:8000/images/1490104055.jpg",
"http://localhost:8000/images/1490104055.jpg",
"http://localhost:8000/images/1490104055.jpg",
null,
null
]
Thanks
i'm new in swift development, i added data in server tried to refresh tableviewcontroller with refreshcontrol function but value in table view didn't change.
class MainTableViewController: UITableViewController, UINavigationControllerDelegate {
#IBOutlet var sosTableView: UITableView!
var datas = [dataSos]()
override func viewDidLoad() {
super.viewDidLoad()
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.labelText = "Loading"
spinningActivity.detailsLabelText = "Please wait"
dispatch_async(dispatch_get_main_queue()) {
self.loadDataServer()
spinningActivity.hide(true)
self.sosTableView.reloadData()
}
//loadDataSos()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
var refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: Selector("refreshData"), forControlEvents: UIControlEvents.ValueChanged)
self.refreshControl = refreshControl
}
Refresh func
func refreshData(){
dispatch_async(dispatch_get_main_queue()) {
self.loadDataServer()
self.sosTableView.reloadData()
}
refreshControl?.endRefreshing()
}
load server func
func loadDataServer(){
do {
let data = NSData(contentsOfURL: NSURL(string: "http://xxxx/scripts/xxx.php")!)
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
//let NumberOfPersons = jsonResult.count
// **LOOP THROUGH THE JSON ARRAY**
for anItem in jsonResult as! [Dictionary<String, AnyObject>] {
let userId = anItem["userId"] as! String
let userName = anItem["firstName"] as! String
let userAddress = anItem["address"] as! String
let userDate = anItem["date"] as! String
let userLocation = anItem["location"] as! String
var userEvent = anItem["event"] as? String
let sosId = anItem["sosId"] as! String
// do something with personName and personID
let imageUrl = NSURL(string:"http://xxx")
let imageData = NSData(contentsOfURL: imageUrl!)
if userEvent == nil{
userEvent = "Need Help"
}else if userEvent! == "1" {
userEvent! = "Thief"
}
else if userEvent! == "2" {
userEvent! = "Fire"
}
else{
userEvent! = "Healthy Issue"
}
//print(personName)
if imageData == nil{
let photo1 = UIImage(named: "defaultPhoto")!
let data1 = dataSos(userId: userId, name: userName, location: userLocation, address: userAddress, event: userEvent!, date: userDate, photo: photo1, sosId: sosId)
self.datas += [data1]
}
else{
let photo1 = UIImage(data: imageData!)
//let photo1 = UIImage(named: "defaultPhoto")
let data1 = dataSos(userId: userId, name: userName, location: userLocation, address: userAddress, event: userEvent!, date: userDate, photo: photo1, sosId: sosId)
self.datas += [data1]
}
}
} catch let error as NSError {
print(error)
}
// }
}
Update: table view data source
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// warning Incomplete implementation, return the number of rows
return datas.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MainTableViewCell
// Configure the cell...
let data0 = datas[indexPath.row]
cell.nameLabel.text = data0.name
cell.locationLabel.text = data0.location
cell.addressTextView.text = data0.address
cell.eventLabel.text = data0.event
cell.dateLabel.text = data0.date
cell.photoLabel.image = data0.photo
self.roundingUIView(cell.photoLabel, cornerRadiusParam: 35)
return cell
}
Ok, I just understood that you're inheriting for UITableViewController, therefor you already have tableView property inherited from it. The table view from this property has already set delegate and dataSource to your controller, but not for your custom sosTableView. You should replace your custom sosTableView with inherited tableView property and then everything gonna work as you're expecting.