I have three textifleds that I am using to append data into a struct. How can I print what I appended? Right now I am getting a error message.
import UIKit
class ViewController: UIViewController {
#IBOutlet var c: UITextField!
#IBOutlet var a: UITextField!
#IBOutlet var b: UITextField!
var contacts: [Person] = []
#IBAction func press(_ sender: Any) {
contacts.append(Person(name: a.text!, surname: b.text! , phone: Int(c.text!)!))
print(ViewController.Person)
}
struct Person {
var name: String
var surname: String
var phone: Int
}
}
You are trying to print the actual struct of Person. You want to print a specific instance.
You can print the whole array as:
print("contacts: \(contacts)")
If you want to print the one instance, I would suggest splitting your code a little:
#IBAction func press(_ sender: Any) {
let person = Person(name: a.text!, surname: b.text! , phone: Int(c.text!)!)
print(person)
contacts.append(person)
}
But to make the printing useful you should add the description property to your struct and make your struct conform to CustomStringConvertible.
struct Person: CustomStringConvertible {
var name: String
var surname: String
var phone: Int
var description: String {
return "Person name: \(name), surname: \(surname), phone: \(phone)"
}
}
You should also learn to safely deal with optionals. The ! operator means "crash if not nil". Your code will crash if the user types in "Hello" into the phone field.
#IBAction func press(_ sender: Any) {
if let name = a.text, let surname = b.text, let phoneStr = c.text, let phone = Int(phoneStr) {
let person = Person(name: name, surname: surname, phone: phone)
print(person)
contacts.append(person)
}
}
And also consider that a phone number is not a numeric value. Use String, not Int to store phone numbers.
I have modified your code, you can use it it will give you result what you want.
import UIKit
class ViewController: UIViewController {
#IBOutlet var c: UITextField!
#IBOutlet var a: UITextField!
#IBOutlet var b: UITextField!
var contacts: [Person] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func press(_ sender: Any) {
contacts.append(Person(name: a.text!, surname: b.text! , phone: Int(c.text!)!))
print(self.contacts.description)
}
struct Person {
var name: String
var surname: String
var phone: Int
}}
I have just made one change in print statement. As your contacts array in you view controller itself so you can directly use it by self and use function description for printing updating values of contacts.
print(ViewController.Person) is wrong
Please use:
print(contacts.last)
Related
Here is my customer class:
class Customer {
// Creating a customer
let name: String
let surname: String
let contactNo: String
let email: String
init(name: String,surname: String,contactNo: String,email: String) {
self.name = name
self.surname = surname
self.contactNo = contactNo
self.email = email
}
}
This is the code I'm using which keeps returning a nil:
class ProfileCus: UIViewController {
// Labels to display data
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var surnameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var contactLabel: UILabel!
// Reference to customer collection in Firestore
private var customerRefCollection = Firestore.firestore().collection("customers")
// Customer Object
private var customer = Customer(name: "a",surname: "a",contactNo: "a",email: "a")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getDataFromFirebase{
self.customerRefCollection = Firestore.firestore().collection("customers")
print(self.customer,"debug step 5")
self.nameLabel.text = self.customer.name
self.surnameLabel.text = self.customer.surname
self.emailLabel.text = self.customer.email
self.contactLabel.text = self.customer.contactNo
}
}
func getDataFromFirebase(completion:#escaping() -> ()){
print(self.customer,"debug step 1")
let userID = Auth.auth().currentUser?.uid
print(userID,"debug step 2")
// Locate the user information on Firestore
customerRefCollection.document(userID!).getDocument { (snapshot, error) in
if let err = error {
debugPrint("Error fetching documents: \(err)")
}
else {
// Ensure that if there's nothing in the document that the function returns
guard let snap = snapshot else {return}
print(snap, "debug step 3")
// Parse the data to the customer model
let data = snap.data()
let name = data?["name"] as? String ?? ""
let surname = data?["surname"] as? String ?? ""
let email = data?["email"] as? String ?? ""
let contact = data?["contact no"] as? String ?? ""
// Create the customer and pass it to the global variable
let cus = Customer(name: name, surname: surname, contactNo: contact, email: email)
print(self.customer,"debug step 4")
self.customer = cus
}
completion()
}
}
}
Can anyone please help me understand what I am doing wrong because the snapshot does return but the way I parse the data is wrong because the customer object returns a nil.
I have added print statements with tags saying debug step 1 ect so you can follow what happens at run time, here is the output:
020-08-13 21:15:20.388052+0200 Clean Wheels[8599:430648] 6.29.0 - [Firebase/Analytics][I-ACS023012] Analytics collection enabled
Customer(name: "a", surname: "a", contactNo: "a", email: "a") debug step 1
Optional("RWVTDIUuL1eahOLpZT1UmMl0cja2") debug step 2
<FIRDocumentSnapshot: 0x6000017499f0> debug step 3
Customer(name: "a", surname: "a", contactNo: "a", email: "a") debug step 4
Customer(name: "", surname: "", contactNo: "", email: "") debug step 5
It seems to me as if the data function is not the correct function to use because when I hard code the values its shows up in the UI Profile View, is there perhaps an alternative?
Output once the code runs
There are a number of ways you can do this but what I'd suggest is passing the customer object through the completion handler (to the caller). You could also configure the customer object to take the document snapshot in its initializer (instead of taking 4 separate properties) and either return a customer object or nil (this would require a failable intializer which is incredibly basic). Also, I didn't see a need to declare so many instance properties (in this example, anyway) so I took them out. I also made the customer number an integer, not a string (to illustrate how I would structure the data).
class Customer {
let name: String
let surname: String
let contactNo: Int // change this back to a string
let email: String
init(name: String, surname: String, contactNo: Int, email: String) {
self.name = name
self.surname = surname
self.contactNo = contactNo
self.email = email
}
}
class ProfileCus: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var surnameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var contactLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
getCustomer { (customer) in
if let customer = customer {
print(customer)
} else {
print("customer not found")
}
}
}
private func getCustomer(completion: #escaping (_ customer: Customer?) -> Void) {
guard let userID = Auth.auth().currentUser?.uid else {
completion(nil)
return
}
Firestore.firestore().collection("customers").document(userID).getDocument { (snapshot, error) in
if let doc = snapshot,
let name = doc.get("name") as? String,
let surname = doc.get("surname") as? String,
let contact = doc.get("contact") as? Int, // cast this as a string
let email = doc.get("email") as? String {
let customer = Customer(name: name, surname: surname, contactNo: contact, email: email)
completion(customer)
} else {
if let error = error {
print(error)
}
completion(nil)
}
}
}
}
I have data in local file, I have loaded them into the tableView, and now I want to search/filter via search bar.
I have tried some solutions I've found on the Internet, but I don't know how to access specific data in my structure.
Here is part of the structure:
struct Model: Codable {
var data: [Description]
}
struct Description: Codable {
var id: Int
var name: String
var address: String
var city: String
var description: String
var countryId: Int
var webSite: String
var status: Int
var accountType: Int
var addDate: String
var placeGroupId: Int
var longitude: Double
var latitude: Double
var distance: Double
var working: Bool
var promotion: String
var repertoire: Repertoire
var workingHour: WorkingHour
var country: Country
var reviewNum: Int
var score: Double
var placeImgUrl: String
}
var model: Model?
and here is some code from my ViewController:
var filteredObjects = [Description]()
let searchController = UISearchController(searchResultsController: nil)
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
<#code#>
}
}
I have variable model, which is type of struct Model. Structure Model have a variable data which is type of struct Description. And finally, in structure Description I have variable name. My tableView cells shows names of models (model?.data[indexPathrow.row].name). Now, I need to search by names shown in tableView cells. I hope I explained it clearly. Thank You very much!
Assuming objects is also [Description] and represents the data source array
func updateSearchResults(for searchController : UISearchController)
{
let searchString = searchController.searchBar.text!
if searchString.isEmpty {
filteredObjects.removeAll()
} else {
filteredObjects = objects.filter{ $0.name.range(of: searchString, options: .caseInsensitive) != nil }
}
tableView.reloadData()
}
In the table view data source methods display objects or filteredObjects depending on searchController.isActive
My code below works perfectly if i switch the strings for the ints. However with the String where it is it is not working.
import UIKit
class ViewController: UIViewController {
#IBOutlet var txt: UITextField!
var arrayOfInt = [String]()
#IBAction func submit(_ sender: Any) {
if let text = txt.text {
if let name = String(text){
arrayOfInt.append(name)
}}}
}
You are (unnecessarily) initialising a String from a String using String(text); Unlike Int(_ string:String) this is not a failable initializer.
It is possible that a String cannot be parsed to an Int, so you need to check that Int(text) didn't return nil, but a String can always be initialised from a String.
When you say
if let name = String(text) {
The compiler give you an error because the if let... conditional binding construct requires an optional, but String(text) does not return an optional.
Your code can be simplified to:
class ViewController: UIViewController {
#IBOutlet var txt: UITextField!
var arrayOfString = [String]()
#IBAction func submit(_ sender: Any) {
if let text = sender.text {
arrayOfString.append(text)
}
}
}
My code below uses init to save 1 entry to the struct. The problem is that it can only save 1 entry. I need to somehow make this into a array. So I am trying to use a button to submit a string to a struct where the struct can take multiple entires right now it can only take 1.
import UIKit
class ViewController: UIViewController {
#IBOutlet var c: UITextField!
#IBOutlet var a: UITextField!
#IBOutlet var label: UILabel!
#IBAction func save(_ sender: Any) {
let judo = Person.init(name: a.text ?? "", phone: Int(c.text ?? "") ?? 0)
}
struct Person : CustomStringConvertible {
var name: String
var phone: Int
static var myStruct = [String]();
var description: String {
return "\(name),\(phone)"
}}}
I understood that you want persons to be added to array on press of button
Here is the solution
var persons:[Person] = [Person]()
#IBAction func save(_ sender: Any) {
let judo = Person.init(name: a.text ?? "", phone: Int(c.text ?? "") ?? 0)
self.persons.append(judo)
}
Hope you are looking for this
I have the user input numbers into some textFields. Then I make some combinations out of them and then do a series of finding of finding which number is greater than the other. For example out of a and b which is greater, then out of c and d which is greater. Then out of the the two winners which is greater from that. I want my ultimate answer to be displayed in a label on a different viewController, but am having trouble with this.
I created a struct called "help" in a swift file called "PrisonerModel.swift" and want the answer to be displayed in the answer label on MatrixViewController. I believe my problem is coming up something in the process of calling the answer.
My code:
import Foundation
struct PrisonerModel {
var x: String
var y: String
var z: String
var a: String
var b: String
var c: String
var d: String
var e: String
var answer: String
}
struct help {
var f: String
func greaterInt(x: Int, _ y: Int) -> Int {
if x > y {
return x}
return y
}}
Second ViewController
class MatrixViewController: UIViewController {
#IBOutlet weak var P1CoopCoop: UILabel!
#IBOutlet weak var P2CoopCoop: UILabel!
#IBOutlet weak var P1DefCoop: UILabel!
#IBOutlet weak var P2DefCoop: UILabel!
#IBOutlet weak var P1CoopDef: UILabel!
#IBOutlet weak var P2CoopDef: UILabel!
#IBOutlet weak var P1DefDef: UILabel!
#IBOutlet weak var P2DefDef: UILabel!
#IBOutlet weak var answer1: UILabel!
var labelText1 = ""
var model: PrisonerModel?
var answer: help?
override func viewDidLoad() {
super.viewDidLoad()
P1CoopCoop.text = model?.x
P2CoopCoop.text = model?.y
P1DefCoop.text = model?.z
P2DefCoop.text = model?.a
P1CoopDef.text = model?.b
P2CoopDef.text = model?.c
P1DefDef.text = model?.d
P2DefDef.text = model?.e
answer1.text = answer?.f
Segue
prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showMatrix" {
if let matrixVC = segue.destinationViewController as? MatrixViewController {
let pm = PrisonerModel(x: Text3.text ?? "", y: Text3.text ?? "", z: Text4.text ?? "", a: Text1.text ?? "", b: Text1.text ?? "", c: Text4.text ?? "", d: Text2.text ?? "", e: Text2.text ?? "", answer: String(Int(Text1.text! + Text3.text!)) ?? "")
matrixVC.model = pm
}
}
}
It isn't clear what the "answer" should be, but some general advice is to convert your text values to integers so that you can perform appropriate mathematical operations on them.
I would implement func shouldPerformSegue(withIdentifier identifier: String,
sender: Any?) -> Bool to first validate your inputs and create your model:
struct PrisonerModel {
var x: Int
var y: Int
var z: Int
var a: Int
var b: Int
var c: Int
var d: Int
var e: Int
var answer: Int
}
var model: PrisonerModel?
override func shouldPerformSegue(withIdentifier identifier: String,
sender: Any?) -> Bool {
if let identifier == "showMatrix" {
guard let a = Int(text1.text!),
let b = Int(text1.text!),
let c = Int(text4.text!),
let d = Int(text2.text!),
let e = Int(text2.text!),
let x = Int(text3.text!),
let y = Int(text3.text!),
let z = Int(text4.text!),
let answer = Int(text1.text + text3.txt) else {
// Probably set some text in a label informing the user that their input is invalid
return false
}
self.model = PrisonerModel(x:x, y:y, z:z, a:a, b:b, c:c, d:d, e:e, answer:answe)
return true
}
}
return false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showMatrix" {
if let matrixVC = segue.destinationViewController as? MatrixViewController,
let model = self.model {
matrixVC.model = model
}
}
}
Note I have changed the name of your text field properties to start with a lower case letter as is convention. Also your code would possibly be clearer if you had better names for your model properties than a,b,c etc.
Also the use of the force unwrap for the text properties is safe unless you have explicitly assigned nil to the property; by default a UITextField will have a string assigned to text.