Getting API data + Optional Problems - ios

I'm struggling to get an optional type to a Label.text. It keeps on giving me "nil" value and won't change the text.
import UIKit
class VerifyPNViewController: UIViewController {
#IBOutlet weak var VerificationMessage: UILabel!
#IBAction func backButton(_ sender: Any) {
self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
func didSuccess(_ response: GetLoginVerificationMessage){
let Main = response.result!
DispatchQueue.main.async {
print(Main)
self.VerificationMessage?.text = Main
print(self.VerificationMessage?.text)
}
}
}
and I will get a
234443 << which is a response.result!
nil << print(self.VerificationMessage?.text)
I have no idea why this value won't go into the "self. Verification?.text" Does anyone have ideas?
Thank you.

Related

Swift - Accessing implicitly unwrapped variable gives a nil error

I'm following a tutorial on CoreData and I've been following it exactly, yet when they run the app, everything works and saves correctly, yet I get a nil error. The tutorial is a few years old, so I'm not sure if something has been udpated in the way CoreData works. It's an app to save goals.
Here's the first view controller where you enter the text of the goal and if it is short or long term:
import UIKit
class CreateGoalViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var goalTextView: UITextView!
#IBOutlet weak var shortTermButton: UIButton!
#IBOutlet weak var longTermButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
var userGoalType: GoalType = .shortTerm
override func viewDidLoad() {
super.viewDidLoad()
nextButton.bindToKeyboard()
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
goalTextView.delegate = self
}
#IBAction func nextButtonPressed(_ sender: Any) {
if goalTextView.text != "" && goalTextView.text != "What is your goal?" {
guard let finishVC = storyboard?.instantiateViewController(withIdentifier: "FinishVC") as? FinishGoalViewController else {return}
finishVC.initData(description: goalTextView.text!, type: userGoalType)
print("\(finishVC.goalType.rawValue) after next button pressed")
performSegue(withIdentifier: "goToFinish", sender: self)
}
}
#IBAction func longTermButtonPressed(_ sender: Any) {
userGoalType = .longTerm
longTermButton.setSelectedColor()
shortTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func shortTermButtonPressed(_ sender: Any) {
userGoalType = .shortTerm
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func textViewDidBeginEditing(_ textView: UITextView) {
goalTextView.text = ""
goalTextView.textColor = UIColor(ciColor: .black)
}
}
And here's the following view controller where you set the number of times you want to do that goal where the CoreData functions are:
import UIKit
import CoreData
class FinishGoalViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var createButton: UIButton!
#IBOutlet weak var pointsTextField: UITextField!
var goalDescription: String!
var goalType: GoalType!
func initData(description: String, type: GoalType) {
self.goalDescription = description
self.goalType = type
}
override func viewDidLoad() {
super.viewDidLoad()
createButton.bindToKeyboard()
pointsTextField.delegate = self
}
#IBAction func createGoalPressed(_ sender: Any) {
if pointsTextField.text != ""{
self.save { finished in
if finished {
dismiss(animated: true)
}
}
}
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func save(completion: (_ finished: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else {return}
let goal = Goal(context: managedContext)
goal.goalDescription = goalDescription
goal.goalType = goalType.rawValue
goal.goalCompletionValue = Int32(pointsTextField.text!)!
goal.goalProgress = Int32(0)
do{
try managedContext.save()
print("successfully saved data")
completion(true)
}catch{
debugPrint("Could not save: \(error.localizedDescription)")
completion(false)
}
}
}
I'm getting a nil error in the save function with the goalType.rawValue turning up nil. The goal type is set up in an enum file:
import Foundation
enum GoalType: String {
case longTerm = "Long Term"
case shortTerm = "Short Term"
}
I'm not sure why there's an error. Because in the CreateGoalViewController, I print the goalType.rawValue from the following view controller and it comes up with the correct string, either short or long-term. But when FinishGoalViewController loads, it is all of a sudden nil.
You are initiating and configuring your FinishGoalViewController in nextButtonPressed but you never use it. performSegue(withIdentifier: "goToFinish", sender: self) will create and push a new instance of FinishGoalViewController.
The most simple aproach would be to push your allready configured controller from your curent Controller. Remove performSegue(... and use.
self.navigationController?.pushViewController(finishVC, animated: true)
If you still want to use the segue, remove everything from the nextButtonPressed function, leaving just the performSegue(... line. After that add this function to your CreateGoalViewController controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToFinish" {
if let finishVC = segue.destination as? FinishGoalViewController {
// configure finshVC here
}
}
}

How to receive tag pressed event using tagListView?

I'm using cocoa pod of TagListView https://github.com/ElaWorkshop/TagListView. And I need to catch the tagPressed event. I know that I should implement the delegate TagListViewDelegate, but I don't know where and how. Sorry for bad eng.
Here's my code
import UIKit
import TagListView
class MoreInfoViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var tagListView: TagListView!
#IBOutlet weak var tagNameTextField: UITextField!
#IBAction func addTagButtonPressed(_ sender: UIButton) {
guard tagNameTextField.text != "" else { return }
let tagName = tagNameTextField.text!
tagNameTextField.text = ""
tagListView.addTag(tagName)
}
}
Quoting directly from the documentation,
You can implement TagListViewDelegate to receive tag pressed event:
// ...
{
// ...
tagListView.delegate = self
// ...
}
func tagPressed(title: String, tagView: TagView, sender: TagListView) {
print("Tag pressed: \(title), \(sender)")
}
Simply define your delegate and place the function within the controller and touch events should be called to this function.

Add elements to search history?

I have a model - Movies.
and two controllers - first for search movie by title, second - for display result with poster, title and year.
Now i need to create some history search on my third controller
(searchHistoryController - TableView) where displayed all movies, and when i tapped on cell with movie's title show movie info.
How I can build it?
I tried create array in my model. And write resutl in it, but each time when i use search it rewrite array, not add new element.
Maybe use realm
Need some help:)
Movie.swift
import Foundation
import UIKit
import Alamofire
import AlamofireImage
protocol MovieDelegate {
func updateMovieInfo()
}
class Movie {
private let omdbUrl = "http://www.omdbapi.com/?"
var title: String?
var filmYear: String?
var poster: String?
var delegete: MovieDelegate!
var historyMovie = [Movie]()
func getMovieInfo(title: String, completion: #escaping ()->()){
let params = ["t": title]
Alamofire.request(omdbUrl, method: .get, parameters: params).validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON { (response) in
switch response.result {
case .success(let JSON):
let response = JSON as! NSDictionary
let status = response["Response"] as! String
if status == "True" {
self.title = (response["Title"] as! String)
self.filmYear = (response["Year"] as! String)
self.poster = (response["Year"] as! String)
// self.delegete.updateMovieInfo()
completion()
} else {
self.title = (response["Error"] as! String)
completion()
}
case .failure(let error):
print (error)
}
}
}
}
SearchVC
import UIKit
class SearchViewController: UIViewController {
var movie = Movie()
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var searchTextField: UITextField!
#IBOutlet weak var searchButton: UIButton!
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
self.performSegue(withIdentifier: "movieInfo", sender: self)
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
}
}
DetailVC
class DetailInfoViewController: UIViewController, MovieDelegate {
#IBAction func showHistory(_ sender: UIButton) {
performSegue(withIdentifier: "showHistory", sender: self)
}
#IBOutlet weak var posterImageView: UIImageView!
#IBOutlet weak var filmNameLabel: UILabel!
#IBOutlet weak var filmYearLabel: UILabel!
var movie = Movie()
var movieTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
self.movie.getMovieInfo(title: movieTitle ) {
self.updateMovieInfo()
}
self.movie.delegete = self
}
func updateMovieInfo() {
getPoster(link: movie.poster)
filmNameLabel.text = movie.title
filmYearLabel.text = movie.filmYear
}
func getPoster(link: String?) {
if link != nil {
guard let url = URL(string: link!) else { return }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
self.posterImageView.image = UIImage(data: data)
}
}
} } else {
self.posterImageView.image = #imageLiteral(resourceName: "Image")
}
}
}
First of all, movieHistory should not be part of your Movie class, but part of your SearchViewController class.
Second of all, unless you want to persist your data, you don't need Realm for this.
Just save the movies in SearchViewController into an array once the search button has been tapped and send it to your other view controller in the segue. Like so
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
movieHistory.append(movie)
self.performSegue(withIdentifier: "movieInfo", sender: movieHistory)
})
}
}
Also, modify prepare(for segue:...) like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
secondVC.movieHistory = movieHistory
}
In detailVC override prepare(for segue:...) as well and send movieHistory to searchHistoryController the same way it is done in the previous VC.

New to testing with swift (quick/nimble), trying to get past oauth screen

I'm attempting to write a quick/nimble test that will just confirm a few things on the screen after my opening screen. The opening screen is simply a button which leads to oauth2 sign in to validate users. I'm just wondering how it is I can spoof a sign in so that I can get to the next screen. I know this is probably not a lot of information but I'm really new to the testing aspect of swift as a whole but I know it's very important to have unit tests.
Here is code that might help:
import UIKit
class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate {
#IBOutlet weak var signInButton: GIDSignInButton!
#IBOutlet weak var errorLabel: UILabel!
var googleViewController: GoogleViewController!
var permittedList:[String] = ["me#mysite.com"]
let env = NSProcessInfo.processInfo().environment
override func viewDidLoad() {
self.view.backgroundColor = UIColor(rgb: 0x6a737b)
super.viewDidLoad()
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().clientID = env["CLIENT_ID"] as? String
GIDSignIn.sharedInstance().signInSilently()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "googledisplay" {
googleViewController = segue.destinationViewController as! GoogleViewController
}
}
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
if let err = error {
println(error)
}
else {
if !contains(permittedList, GIDSignIn.sharedInstance().currentUser.profile.email) {
GIDSignIn.sharedInstance().signOut()
GIDSignIn.sharedInstance().disconnect()
self.errorLabel.text = "You must sign in with a permitted email address"
}
else{
self.errorLabel.text = ""
performSegueWithIdentifier("googledisplay", sender: self)
}
}
}
}
test:
import Foundation
import Quick
import Nimble
import PasswordRecovery
class PasswordViewControllerSpec: QuickSpec {
var viewController = PasswordViewController()
override func spec() {
let _ = PasswordViewController().view
viewController.setNewEmail("s#s.com")
viewController.setNewLRA("unknown")
viewController.reset_code(t: 1447267160)
expect(self.viewController.getLRA()).to(equal("LRA"))
}
}
PasswordViewController is the name of the view which is loaded after the original screen. Thank you for any help, if there is anything more needed please tell me!

Swift Barcode Scanning

I am attempting add barcode scanning to my app using RSBarcodes. I'm having two issues: Not being able to update a label that displays the barcode scanned and the delegate to send the barcode to my calling view controller not working. Below is my code for the view controller that handles the scanning:
import UIKit
import AVFoundation
import RSBarcodes
class ScanViewController: RSCodeReaderViewController {
#IBOutlet weak var label1Label: UILabel!
#IBOutlet weak var label2Label: UILabel!
#IBOutlet weak var scanLabel: UIButton!
var delegate: barcodesScannedDelegate?
var codes:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
var code=""
// Do any additional setup after loading the view.
focusMarkLayer.strokeColor = UIColor.redColor().CGColor
cornersLayer.strokeColor = UIColor.yellowColor().CGColor
tapHandler = { point in
//println(point)
}
barcodesHandler = { barcodes in
for barcode in barcodes {
if !contains(self.codes, barcode.stringValue) {
self.codes.append(barcode.stringValue)
code = barcode.stringValue
}
}
println(code)
self.label1Label.text = code
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func finishedPressed(sender: UIButton) {
delegate?.barcodesScanned(self.codes)
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func cancelPressed(sender: UIButton) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Just to ensure I did the delegate correctly, here is my code in Protocol.swift:
protocol selectCarrierDelegate {
func selectCarrier(carrierID: String,carrier: String)
}
protocol barcodesScannedDelegate {
func barcodesScanned(barcodes: [String])
}
And the relevant code in the controller that should receive the barcode:
class InBoundViewController: UIViewController,selectCarrierDelegate,UIAlertViewDelegate,UITableViewDelegate,UITableViewDataSource,barcodesScannedDelegate {
func barcodesScanned(barcodes: [String]) {
println("codes=\(barcodes)")
}
Anyone have any ideas why the label won't change and the delegate isn't working?
You need to update all UI in the main thread.
Try this:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.label1Label.text = code
})
Swift 4:
DispatchQueue.main.async {
self.label1Label.text = code
}

Resources