Trying to call a function in my code, not working - swift

So I have created a function called startover1() and I am trying to call it in a different function, however, Xcode keeps saying there is an error.
I tried putting the function in different places, but it seems as though the function is not being read.
import Foundation
import UIKit
class BU: UIViewController {
let allBUSentences = BUSentenceBank()
var sentenceNumber2 : Int = 0
#IBOutlet weak var BUSentenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let BUfirstQuestion = allBUSentences.list2[sentenceNumber2]
BUSentenceLabel.text = BUfirstQuestion.BUSentenceText
}
#IBOutlet var BUNextButton: UIButton!
#IBAction func BUNext2(_ sender: Any) {
sentenceNumber2 = sentenceNumber2 + 1
nextSentence()
}
func nextSentence() {
if sentenceNumber2 <= 19 {
BUSentenceLabel.text = allBUSentences.list2[sentenceNumber2].BUSentenceText
}
else{
let alert2 = UIAlertController(title: "Game Over", message: "Restart and keep drinking?", preferredStyle: .alert)
let restartAction2 = UIAlertAction(title: "Restart", style: .default) { (UIAlertAction) in self.startover1()
}
alert2.addAction(restartAction2)
present(alert2, animated: true, completion: nil)
}
func startover1() {
sentenceNumber2 = 0
nextSentence()
}
}
}
Hopefully it will recognize the method. The error is Value of type 'BU' has no member 'startover1'

The problem is that nextSentence is nested inside another func declaration. To make it work simply remove the declaration to outside of the previous function scope defined by the {brackets}. Check out:
import Foundation
import UIKit
class BU: UIViewController {
let allBUSentences = BUSentenceBank()
var sentenceNumber2 : Int = 0
#IBOutlet weak var BUSentenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let BUfirstQuestion = allBUSentences.list2[sentenceNumber2]
BUSentenceLabel.text = BUfirstQuestion.BUSentenceText
}
#IBOutlet var BUNextButton: UIButton!
#IBAction func BUNext2(_ sender: Any) {
sentenceNumber2 = sentenceNumber2 + 1
nextSentence()
}
func nextSentence() {
if sentenceNumber2 <= 19 {
BUSentenceLabel.text = allBUSentences.list2[sentenceNumber2].BUSentenceText
}
else{
let alert2 = UIAlertController(title: "Game Over", message: "Restart and keep drinking?", preferredStyle: .alert)
let restartAction2 = UIAlertAction(title: "Restart", style: .default) { (UIAlertAction) in self.startover1()
}
alert2.addAction(restartAction2)
present(alert2, animated: true, completion: nil)
}
startover1() //This is not a declaration, but a call
}
//This is startover1 declaration
func startover1() {
sentenceNumber2 = 0
nextSentence()
}
}

Related

How to save position in an array and a view count using UserDefaults?

first time poster here. I want to save the position in an array and a view count individually for each item in the array. I have a struct which handles the data here;
import Foundation
struct Vocab {
let vocabTitle: String
let vocabHiragana: String
let englishTranslation: String
}
I then call it using a separate swift file with a struct:
import Foundation
var vocabNumber = 0
struct VocabBuilder {
let testVocab = [ Vocab(vocabTitle: "上達", vocabHiragana: "じょうたつ", englishTranslation: "Improvement"),
Vocab(vocabTitle: "乗り越える", vocabHiragana: "のりこえる", englishTranslation: "To Push Through"),
Vocab(vocabTitle: "耐久性", vocabHiragana: "たいきゅうせい", englishTranslation: "Durability"),
Vocab(vocabTitle: "促す", vocabHiragana: "うながす", englishTranslation: "To doubt"),
Vocab(vocabTitle: "一律", vocabHiragana: "いちりつ", englishTranslation: "Across the board"),
Vocab(vocabTitle: "押し切り", vocabHiragana: "おしきり", englishTranslation: "Objection"),
Vocab(vocabTitle: "打ち込む", vocabHiragana: "うちこむ", englishTranslation: "To input") ,
Vocab(vocabTitle: "溶け込む", vocabHiragana: "とけこむ", englishTranslation: "To get used to"),
Vocab(vocabTitle: "放り込む", vocabHiragana: "ほうりこむ", englishTranslation: "Throw into (something)"),
Vocab(vocabTitle: "後継者", vocabHiragana: "こうけいしゃ", englishTranslation: "Successor") ]
func getVocab() -> String {
return testVocab[vocabNumber].vocabTitle
}
func getHiragana() -> String {
return testVocab[vocabNumber].vocabHiragana
}
func getEnglishTranslation() -> String {
return testVocab[vocabNumber].englishTranslation
}
mutating func nextVocab() {
NSLog("Before", "\(vocabNumber)")
vocabNumber = (vocabNumber + Int.random(in: 0...9)) % testVocab.count
NSLog("After", "\(vocabNumber)")
}
}
The problem I have is that in my main ViewController is that I want to save the position in an array including all the functions that return strings into the ViewController:
import UIKit
class ViewController: UIViewController {
var greySquare: UIView!
var answerBox: UILabel!
var continueButton: UIButton!
var inputButton: UIButton!
var rightWrongAnswerBox: UILabel!
let rightWrongText = String()
var englishTranslationBox: UILabel!
var hiraganaBox: UILabel!
var viewCountBox: UILabel!
var userInputs: String?
var viewedVocab = [String]()
var pictureViewCount: Int = 0
var vocabInfo = VocabBuilder() //create var to handle the struct info
override func viewDidLoad() {
super.viewDidLoad()
func loadView() {
view = UIView()
view.backgroundColor = .white
// initialised views for each box here
NSLayoutConstraint.activate([
// had the constraints here for the views.
])
loadView()
loadUI()
let defaults = UserDefaults.standard
//let savedData = pictureViewCount
if let savedViewCount = defaults.value(forKey: "picVC") as? Int {
print("savedValue: \(savedViewCount + 1)")
}
}
func loadUI() {
answerBox.text = vocabInfo.getVocab()
englishTranslationBox.text = vocabInfo.getEnglishTranslation()
hiraganaBox.textColor = .white
hiraganaBox.text = vocabInfo.getHiragana()
viewCountBox.text = ("View Count: \(pictureViewCount)"}
}
rightWrongAnswerBox.text = "Try and Remember!"
rightWrongAnswerBox.textColor = .black
pictureViewCount += 1
save()
}
func submit(answer: String) {
if hiraganaBox.text == answer {
let ac = UIAlertController(title: "Correct!", message: "Go to the next word!", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
hiraganaBox.textColor = .black
rightWrongAnswerBox.text = "Correct!"
rightWrongAnswerBox.textColor = .green
} else if hiraganaBox.text != answer {
let ac = UIAlertController(title: "Incorrect!", message: "Give it another go.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
rightWrongAnswerBox.text = "Incorrect"
rightWrongAnswerBox.textColor = .red
}
}
#objc func answerAttempt (_ sender: UIButton) {
let ac = UIAlertController(title: "What is this word in Hiragana?", message: "Enter your answer into the box below", preferredStyle: .alert)
ac.addTextField()
let submitAction = UIAlertAction(title: "Submit", style: .default){
[weak ac] action in// make a closure to handle text field.
guard let answer = ac?.textFields?[0].text else { return }
self.submit(answer: answer)
}
ac.addAction(submitAction)
present(ac, animated: true)
}
#objc func continueToNextWord (_ sender: UIButton) {
vocabInfo.nextVocab()
loadUI()
}
func save(){
//let jsonEncoder = JSONEncoder()
//if let saveNumber = try?jsonEncoder.encode(pictureViewCount) {
// let savedWord = try?jsonEncoder.encode(vocabInfo.getVocab())
let defaults = UserDefaults.standard
let savedData = pictureViewCount
let currentCount = UserDefaults.standard.integer(forKey: "picVC")
UserDefaults.standard.setValue(currentCount + 1, forKey: "picVC")
defaults.set(saveData, forKey: "pictureViewCount")
defaults.setValue(savedWord, forKey: "savedWord")
} else {
print("Failed to save View Count.")
}
}
}
What I've tried:
The issue I have is that I'm just not sure after looking at tutorials where saving arrays. What I've tried is using JSON to save and encode and decode the user defaults to keep the position in the array loaded up when the app is closed and loaded again. Also I tried to increase the picture view count for each item in the array, which is randomly loaded up and save it as an integer but it just increases the view count when the continueToNextWord function is called.
View Controller as it is now so when I press the next word button I want it to save the view count on the randomly shown words and assign it to that item.
I'm gonna try some different ways to get the UserDefaults working but any help will be appreciated.
If its not clear I can also use DMs.

Is there a way to throw errors from firebase login to error handling?

I have one class that i call RealTimeAPI and then the actual LoginViewController class and what i want to do is i want to add my register method in my RealtimeApi class and call register in my LoginView. But then i need to errorhandle in my LoginView so i need to throw the error from the completionhandler in the registerfunction RealtimeAPIs Createuser. is this possible or no? this is my code so far.
RealtimeAPIClass
import Foundation
import Firebase
enum RegisterError: Error
{
case IncompleteForm
case NonMatchingForm
case FirebaseError
}
class RealTimeApi
{
private let Reference: DatabaseReference! = Database.database().reference()
private var nilOrNot = [Bool]()
public var errorDescription: String?
func Register(FullName: String?, Username: String?, Email: String?, EmailVerification: String?, Password: String?, PasswordVerification: String? )
{
Auth.auth().createUser(withEmail: Email!, password: Password, completion: er, Error){
}
}
func Login(Username:String, Password: String)
{
}
func CheckLoggedinUser() -> Bool
{
let currentuser = Auth.auth().currentUser
if(currentuser == nil)
{
return false
}else{
return true
}
}
}
Loginview
import UIKit
import Firebase
class LogInV: UIViewController {
#IBOutlet weak var UsernameTxt: UITextField!
#IBOutlet weak var PasswordTxt: UITextField!
#IBOutlet var TextfieldRegistrationCollection: [UITextField]!
#IBOutlet weak var ImageView: UIView!
#IBOutlet weak var RegisterView: UIView!
#IBOutlet weak var RV_VerticalAlignmentConstraint: NSLayoutConstraint!
#IBOutlet weak var RegisterBtnO: UIButton!
var Data = RealTimeApi()
var TextFieldStyle = TextfieldStyling()
override func viewDidLoad() {
super.viewDidLoad()
TextFieldStyle.StylizeTextField(StylizedTextField: UsernameTxt)
TextFieldStyle.StylizeTextField(StylizedTextField: PasswordTxt)
for i in 0...TextfieldRegistrationCollection.count - 1 {
TextFieldStyle.StylizeTextField(StylizedTextField: TextfieldRegistrationCollection[i])
}
TextfieldValidation()
RV_VerticalAlignmentConstraint.constant += view.bounds.height
}
override func viewDidAppear(_ animated: Bool) {
RegisterBtnO.isEnabled = false
}
#IBAction func LoginButtons(_ sender: UIButton) {
switch sender.tag{
case 0:
break
case 1:
Slide()
break
default:
print("button not pressed")
break
}
}
func Slide()
{
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.RV_VerticalAlignmentConstraint.constant -= self.view.bounds.height
self.view.layoutIfNeeded()
}, completion: nil)
}
#IBAction func RegisterBtn(_ sender: UIButton) {
}
//Validate textfields so that user register does not work for empty input
func TextfieldValidation()
{
for i in 0...TextfieldRegistrationCollection.count - 1
{
TextfieldRegistrationCollection[i].addTarget(self, action: #selector(LogInV.textFieldDidChange), for: UIControlEvents.editingChanged)
}
}
//selector function for controlling empty textfield
#objc func textFieldDidChange(){
var NoneIsEmpty = Int()
for i in 0...TextfieldRegistrationCollection.count - 1{
if let text = TextfieldRegistrationCollection[i].text, text.isEmpty == false {
NoneIsEmpty += 1
}
}
if(NoneIsEmpty == TextfieldRegistrationCollection.count)
{
RegisterBtnO.isEnabled = true
}else{
RegisterBtnO.isEnabled = false
}
NoneIsEmpty = 0
}
func showAlert(error: String)
{
let Erroralert = UIAlertController(title: "Error", message: error, preferredStyle: .alert)
Erroralert.addAction(UIAlertAction(title: "Dissmiss",style: .cancel ,handler: {action in
print("tapped actionbutton")
}))
present(Erroralert, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
}
func UploadCustomerList(customer: Customer)
{
}
}
So basically i want to do something like this
Auth.auth().createUser(withEmail: Email!, password: Password, completion: User, Error){
throw Error
}
I have created a helper class with the following method:
func setupAlert(with title: String, with message: String?, viewController: UIViewController) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let cancel = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alert.addAction(cancel)
viewController.present(alert, animated: true, completion: nil)
}
you must pass the viewcontroller in the parameter where you want to display the alert. Then you can also pass the error, which can be found in Auth.auth()

Trying to track down why I am getting back Nil data from one view to another

I have two view controllers both views are holding an integer of data from the textfield and that data I would like to be saved and or held so I can error check against that number on the next view. The issue is my data is always nil. If someone can shed some insight on why this is the case I would greatly appreciate it. Code below.
Thanks to Keshu the answer has been solved. I put the Code below in the else statement and works perfectly.
gameInfo(player: yourdata, numberPicked: yourdata, datePlayed: yourdata, winner: yourdata, turn: yourdata)
First view
class PlayerSelectViewController: UIViewController {
#IBOutlet weak var playerButton: UIButton!
#IBOutlet weak var playersTextBox: UITextField!
var gameInfo:GameInfo?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func cancelBtnClicked(_ sender: Any) {
self.navigationController?.popToRootViewController(animated: true)
}
#IBAction func okBtnClicked(_ sender: Any) {
guard let playerTotal = Int(playersTextBox.text!) else {
makeAlert(title: "Enter a Number", message: "Please enter a number and then press Ok")
return
}
if playerTotal < 2 {
makeAlert(title: "Incorrect Number", message: "Please input more than \(playersTextBox.text!)")
} else if playerTotal > 50 {
makeAlert(title: "Incorrect Number", message: "Please input less than \(playersTextBox.text!)")
} else {
gameInfo?.player = playerTotal
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let serverController = segue.destination as! ServerSelectViewController
serverController.gameInfo = gameInfo
}
func makeAlert (title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: {(action) in
alert.dismiss(animated: true, completion: nil)}))
self.present(alert, animated: true, completion: nil)
}
Second view
#IBOutlet weak var serverTextBox: UITextField!
#IBOutlet weak var serverButton: UIButton!
var gameInfo:GameInfo?
override func viewDidLoad() {
super.viewDidLoad()
print(gameInfo?.player as Any)
}
#IBAction func cancelBtnClicked(_ sender: Any) {
self.navigationController?.popToRootViewController(animated: true)
}
#IBAction func okBtnClicked(_ sender: Any) {
guard let number = Int(serverTextBox.text!) else {
makeAlert(title: "Enter a Number", message: "Please enter a number and then press Ok")
return
}
if number < 1 {
makeAlert(title: "Incorrect Number", message: "Please input more than \(serverTextBox.text!)")
} else if number > 1000 {
makeAlert(title: "Incorrect Number", message: "Please input less than \(serverTextBox.text!)")
} else {
gameInfo?.numberPicked = number
}
}
func makeAlert (title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: {(action) in
alert.dismiss(animated: true, completion: nil)}))
self.present(alert, animated: true, completion: nil)
}
Model
class GameInfo: Codable {
var player: Int
var numberPicked: Int
var datePlayed: Date
var winner: String
var turn: Int
init(player: Int, numberPicked: Int, datePlayed: Date, winner: String, turn: Int) {
self.player = player
self.numberPicked = numberPicked
self.datePlayed = datePlayed
self.winner = winner
self.turn = turn
}
static func loadGameInfo() -> [GameInfo] {
return []
}
static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
static let archiveURL = documentsDirectory.appendingPathComponent("gameInformation").appendingPathExtension("plist")
static func saveToFile(gameInformation: [GameInfo]) {
let propertyListEncoder = PropertyListEncoder()
let gameInfo = try? propertyListEncoder.encode(gameInformation)
try? gameInfo?.write(to: archiveURL, options: .noFileProtection)
}
static func loadFromFile() -> [GameInfo]? {
guard let gameInfo = try? Data(contentsOf: archiveURL) else { return nil }
let propertyListDecoder = PropertyListDecoder()
return try? propertyListDecoder.decode(Array<GameInfo>.self, from: gameInfo)
}
The issue is that you have created a segue from your button to your next view controller. So by the time your okBtnClicked action is called, you are on your next view controller and so the data is nil.
Remove the segue from your button and make it from your ViewController 1 to ViewController2. And then in your okBtnClicked function, performSegue at the end.
#IBAction func okBtnClicked(_ sender: Any) {
guard let number = Int(serverTextBox.text!) else {
makeAlert(title: "Enter a Number", message: "Please enter a number and then press Ok")
return
}
if number < 1 {
makeAlert(title: "Incorrect Number", message: "Please input more than \(serverTextBox.text!)")
} else if number > 1000 {
makeAlert(title: "Incorrect Number", message: "Please input less than \(serverTextBox.text!)")
} else {
gameInfo = GameInfo(player: number, numberPicked: 0, datePlayed: Date(), winner:"", turn: 0)
}
self.performSegue(withIdentifier: "yourSegueIdentifier", sender: self)
}

QUIZ app ... UIAlertController not coming up and not restarting back to initial question

The nextQuestion() and the startOver() function I believe is where it is giving me trouble... when I go through the questions it get to the end and does not pop up the alert on the screen just have to hit the True or false one and then it will startOver() but then it skips the first one and goes directly to the second question
import UIKit
class ViewController: UIViewController {
//Place your instance variables here
let allQuestions = QuestionBank()
var pickedAnswer : Bool = false
var questionNumber : Int = 0
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet var progressBar: UIView!
#IBOutlet weak var progressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let firstQuestion = allQuestions.list[0]
questionLabel.text = firstQuestion.questionText
//when app loads it will display the first question in the array of question bank
}
#IBAction func answerPressed(_ sender: AnyObject) {
if sender.tag == 1 {
pickedAnswer = true
}
else if sender.tag == 2 {
pickedAnswer = false
}
//above - will change the picked answer based on which tag # is pressed
checkAnswer()
questionNumber = questionNumber + 1
nextQuestion()
}
func updateUI() {
}
func nextQuestion() {
if questionNumber <= 12 {
questionLabel.text = allQuestions.list[questionNumber].questionText
//if the current questionNumber is equal to or less than 12 then it will change the display text to the current question
}
else {
let alert = UIAlertController(title: "Congrats!", message: "Do you want to restart?", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Restart", style: .default, handler: { UIAlertAction in
self.startOver()
})
alert.addAction(restartAction)
present(alert, animated: true, completion: nil)
}
}
func checkAnswer() {
let correctAnswer = allQuestions.list[questionNumber].answer
if correctAnswer == pickedAnswer {
print("You got it!")
}
else {
print("Wrong!")
}
}
func startOver() {
questionNumber = 0
nextQuestion()
}
}
its a firmware issues. just updated to the latest IOS 13 update and the simulator ran the app no issues so I believe it is with my firmware being the latest versus Xcode with the previous one. it ran no issues on the simulator. simulator on 13.1.1 and I just uploaded to 13.1.3 that's the only difference. I had looked at the completed app from the course and its all line for line the same except the firmware.app running on Xcode simulator

FBLoginManager undeclared type

I installed FacebookSDK using Cocoapods, according to Terminal, I have installed FacebookSDK 4.8.0 (CoreKit, ShareKit and LoginKit), I imported the .h files in my BH-File.h, and already initialized everything in my AppDelegate.
For some reason, when trying to log in using a custom button, when I initialize FBLoginManager, I get an error Use of undeclared type "FBLoginManager".
this is my code
if (FBSDKAccessToken.currentAccessToken() == nil)
{
let fbLoginManager : FBSDKLoginManager =
fbLoginManager.logInWithReadPermissions(["public_profile", "email"], fromViewController: self, handler: { (loginResult, error) -> Void in
if error == nil {
print (FBSDKAccessToken.currentAccessToken().tokenString)
}
else {
print ("ERROR*****: \(error)")
}
})
}
What fixed to me was adding import FBSDKCoreKit and FBSDKLoginKit to my class, for some reason is not enough adding it in the BH-file.h
Try something like this, I just checked the code and it works (it's not exactly what you're looking for but I'm sure you can modify it as needed)
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
class ProfileViewController: UIViewController,FBSDKLoginButtonDelegate {
// #IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var fbLoginButton: FBSDKLoginButton!
override func viewDidLoad() {
super.viewDidLoad()
self.fbLoginButton.delegate = self
self.fbLoginButton.readPermissions = ["public_profile"]
self.fbLoginButton.publishPermissions = ["publish_actions"]
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "fbProfileChanged:",
name: FBSDKProfileDidChangeNotification,
object: nil)
FBSDKProfile.enableUpdatesOnAccessTokenChange(true)
// If we have a current Facebook access token, force the profile change handler
if ((FBSDKAccessToken.currentAccessToken()) != nil)
{
self.fbProfileChanged(self)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
//facebooks functions
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
if (error != nil)
{
print( "\(error.localizedDescription)" )
}
else if (result.isCancelled)
{
// Logged out?
print( "Login Cancelled")
}
else
{
// Logged in?
print( "Logged in, segue now")
self.performSegueWithIdentifier("showHome", sender: self)
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
}
//see bitfountain
func fbProfileChanged(sender: AnyObject!) {
let fbProfile = FBSDKProfile.currentProfile()
if (fbProfile != nil)
{
// Fetch & format the profile picture
let strProfilePicURL = fbProfile.imagePathForPictureMode(FBSDKProfilePictureMode.Square, size: imageView.frame.size)
let url = NSURL(string: strProfilePicURL, relativeToURL: NSURL(string: "http://graph.facebook.com/"))
let imageData = NSData(contentsOfURL: url!)
let image = UIImage(data: imageData!)
self.nameLabel.text = fbProfile.name
self.imageView.image = image
self.nameLabel.hidden = false
self.imageView.hidden = false
self.nextButton.hidden = false
}
else
{
self.nameLabel.text = ""
self.imageView.image = UIImage(named: "")
self.nameLabel.hidden = true
self.imageView.hidden = true
}
}
#IBAction func nextButtonPressed(sender: UIButton) {
self.performSegueWithIdentifier("showHome", sender: self)
}
}