Editing a URL programatically in Swift xCode - ios

I am trying to change my UIWebView Url depending on the variables I get from my Parse.com database.
First, I split the three worded String into three parts then I place the parts into the url.
However, I am getting an error! It is very strange:
Here is the code incase you are unable to see it:
import UIKit
import Parse
import ParseUI
class myBookingsItemTableViewController: UITableViewController {
var object: PFObject!
#IBOutlet weak var typeOfBookingLabel: UILabel!
#IBOutlet weak var typeOfBookingQRCode: UIWebView!
var ticketId = String()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (self.object != nil) {
self.typeOfBookingLabel?.text = self.object["booking"] as? String
var ticketID = self.object["ticketId"] as? String
self.ticketId = ticketID!
var ticketIdArr = split(ticketId) {$0 == " "}
var first: String = ticketIdArr[0]
var second: String? = ticketIdArr.count > 1 ? ticketIdArr[1] : nil
var third: String? = ticketIdArr.count > 2 ? ticketIdArr[2] : nil
let url = NSURL (string: "http://chart.apis.google.com/chart?chl=\(first)+\(second)+\(third)&chs=200x200&cht=qr&chld=H%7C0")
let requestObj = NSURLRequest(URL: url!)
typeOfBookingQRCode.loadRequest(requestObj)
} else {
self.object = PFObject(className: "Bookings")
}
}
}

You have to make sure that your first, second and third do not contain any whitespaces - otherwise you will not be able to create a URL from it - it will return nil and your unwrapping fails.
You can do that using
first = first.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
Additionally I would recommend against using nil in your situation but rather either exclude the second and third argument if they would be nil or replace the nil with an actual value - that way you can change their type to String and do not have to worry about the optionals any more.
The following code snippet escapes all three values and always generates an URL:
func urlEncode(par:String!) -> String! {
return par.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
}
var first: String = urlEncode(ticketIdArr[0])
var second: String = ticketIdArr.count > 1 ? urlEncode(ticketIdArr[1]) : "nil"
var third: String = ticketIdArr.count > 2 ? urlEncode(ticketIdArr[2]) : "nil"

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.

How do I find the Max from a group of user inputed Ints using Xcode 11 and swift

Here's what I have so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var game1: UITextField!
#IBOutlet weak var game2: UITextField!
#IBOutlet weak var game3: UITextField!
#IBOutlet weak var series: UILabel!
#IBOutlet weak var average: UILabel!
#IBOutlet weak var high: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func calculate(_ sender: Any) {
let game1Results = Int(game1.text!)
let game2Results = Int(game2.text!)
let game3Results = Int(game3.text!)
let gameResultsArray = [game1Results, game2Results, game3Results]
high.text = "\(gameResultsArray.max())"
}
}
I've been trying to use the .max function but I'm getting errors stating I need to refer to conform to "Comparable". I'm pretty new to swift, any help will be much appreciated.
It happens because you try to compare values of optional Int (Int?)
First of all, you should know that not each String can be converted to an Int
E.g. '42' will be converted correctly, but 'ie42zx' can't be converted to and Int - that's why Int(String) function returns optional Int (Int?)
'Optional' property says like 'i can have a value, but I can also be nil'. That's why you should unwrap optionals in your code
First, I suggest to avoid force unwraping here.
let game1Results = Int(game1.text!)
It can be done like that:
guard
let text1 = game1.text,
let text2 = game2.text,
let text3 = game3.text
else { return }
You should do that because not every text field contains text, so textField.text property returns an optional String?
Then you can convert your String results to an Int and unwrap these results before compare:
guard
let game1Results = Int(text1),
let game2Results = Int(text2),
let game3Results = Int(text3)
else { return }
I would suggest to read more about optionals in swift and unwrapping (see Optionals paragraph)
I believe your issue is that the Int constructor with a string returns an optional integer value. You're trying to take the max of a bunch of optionals, so it cant figure it out.
You can use the compactMap function on your array before calling max() to filter out the nil values.
gameResultsArray.compactMap{ $0 }.max()

How to get all dictionary keys ('LazyMapCollection<..>' error)

I have a problem with getting all dictionary keys.
When I call .keys, I get error as follows.
And I don't know how to solve it.
Thanks.
error:
Value of optional type 'LazyMapCollection<Dictionary<String, ChatroomMember>, String>?' not unwrapped; did you mean to use '!' or '?'?
class Chatroom:Model {
var id:String = ""
var members:[String:ChatroomMember] = [String:ChatroomMember]()
}
class ChatroomMember:Model {
var id:String = ""
}
class ViewController: UIViewController {
weak var chatroom:Chatroom?
override func viewDidLoad() {
var memberUsers:[String] = [String](chatroom?.members.keys) // get Error
}
}
Simply unwrap your chatroom:
var memberUsers:[String] = [String](chatroom!.members.keys)
Or (if chatroom has a chance to remain uninitialized) use optional if let unwrap:
var memberUsers:[String]
if let chatroom = chatroom {
memberUsers = Array(chatroom.members.keys)
} else {
memberUsers = []
}
Or if your entire app depends on presence of chatroom, guard it:
guard let chatroom = self.chatroom else {
// chatroom is not here!
return
}
var memberUsers = Array(chatroom.members.keys)
P.S. You can rely on Swift type inference and use Array(..) instead of [String](..) to cast lazy keys collection to normal array.

When trying to convert a UITextField text to Int Xcode 7 gives me an error

I'm new to programming and after doing some tutorials online I decided to make my own app with Xcode 7 to be able to run my app on my iPhone. But when I try to get input data from a text field and put it into a var it gives me an error. I have tried to use the following:
var initialChips = 0
var initialBlind = 0
#IBOutlet weak var txtBlind: UITextField!
#IBOutlet weak var txtChips: UITextField!
#IBOutlet weak var doneBtn: UIBarButtonItem!
#IBAction func doneBtn(sender: AnyObject) {
let initialChips:Int? = Int(txtChips.text)
let initialBlind:Int? = Int(txtBlind.text)
}
Xcode will ask to put a "!" after ".text" but after doing that it gives me a warning: "Initialization of immutable value 'initialChips' (or 'initialBlind' was never used: consider replacing with assignment to '_'or removing it".
What can I do to set a value to my var?
The thing is that the let initialChips:Int? = Int(txtChips.text) generates a new variable called initialChips (different from the variable from the class). The difference between let and var is that let is inmutable and var is not. You should do:
initialChips = Int(txtChips.text)
To assign the integer from the UITextField to your class variable rather than declaring a new inmutable variable (with the same name) and do nothing with it.
Of course, the same happens with initialBlind
EDIT:
Try the following:
#IBAction func doneBtn(sender: AnyObject) {
if let initialChips = Int(txtChips.text!) {
self.initialChips = initialChips
}else {
self.initialChips = 0
}
if let initialBind = Int(txtBind.text!) {
self.initialBind = initialBind
}else {
self.initialBind = 0
}
}
You should add the ! after the closing parenthesis, not after .text, like so:
let initialChips:Int? = Int(txtChips.text)!

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