Array appending not working across files in swift - ios

Im currently implementing a like functionality in my app, and I can't seem to be able to get this append and retrieving to work. Here is my Button Action which is found in my ViewController file
var Liked = Favourite()
let factBook = FactBook()
#IBAction func favour() {
var currentQuote = factBook.factsArray[factIndex]
Liked.favouriteArray.append(currentQuote)
}
The Favourite struct is called from
import Foundation
struct Favourite {
var favouriteArray: [String] = []
}
(The factBook struct is the same thing except the array actually has elements inside.)
Now my goal is to get all this to display on a separate view controller called favouriteViewController:
import UIKit
class FavouriteViewController: UIViewController {
#IBOutlet var LikeQuote: UILabel!
var liked = Favourite()
override func viewDidLoad() {
super.viewDidLoad()
if liked.favouriteArray.count > 0 {
LikeQuote.text = liked.favouriteArray[0]
} else if liked.favouriteArray.count == 0 {
LikeQuote.text = "No Liked Quotes Found, Go Favour Some!"
}
}
Now when I hit the button, theoretically I should be able to append it to the favouriteArray and then be able to display it on my favouriteViewController file, however when I save it and then open viewcontroller file it defaults to the liked.favouriteArray.count=0 scenario and prints out the text no matter how many quotes I save. I just need an idea of what's going wrong in this process?
Update: If I put append Hello world into text it still does not append to element and evaluates the array value as 0.

The problem is that because you are not saving the data somewhere like into a database or file, you won't be able to retrieve it so the code you add into the viewDidLoad will not work The only way for it to work is when the UIButton was tapped check the code below. Hope that Helps
import Foundation
import UIKit
struct Favourite {
var favouriteArray = [String]()
}
class FavouriteViewController: UIViewController {
// let factBook = FactBook()
#IBOutlet var LikeQuote: UILabel!
var liked = Favourite()
override func viewDidLoad() {
super.viewDidLoad()
}
func updateContent(){
if liked.favouriteArray.count > 0 {
LikeQuote.text = liked.favouriteArray[0]
}
else if liked.favouriteArray.count == 0 {
LikeQuote.text = "No Liked Quotes Found, Go Favour Some!"
}
}
#IBAction func favour() {
// var currentQuote = liked.factsArray[factIndex]
// Liked.favouriteArray.append(currentQuote)
liked.favouriteArray.append("today is a new day ")
updateContent()
}
}

Related

Swift NavigationBar Press "Back" to get values, why?

I am using some values to perform some calculations. For testing purposes I show in Label1 a value as string, since it is stored as a string and in Label2 I show a casted value as a Double since I need them at the end as doubles for my calculations.
The weird thing is, that when I access the ViewController the first time it doesn't show any values. But if I go back and klick on it again using the navigation controller it actually works. But I need the values right away cause my original intention is as I said, not showing some labels but rather making some calculations with it.
I made a little gif to show you what the problem is but I have problem with adding photos. Basically what happens is, that I click on the ViewController with the labels and nothing is showed. I go back and press again and the values will be showed in the labels.
Why is that and how can it be showed right away/ used for calculations right away
Thanks for the help. :)
class AHPfinalPreferencesViewController: UIViewController {
var ahpPrios = [AHPPriorityStruct]()
let decoder = JSONDecoder()
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
let ajkpXc = globaLajkpXc
let ajkpXijr = globaLajkpXijr
let valueA = globaLajkpXc
let valueB = Double(globaLajkpXijr)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UserService.ahpPref(for: User.current) { (ahpPrios) in
self.ahpPrios = ahpPrios
print("This is our AHP PRIOS", ahpPrios)
for ahpPrio in ahpPrios {
print(ahpPrio)
}
print("this is the global ajk. ", self.ajkpXc)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Mark: - Get Data
label1.text = valueA
label2.text = "\(String(describing: valueB))"
// MARK: - Set Values for calculation
// setValues()
// ahpCalculation()
}
}
Could it be because of the globalVariables? I know that it is not the right way to do it but for my purposes its absolutely "okay"
import Foundation
import FirebaseAuth.FIRUser
import FirebaseDatabase
import FirebaseUI
import FirebaseAuth
import CodableFirebase
var globaLajkpXc: String = String()
var globaLajkpXijr: String = String()
var globaLajkpXqpa: String = String()
struct UserService {
static func ahpPref(for user: User, completion: #escaping ([AHPPriorityStruct]) -> Void) {
let ref = Database.database().reference().child("AHPRatings").child(user.uid)
ref.observe(DataEventType.value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let ahpPrios = try FirebaseDecoder().decode(AHPPriorityStruct.self, from: value)
print(ahpPrios)
// MARK: - lets store the values in the actual constants :)
let ajkpXc = ahpPrios.ajkpXc
let ajkpXijr = ahpPrios.ajkpXijr
let ajkpXqpa = ahpPrios.ajkpXqpa
globaLajkpXc = ajkpXc ?? "no Value"
globaLajkpXijr = ajkpXijr ?? "no Value"
globaLajkpXqpa = ajkpXqpa ?? "no Value"
} catch let error {
print(error)
}
})
}
}
[1]: https://i.stack.imgur.com/VKxaE.png
You are calling UserService's ahpPref in your controller's viewWillAppear. BUT you are attempting to put your valueA (globaLajkpXc's value) to your label in your controller's viewDidLoad.
So what does that mean? Do you know which of these two controller's life cycle method gets called and when they do get called?
To solve your problem, have your label assigning value code
label1.text = globaLajkpXc
move in the completion block of your ahpPref (in the viewWillAppear).
Here's the Apple's documentation about the UIViewController's lifecycle: https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html
Also, below this line: globaLajkpXqpa = ajkpXqpa ?? "no Value"
add your completion call, like:
completion([ahpPrios]).
This should make my answer above work.

Set up WatchKit Table

I'm trying to load my data into a WatchKit table. Basically, set the text of the match label in each table group cell with the array of matchs I have.
I've got the data, and everything set up, but actually loading it into the table is where I'm stuck.
InterfaceController.swift:
var receivedData = Array<Dictionary<String, String>>()
var eventsListSO = Array<Event>()
#IBOutlet var rowTable: WKInterfaceTable!
func doTable() {
eventsListSO = Event.eventsListFromValues(receivedData)
rowTable.setNumberOfRows(eventsListSO.count, withRowType: "rows")
for var i = 0; i < self.rowTable.numberOfRows; i++ {
let row = rowTable.rowControllerAtIndex(i) as? TableRowController
for eventm in eventsListSO {
row!.mLabel.setText(eventm.eventMatch)
NSLog("SetupTableM: %#", eventm.eventMatch)
}
}
}
I was trying to do it in doTable because that seemed like best place to do this, and I think doTable is set up right, but I'm not sure? Not sure if I need to make the array an optional type or what.
Here is the referencing code if needed:
RowController.swift:
class TableRowController {
#IBOutlet var mLabel: WKInterfaceLabel!
#IBOutlet var cGroup: WKInterfaceGroup!
}
Event.swift:
class Event {
var eventTColor:String
var eventMatch:String
init(dataDictionary:Dictionary<String,String>) {
eventTColor = dataDictionary["TColor"]!
eventMatch = dataDictionary["Match"]!
}
class func newEvent(dataDictionary:Dictionary<String,String>) -> Event {
return Event(dataDictionary: dataDictionary)
}
class func eventsListFromValues(values: Array<Dictionary<String, String>>) -> Array<Event> {
var array = Array<Event>()
for eventValues in values {
let event = Event(dataDictionary: eventValues)
array.append(event)
}
return array
}
}
So I'm not sure if:
- doTable is set up right (can't be because eventsListSO.count is null)
The way you work with tables in WatchKit is a lot different than UIKit.
After you call setNumberOfRows you need to iterate over each row and get the RowController.
for var i = 0; i < self.rowTable.numberOfRows; i++ {
var row = self.rowTable.rowControllerAtIndex(i)
//setup row here
}
You can check Raywenderlich's tutorial about WatchKit: http://www.raywenderlich.com/96741/watchkit-tutorial-with-swift-tables-glances-and-handoff, it teach you how to show tables on your watch, hope this help!

Vote implementation in parse

I am stuck for a very long time. I am trying to implement a vote feature in a collection view. If the user taps the button it adds one vote to parse and shows it on the label. My code does that however when I look into the parse dashboard I see that a new row is create and the number of votes is not going into the post
My code for the cell is
import UIKit
import ParseUI
import Parse
var votes = [PFObject]()
class NewCollectionViewCell: UICollectionViewCell {
var parseObject = PFObject(className: "Posts")
#IBOutlet weak var postsImageView: PFImageView!
#IBOutlet weak var postsLabel: UILabel!
#IBOutlet weak var votesLabel:UILabel?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
postsLabel.textAlignment = NSTextAlignment.Center
print("Passing11")
}
#IBAction func vote(sender: AnyObject) {
if let votes = parseObject.objectForKey("votes") as? Int {
parseObject.setObject(votes + 1, forKey: "votes")
parseObject.saveInBackgroundWithTarget(nil, selector: nil)
votesLabel?.text = "\(votes + 1) votes"
print("Passing22")
}
else
{
parseObject.setObject(1, forKey: "votes")
parseObject.saveInBackgroundWithTarget(nil, selector: nil)
votesLabel?.text = "1 votes"
print("Passing33")
}
}}
and collection view is
if let votes = parseObject.objectForKey("votes") as? Int {
cell.votesLabel?.text = "\(votes) votes"
}
else
{
cell.votesLabel?.text = "0 votes"
}
return cell
}
How can I make it work? Thank you.
From what I remember in my Parse project. If you need to retrieve and update an existing row in Parse you need to create a PFQuery object first and retrieve the desired row using that query object. And then you can update its "vote" or whatever attribute value you want to. Kindly try that.

How to create an if statement with an array in swift?

I am trying to make an app that uses a login system. I have created an array for both the usernames and the passwords. But I need to test if the password is correct or not. How can I create an if statement that tests for values that are inside of an array? Here is my code in case you need it.
import UIKit
var namesArray = [String]()
var passWordArray = [String]()
class ViewController: UIViewController {
var username = "Ege"
var password = "Gürkan"
var trueFalse = true
#IBOutlet weak var idTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var trueWrongLabel: UILabel!
#IBAction func buttonPressed(sender: AnyObject) {
if idTextField.text == "Ege" && passwordTextField.text == "Gürkan" {
self.performSegueWithIdentifier("geçiş", sender: self)
} else {
trueWrongLabel.text = "Wrong id/pw"
idTextField.resignFirstResponder()
passwordTextField.resignFirstResponder()
trueWrongLabel.textColor = UIColor.redColor()
idTextField.text = nil
passwordTextField.text = nil
}
}
override func viewDidLoad() {
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setObject(namesArray, forKey: "nameArray")
var recalledNameArray = NSUserDefaults.standardUserDefaults().objectForKey("nameArray")! as NSArray
NSUserDefaults.standardUserDefaults().setObject(passWordArray, forKey: "passWordArray")
var recalledPassWordArray: AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey("passWordArray")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In the line that says: if isTextField.text == "Ege"
I want to test if there is a value for what has the user just entered in the textfield. How can I do it?
PS: I am very new to programming in general so please don't use complicated terms while explaining
This is beginning programming stuff.
There are lots of ways to do this.
One straightforward way would be to create a struct that contains a name property and a password property. Then create an array of the structs. When the user enters a username and password, use a filter command to find the structure that contains the matching username, and then check to see if the password is correct.
If you're not comfortable with the Swift filter function (I haven't used it yet myself) then you could also loop through the array yourself looking for matches.
Why you are using Array for userName and Array for Password?
How can you know if that password for that userName?
I advice you to use Dictionary it is the best solution for you.
so one Dictionary for username and password and the values will be:
key:value
Ege:Gürkan
and the code will be:
for (key,value) in dic
{
if (key == idTextField.text && value == passwordTextField.text)
{
println("true")
}
}
I hope this is what you want!.

Swift: Set UITextField.text from class (retrieved value from SQLite)

I am using Swift with SQLite.swift. I have the following UIViewController:
class LoginViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
func setEmailAddress(email:String){
emailField.text = email
}
override func viewDidLoad() {
MySQLite().updateLatestEmailAddressFromUserTable() // breaks here (email is in console, though...)
}
}
Then I am trying to update it's value (through the setEmailAddress function) from another class:
class MySQLite {
func updateLatestEmailAddressFromUserTable(){
let dbPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String
let db = Database("\(dbPath)/db.sqlite3")
let users = db["users"]
let id = Expression<Int>("id")
let email = Expression<String>("email")
let time = Expression<Int>("time")
for user in users.limit(1).order(time.desc) {
println(user[email]) // this works, correctly outputs in console: email#domain.com
LoginViewController().setEmailAddress(user[email]) // breaks here
}
}
}
above code gives me the following error
fatal error: unexpectedly found nil while unwrapping an Optional value
To explain a little further: I am retrieving the most recent entry in SQLite table to get the user's email address and update the text field in the login view controller. This allows for easier log in for returning users.
I have been struggling with this for over 2 hours now and trying various things. The main problem I believe is that when I try to simply return the email address as string from my second function and set the field directly from LoginViewController, it doesn't work (SQLite related code was not "executed" yet I believe).
possibly related thread (Obj-C):
set UITextField.text from another class
Here whats happening LoginViewController().setEmailAddress(user[email]) creates new instance of LoginViewController which is not same as your current LoginViewController.
Why don't you make protocol and define as delegate in MySQLite
And LoginViewController will have implementation of update method. Pass the delegate to MySqlite
In MySQLite when you get the value form database call the delegate update method.
Example
MySQLite
protocol loginDelegate
{
func update(NSString)
}
class MySQLite {
var delegate:loginDelegate?
func updateLatestEmailAddressFromUserTable(){
let dbPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String
let db = Database("\(dbPath)/db.sqlite3")
let users = db["users"]
let id = Expression<Int>("id")
let email = Expression<String>("email")
let time = Expression<Int>("time")
for user in users.limit(1).order(time.desc) {
println(user[email]) // this works, correctly outputs in console: email#domain.com
if((delegate) != nil)
{
delegate?.update("example#example.com")
}
}
}
}
class LoginViewController: UIViewController,loginDelegate {
#IBOutlet weak var emailField: UITextField!
func setEmailAddress(email:String){
emailField.text = email
}
override func viewDidLoad() {
var mySQLite: MySQLite=LoginClass();
mySQLite.delegate=self;
[mySQLite .updateLatestEmailAddressFromUserTable()];
}
func update(email: NSString) {
println(email);
emailField.text = email
}
}
Make sure that the view which has the emailField has been instantiated on the screen.
#IBOutlet weak var emailField: UITextField!
This is an optional, which will be nil until the storyboard or nib for it is loaded. I assume OnBoardingRegistrationFormController is an instance of your LoginViewController class?
I see you've accepted an answer, but in this case creating a protocol is likely overkill. If sqlite is your model, why not just have the function return a value, and then you can assign the value to the text field in the controller. ex.
class LoginViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
override func viewDidLoad() {
emailField.text = MySQLite().updateLatestEmailAddressFromUserTable()
}
}
class MySQLite {
func updateLatestEmailAddressFromUserTable() -> String{
let dbPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String
let db = Database("\(dbPath)/db.sqlite3")
let users = db["users"]
let id = Expression<Int>("id")
let email = Expression<String>("email")
let time = Expression<Int>("time")
for user in users.limit(1).order(time.desc) {
println(user[email]) // this works, correctly outputs in console: email#domain.com
return user[email]
}
}
}
The issue is that LoginViewController's view isn't loaded when you try to assign a text to the textField. i.e: emailField is nil and unwrapping nil values leads to a runtime crash (since the outlet has not been connected to it's storyboard/xib counterpart).

Resources