How to insert data in SQLite in swift iOS? - ios

I'm very new to SQLite and Swift. I have followed this tutorial https://medium.com/#imbilalhassan/saving-data-in-sqlite-db-in-ios-using-swift-4-76b743d3ce0e
but I want ID to auto-increment by SQLite.
// Model
struct PersonModel {
let firstName: String?
let lastName: String?
let phone: String?
let address: String?
}
// DBManager
import Foundation
import UIKit
import SQLite3
class DBManager
{
init()
{
db = openDatabase()
createTable()
}
let dbPath: String = "myDb.sqlite"
var db:OpaquePointer?
// MARK: - Open DataBase
func openDatabase() -> OpaquePointer?
{
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(dbPath)
var db: OpaquePointer? = nil
if sqlite3_open(fileURL.path, &db) != SQLITE_OK
{
print("error opening database")
return nil
}
else
{
print("Successfully opened connection to database at \(dbPath)")
return db
}
}
// MARK: - Create Table
func createTable() {
let createTableString = "CREATE TABLE IF NOT EXISTS person(id INTEGER PRIMARY KEY AUTOINCREMENT, firstName TEXT, lastName TEXT, phone TEXT, address TEXT);"
var createTableStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK
{
if sqlite3_step(createTableStatement) == SQLITE_DONE
{
print("person table created.")
} else {
print("person table could not be created.")
}
} else {
print("CREATE TABLE statement could not be prepared.")
}
sqlite3_finalize(createTableStatement)
}
// MARK: - Insert
func insert(firstName: String, lastName: String, phone: String, address: String)
{
let insertStatementString = "INSERT INTO person (id, firstName, lastName, phone, address) VALUES (?, ?, ?, ?, ?);"
var insertStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
sqlite3_bind_text(insertStatement, 1, (firstName as NSString).utf8String, -1, nil)
sqlite3_bind_text(insertStatement, 2, (lastName as NSString).utf8String, -1, nil)
sqlite3_bind_text(insertStatement, 3, (phone as NSString).utf8String, -1, nil)
sqlite3_bind_text(insertStatement, 4, (address as NSString).utf8String, -1, nil)
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("Successfully inserted row.")
} else {
print("Could not insert row.")
}
} else {
print("INSERT statement could not be prepared.")
}
sqlite3_finalize(insertStatement)
}
// MARK: - Read
func read() -> [PersonModel] {
let queryStatementString = "SELECT * FROM person;"
var queryStatement: OpaquePointer? = nil
var psns : [PersonModel] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
while sqlite3_step(queryStatement) == SQLITE_ROW {
let id = sqlite3_column_int(queryStatement, 0)
let firstName = String(describing: String(cString: sqlite3_column_text(queryStatement, 1)))
let lastName = String(describing: String(cString: sqlite3_column_text(queryStatement, 2)))
let phone = String(describing: String(cString: sqlite3_column_text(queryStatement, 3)))
let address = String(describing: String(cString: sqlite3_column_text(queryStatement, 4))) // this is where i'm getting error: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
psns.append(PersonModel(firstName: firstName, lastName: lastName, phone: phone, address: address))
print("Query Result:")
print("\(id) | \(firstName) | \(lastName) | \(phone) | \(address)")
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
return psns
}
// MARK: - Delete
func deleteByID(id:Int) {
let deleteStatementStirng = "DELETE FROM person WHERE Id = ?;"
var deleteStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, deleteStatementStirng, -1, &deleteStatement, nil) == SQLITE_OK {
sqlite3_bind_int(deleteStatement, 1, Int32(id))
if sqlite3_step(deleteStatement) == SQLITE_DONE {
print("Successfully deleted row.")
} else {
print("Could not delete row.")
}
} else {
print("DELETE statement could not be prepared")
}
sqlite3_finalize(deleteStatement)
}
}
// ViewController
class ViewController: UIViewController {
#IBOutlet weak var txtFirstName: UITextField!
#IBOutlet weak var txtLastName: UITextField!
#IBOutlet weak var txtPhoneNumber: UITextField!
#IBOutlet weak var txtAddress: UITextField!
#IBOutlet weak var btnSave: UIButton!
var db: DBManager = DBManager()
var persons: [PersonModel] = []
override func viewDidLoad() {
super.viewDidLoad()
}
func setUpDataBase() {
db.insert(firstName: txtFirstName.text ?? "", lastName: txtLastName.text ?? "", phone: txtPhoneNumber.text ?? "", address: txtAddress.text ?? "")
persons = db.read()
}
// MARK: - Button Save Event
#IBAction func btnSave_Event(_ sender: UIButton) {
setUpDataBase()
}
}
when I insert data in SQLite, in func read(), line:
let address = String(describing: String(cString: sqlite3_column_text(queryStatement, 4)))
is throwing error:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
I'm adding value in address textField and still getting this error, also I have no idea if this is right approach for auto-increment ID. could anyone help me out ?
UPDATE
as per #Joakim Danielson suggestion I solved the issue:
// MARK: - Insert
func insert(firstName: String, lastName: String, phone: String, address: String)
{
let insertStatementString = "INSERT INTO person (firstName, lastName, phone, address) VALUES (?, ?, ?, ?);"
var insertStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
sqlite3_bind_text(insertStatement, 1, (firstName as NSString).utf8String, -1, nil)
sqlite3_bind_text(insertStatement, 2, (lastName as NSString).utf8String, -1, nil)
sqlite3_bind_text(insertStatement, 3, (phone as NSString).utf8String, -1, nil)
sqlite3_bind_text(insertStatement, 4, (address as NSString).utf8String, -1, nil)
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("Successfully inserted row.")
} else {
print("Could not insert row.")
}
} else {
print("INSERT statement could not be prepared.")
}
sqlite3_finalize(insertStatement)
}
// MARK: - Read
func read() -> [PersonModel] {
let queryStatementString = "SELECT * FROM person;"
var queryStatement: OpaquePointer? = nil
var psns : [PersonModel] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
while sqlite3_step(queryStatement) == SQLITE_ROW {
let id = sqlite3_column_int(queryStatement, 0)
let firstName = String(describing: String(cString: sqlite3_column_text(queryStatement, 1)))
let lastName = String(describing: String(cString: sqlite3_column_text(queryStatement, 2)))
let phone = String(describing: String(cString: sqlite3_column_text(queryStatement, 3)))
let address = String(describing: String(cString: sqlite3_column_text(queryStatement, 4)))
psns.append(PersonModel(id: Int(id), firstName: firstName, lastName: lastName, phone: phone, address: address))
print("Query Result:")
print("\(id) | \(firstName) | \(lastName) | \(phone) | \(address)")
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
return psns
}

You should not include the id attribute in your SQL insert statement since it will be automatically handled by the db engine. So change your insert statement to
let insertStatementString = "INSERT INTO person (firstName, lastName, phone, address) VALUES (?, ?, ?, ?);"
When I did this I could run your database related code in a playground and reading values successfully without any runtime error. If you are still getting Unexpectedly found nil while implicitly unwrapping an Optional value error then there is something else that is incorrect, perhaps related to your view controller.

I would recoomend to use Swift package manager to install SQlite3 module by writing the following to your package file:
// Package.swift
dependencies: [
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
]
Build your project: $ swift build
Using a third-party library to manage your database is a common way to access data stored in a Database, it allow you to perform any operation (insert, read, delete) without using string statements, this through by differents methods such as Table, prepare, insert, filter, etc... One of the key features is that you can set different types of fields depending on your data (string, numbers, datetime, arrays) and create any required model structure.
struct PersonModel {
let firstName: String?
let lastName: String?
let phone: String?
let address: String?
}
import Foundation
import UIKit
import SQLite3
class DBManager
{
init()
{
db = openDatabase()
createTable()
}
let dbPath: String = "myDb.sqlite"
var db:OpaquePointer?
func openDatabase() -> OpaquePointer?
{
let fileURL = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
var db = try? Connection(dbPath)
}
func createTable() {
let createTableString = Table("person")
let id = Expression<Int64>("id")
let firstName = Expression<String?>("firstname")
let lastName = Expression<String>("lastname")
let phone = Expression<String>("phone")
let address = Expression<String>("address")
try! db?.run(users.create(ifNotExists: true, block: { (table) in
table.column(id, primaryKey: true)
table.column(firstName)
table.column(lastName)
table.column(phone, unique: true)
table.column(address)
}))
}
func insert(firstName: String, lastName: String, phone: String, address: String, person: Table)
{
let insert = person.insert("firstname" <- firstName, "lastname" <- lastName, "phone" <- phone, "address" <- address)
let rowid = (try! db?.run(insert))!
}
func read(person: Table) -> [PersonModel] {
var psns : [PersonModel] = []
for user in (try! db?.prepare(person))! {
print("Query :id: \(user[id]), name: \(user[firstname]), second name: \(user[lastname]), phone: \(user[phone]), address: \(user[address])")
}
return psns
}
func deleteByID(id:Int, person: Table) {
try! db?.run(person.filter(id).delete())
}
}
note: method used to create a database table createTable is equivalent to this sentence
CREATE TABLE IF NOT EXISTS "users" (
"id" INTEGER PRIMARY KEY NOT NULL,
"firstname" TEXT,
"lastname" TEXT
"phone" TEXT NOT NULL UNIQUE
"address" TEXT
)

Related

How can I save an image using CloudKit?

I've run into a problem saving images using iCloud. After following a few tutorials I was able to save and fetch records containing strings from iCloud but I can't figure out how to do the same with images. Here is some of my code below so that maybe someone can help me figure out what to do next. Any help is appreciated!
// saving to cloud
static func save(user: AppUser, completion: #escaping (Result<AppUser, Error>) -> ()) {
let userRecord = CKRecord(recordType: RecordType.User)
userRecord["displayName"] = user.displayName as CKRecordValue
userRecord["pfp"] = user.pfp as CKRecordValue // <-- **not sure if this is right?**
userRecord["username"] = user.username as CKRecordValue
userRecord["password"] = user.password as CKRecordValue
userRecord["bio"] = user.bio as CKRecordValue
CKContainer.default().privateCloudDatabase.save(userRecord) { (record, err) in
DispatchQueue.main.async {
if let err = err {
completion(.failure(err))
return
}
guard let record = record else {
completion(.failure(CloudFuncUserError.recordFailure))
return
}
let recordID = record.recordID
guard let displayName = record["displayName"] as? String else {
completion(.failure(CloudFuncUserError.castFailure))
return
}
guard let pfp = record["pfp"] as? CKAsset else {
completion(.failure(CloudFuncUserError.castFailure))
return
} // <-- **not sure if this is right?**
guard let username = record["username"] as? String else {
completion(.failure(CloudFuncUserError.castFailure))
return
}
guard let password = record["password"] as? String else {
completion(.failure(CloudFuncUserError.castFailure))
return
}
guard let bio = record["bio"] as? String else {
completion(.failure(CloudFuncUserError.castFailure))
return
}
let appUser = AppUser(recordID: recordID, displayName: displayName, pfp: pfp, username: username, password: password, bio: bio)
completion(.success(appUser))
}
}
}
initialization
#main
struct TestForReleaseApp: App {
var user = appUser()
var isUser = AppUser(displayName: "", pfp: CKAsset <-- (this is a placeholder, I don't know what goes here), username: "", password: "", bio: "")
var posts = Posts()
var post = Post(displayName: "", username: "", content: "", createdAt: Date(), star: 0, stared: 0, repost: 0, reposted: 0, share: 0, shared: 0, comment: 0, commented: 0, comments: [""], isPublic: 0, isPremium: 0)
var body: some Scene {
WindowGroup {
ContentView(post: post, user: isUser, appUser: user).environmentObject(user).environmentObject(posts).preferredColorScheme(.dark)
}
}
}

Struggling to pass a single document through Firestore - Swift

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)
}
}
}
}

Every column has last columns value. SQLITE DATABASE

i am getting same output for every column in the row.
my output should be like
"jack 1 blast male"
but i get like
"male 1 male male"
every column has last columns value.
my whole code is listed below.
please help i am new into sqlite .
i don't know where i should change the code to make it correct please help .
import UIKit
import SQLite3
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var lab1: UILabel!
var db: OpaquePointer?
var heroList = [Hero]()
#IBOutlet weak var tableViewHeroes: UITableView!
#IBAction func buttonSave(_ sender: UIButton) {
let name = "jack"
let powerRanking = "1"
let power = "blast"
let gender = "Male"
var stmt: OpaquePointer?
let queryString = "INSERT INTO Heroes (name, powerrank, power, gender) VALUES (?,?,?,?)"
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error preparing insert: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_int(stmt, 2, (powerRanking as NSString).intValue) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 3, power, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 4, gender, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure inserting hero: \(errmsg)")
return
}
readValues()
print("Herro saved successfully")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return heroList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")
let hero: Hero
hero = heroList[indexPath.row]
cell.textLabel?.text = hero.id.description + (hero.name?.description)! + (hero.powerRanking.description) + (hero.power?.description)! + (hero.gender?.description)!
return cell
}
func readValues(){
heroList.removeAll()
let queryString = "SELECT * FROM Heroes ORDER BY id DESC LIMIT 1;"
var stmt:OpaquePointer?
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error preparing insert: \(errmsg)")
return
}
while(sqlite3_step(stmt) == SQLITE_ROW){
let id = sqlite3_column_int(stmt, 0)
let name = String(cString: sqlite3_column_text(stmt, 1))
let powerrank = sqlite3_column_int(stmt, 2)
let power = String(cString: sqlite3_column_text(stmt, 3))
let gender = String(cString: sqlite3_column_text(stmt, 4))
heroList.append(Hero(id: Int(id), name: String(name), powerRanking: Int(powerrank), power: String(power), gender: String(gender)))
}
self.tableViewHeroes.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("HeroesDatabase.sqlite")
if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
print("error opening database")
}
if sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS Heroes (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255), powerrank INTEGER, power VARCHAR(255), gender VARCHAR(255))", nil, nil, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error creating table: \(errmsg)")
}
readValues()
}
}
by giving encoding to string will solve problem
if sqlite3_bind_text(stmt, 2, (monthen as NSString).utf8String, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}

Why am I NOT getting an error when I test password validation?

As you can likely tell by this question I'm new to iOS/programming: I'm trying to implement password validation in which the user input must be at least 8 characters, contain 1 upper case letter, 1 lower case letter, 1 number, and 1 special character. I decided to use a regex and an extension on UIViewController to do this. I read somewhere that I have to include this outside of my "SignUpViewController" scope (not sure why) which is what I've done. The db does have open read/write rules since I'm still testing things so is that the reason it's not checking for this validation and signing up the user every time? Please help
Below is the code:
import UIKit
import Firebase
class SignUpViewController: UIViewController {
//Outlets
#IBOutlet weak var firstNameText: UITextField!
#IBOutlet weak var lastNameText: UITextField!
#IBOutlet weak var emailText: UITextField!
#IBOutlet weak var passwordText: UITextField!
#IBOutlet weak var signUpButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func signUpButtonTapped(_ sender: Any) {
guard let firstName = firstNameText.text,
let lastName = lastNameText.text,
let email = emailText.text else { return }
guard emailText.containsValidEmail() else {
print("Invalid email. Please try again.")
return
}
let password = passwordText.text else { return }
guard passwordText.containsValidPassword() else {
print("Invalid password. Please try again.")
return
}
Auth.auth().createUser(withEmail: email, password: password) { (authResult, error) in
if let error = error {
debugPrint("Error creating user: \(error.localizedDescription)")
}
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = firstName
changeRequest?.commitChanges(completion: { (error) in
if let error = error {
debugPrint(error.localizedDescription)
}
})
guard let userId = authResult?.user else { return }
let userData: [String: Any] = [
"firstName" : "",
"lastName" : "",
"User ID" : userId,
"dateCreated" : FieldValue.serverTimestamp(),
]
let db = Firestore.firestore()
db.collection("users").document("one").setData(userData) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
}
}
}
public extension UITextField {
func containsValidPassword() -> Bool {
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[d$#$!%*?&#])[A-Za-z\\dd$#$!%*?&#]{8,}"
return NSPredicate(format: "SELF MATCHES %#", passwordRegex).evaluate(with: self.text)
}
}
public extension UITextField {
func containsValidEmail() -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluate(with: self.text)
}
}
UPDATE 12/14:
I edited the original post by adding extensions for password and email validation. I added password validation by using an extension on UITextField. However, when I try to do the same for the email I get compile errors. Why is this?
First of all, you're not calling the isValidPassword function anywhere in your code. Secondly, if you'd actually call it, it would cause a runtime exception, since you'd be trying to pass a UIViewController instance to an NSPredicate to match on a regex.
You should rather define isValidPassword on UITextField and validate its text property. (I'd also rename the function to containsValidPassword in this case, since the UITextField isn't a password, it just contains the String for the password.)
extension UITextField {
func containsValidPassword() -> Bool {
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[d$#$!%*?&#])[A-Za-z\\dd$#$!%*?&#]{8,}"
return NSPredicate(format: "SELF MATCHES %#", passwordRegex).evaluate(with: self.text)
}
}
Then you also need to call the validation:
#IBAction func signUpButtonTapped(_ sender: Any) {
guard let firstName = firstNameText.text,
let lastName = lastNameText.text,
let email = emailText.text,
let password = passwordText.text else { return }
guard passwordText.containsValidPassword() else {
// Let the user know that their pwd is incorrect
return
}
...
You're evaluating your predicate with self where self is of type UIViewController (that's what you're extending), you've probably meant to extend String instead.
extension String {
func isValidPassword() -> Bool {
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[d$#$!%*?&#])[A-Za-z\\dd$#$!%*?&#]{8,}"
return NSPredicate(format: "SELF MATCHES %#", passwordRegex).evaluate(with: self)
}
}
Usage:
let isValidPassword = "something".isValidPassword()
Actually I'm not sure if extensions suit this condition, I would recommend you to create a simple function inside your SignUpViewController-
func isValidPassword(password: String) -> Bool {
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[d$#$!%*?&#])[A-Za-z\\dd$#$!%*?&#]{8,}"
return NSPredicate(format: "SELF MATCHES %#", passwordRegex).evaluate(with: password)
}
Or if you want to be more specific-
func isValidPassword() -> Bool{
guard let password = passwordText.text else { return false }
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[d$#$!%*?&#])[A-Za-z\\dd$#$!%*?&#]{8,}"
return NSPredicate(format: "SELF MATCHES %#", passwordRegex).evaluate(with: password)
}
import UIKit
import Firebase
class SignUpViewController: UIViewController {
//Outlets
#IBOutlet weak var firstNameText: UITextField!
#IBOutlet weak var lastNameText: UITextField!
#IBOutlet weak var emailText: UITextField!
#IBOutlet weak var passwordText: UITextField!
#IBOutlet weak var signUpButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func signUpButtonTapped(_ sender: Any) {
guard let firstName = firstNameText.text,
let lastName = lastNameText.text,
let email = emailText.text else { return }
guard emailText.containsValidEmail() else {
print("Invalid email/password. Please try again.")
return
}
guard let password = passwordText.text else { return }
guard passwordText.containsValidPassword() else {
print("Invalid email/password. Please try again.")
return
}
Auth.auth().createUser(withEmail: email, password: password) { (authResult, error) in
if let error = error {
debugPrint("Error creating user: \(error.localizedDescription)")
}
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = firstName
changeRequest?.commitChanges(completion: { (error) in
if let error = error {
debugPrint(error.localizedDescription)
}
})
guard let userId = authResult?.user else { return }
let userData: [String: Any] = [
"firstName" : "",
"lastName" : "",
"User ID" : userId,
"dateCreated" : FieldValue.serverTimestamp(),
]
let db = Firestore.firestore()
db.collection("users").document("one").setData(userData) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
}
}
}
public extension UITextField {
func containsValidPassword() -> Bool {
let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[d$#$!%*?&#])[A-Za-z\\dd$#$!%*?&#]{8,}"
return NSPredicate(format: "SELF MATCHES %#", passwordRegex).evaluate(with: self.text)
}
}
public extension UITextField {
func containsValidEmail() -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluate(with: self.text)
}
}
This format runs as expected. Thank you

Data is being retrieved as project.model not as elements in swift ios

I am trying to retrieve the data elements from DataTable in SQLite database but when I retrieve the data I am getting the data as myproject.mymodelclass where WidgetData is my model class and myproject is the the name of my project on which I am working here is the snapshot of console
[MyProject.WidgetData, MyProject.WidgetData, MyProject.WidgetData, MyProject.WidgetData]
But I want all the elements of DataTable should be printed in ViewController class.
WidgetData Class
import Foundation
class WidgetData {
var widgetid: Int64?
var widgetname: String = ""
var widgetdescription : String = ""
var widgetform: String = ""
var formid : Int64?
var formname : String = ""
var formdescription : String = ""
var formcategory : String = ""
init(widgetid: Int64) {
self.widgetid = widgetid
}
init(formid: Int64) {
self.formid = formid
}
init(widgetid: Int64, widgetname: String, widgetdescription: String, widgetform: String) {
self.widgetid = widgetid
self.widgetname = widgetname
self.widgetdescription = widgetdescription
self.widgetform = widgetform
}
init(formid: Int64, formname : String, formdescription : String, formcategory : String) {
self.formid = formid
self.formname = formname
self.formdescription = formdescription
self.formcategory = formcategory
}
}
StephencelisDB class
import Foundation
class WidgetData {
var widgetid: Int64?
var widgetname: String = ""
var widgetdescription : String = ""
var widgetform: String = ""
var formid : Int64?
var formname : String = ""
var formdescription : String = ""
var formcategory : String = ""
init(widgetid: Int64) {
self.widgetid = widgetid
}
init(formid: Int64) {
self.formid = formid
}
init(widgetid: Int64, widgetname: String, widgetdescription: String, widgetform: String) {
self.widgetid = widgetid
self.widgetname = widgetname
self.widgetdescription = widgetdescription
self.widgetform = widgetform
}
init(formid: Int64, formname : String, formdescription : String, formcategory : String) {
self.formid = formid
self.formname = formname
self.formdescription = formdescription
self.formcategory = formcategory
}
}
ViewController in which I am retrieving the values
//SQLite start
var dataList = [WidgetData]()
var db: OpaquePointer?
var stmt: OpaquePointer?
var widgetnameV = String()
var widgetdescriptionV = String()
var widgetformV = String()
private var contacts = [WidgetData]()
private var selectedContact: Int?
func formDataSetup() {
let queryString = "INSERT INTO WidgetTable (widgetname, widgetdescription, widgetform) VALUES (?,?,?)"
//preparing the query
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error preparing insert: \(errmsg)")
return
}
//binding the parameters
if sqlite3_bind_text(stmt, 1, widgetnameV, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 2, widgetdescriptionV, -1, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 3, widgetformV, -1, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure binding name: \(errmsg)")
return
}
//executing the query to insert values
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure inserting hero: \(errmsg)")
return
}
}
func readValues(){
//first empty the list of heroes
// heroList.removeAll()
//this is our select query
let queryString = "SELECT * FROM WidgetTable"
//
print(dataList)
//statement pointer
var stmt:OpaquePointer?
//preparing the query
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error preparing insert: \(errmsg)")
return
}
//traversing through all the records
while(sqlite3_step(stmt) == SQLITE_ROW){
let widgetid = sqlite3_column_int(stmt, 0)
let widgetnameC = String(cString: sqlite3_column_text(stmt, 1))
let widgetdescriptionC = String(cString: sqlite3_column_text(stmt, 2))
let widgetformC = String(cString: sqlite3_column_text(stmt, 3))
//adding values to list
dataList.append(WidgetData(widgetid: Int64(Int(widgetid)), widgetname: String(describing: widgetnameC), widgetdescription: String(describing: widgetdescriptionC), widgetform: String(describing: widgetformC)))
print(dataList)
}
}
implement description var to print the class properties
class WidgetData:NSObject
{
var tripId: String
var name: String
init(tripId: String ,name:String)
{
self.tripId = tripId
self.name = name
}
override var description: String
{
return "\(self.tripId) \(self.name)"
}
}
Update
Your problem is that you have more than one init , first I advice isolating the data to other class , or in description check first item in every init and use ! if all the init values are not nil

Resources