I have buttons that when pressed, will call/message a number from an array. i.e. button1 will call the number at index 0 of the array, button2 at index 1, etc.. For some reason whenever the number from the array contains a format other than xxx-xxx-xxx it crashes (i.e. (xxx) xxx-xxx). And yet, the log gives me the following error even though the array isn't nil:
Anyone know why this is happening?
Here is the code for everything:
import UIKit
import AddressBook
var contactInfo: [String] = []
[...]
override func viewDidLoad() {
super.viewDidLoad()
//this is the function that grabs the array from an app group
setUpCallMessageButtons()
[...]
callButton1.addTarget(self, action: "call:", forControlEvents: UIControlEvents.TouchUpInside)
}
func call(sender:UIButton!)
{
if (sender == callButton1) {
println("\(contactInfo)")
var url:NSURL? = NSURL(string: "tel:\(contactInfo[0])")
self.extensionContext?.openURL(url!, completionHandler:{(success: Bool) -> Void in
})
}
}
func setUpCallMessageButtons(){
let appGroupID = "**redacted**"
let defaults = NSUserDefaults(suiteName: appGroupID)
contactInfo = (defaults!.objectForKey("contactInfo") as! [String])
println("\(contactInfo)")
//This is gives the log down below. As you can see, none are nil.
}
Buttons 1,2 and 5 work while 3 and 4 always crash.
My guess is that if the phone number isn't formatted correctly, the call to convert it to an NSURL is failing and returning nil.
You probably need to wrap your call to openURL in an optional binding ("if let") block:
var url:NSURL? = NSURL(string: "tel:\(contactInfo[0])")
if let url = url
{
self.extensionContext?.openURL(url!,
completionHandler:
{
(success: Bool) -> Void in
}
}
else
{
println("Phone number \(contactInfo[0]) is not in a valid format")
}
You might want to strip away parenthesis from your phone number before trying to create your URL. A simple way would be to use the NSString method stringByReplacingOccurrencesOfString:withString:.
Here's a little storyboard - which shows you where the nil is coming from
Unexpectedly found nil means there is a variable which is expected to be non-nil but at run time was nil
This is the line of code that is causing the issue
self.extensionContext?.openURL(url!, completionHandler:{(success: Bool)
It expects url to be non-nil (i.e. the !) but it is definitely nil (see image)
If this data comes from the user or from the internet, you might want a method to strip away all non-numeric characters. Something like this (from a working playground I just banged out) :
import UIKit
func digitsOnly(#fromString: String) -> String
{
var workString = NSMutableString(string: fromString)
let digitsSet = NSCharacterSet.decimalDigitCharacterSet()
var index: Int
for index = count(fromString)-1; index>=0; index--
{
if !digitsSet.characterIsMember(workString.characterAtIndex(index))
{
workString.deleteCharactersInRange(NSRange(location:index, length:1))
}
}
return workString as String
}
let testString = "(555) 111-2222"
let result = digitsOnly(fromString:testString)
println("digitsOnly(\"\(testString)\") = \"\(result)\" ")
This displays:
digitsOnly("(555) 111-2222") = "5551112222"
Edit:
Or alternately a more Swift-like version of the same function:
func digitsOnly(#fromString: String) -> String
{
var result = String()
let digitsSet = NSCharacterSet.decimalDigitCharacterSet()
for char in fromString
{
if digitsSet.characterIsMember(char as unichar)
result += char
}
}
EDIT #2:
You can increase the set of characters that is left in place by changing the character set you use. Replace the line
let digitsSet = NSCharacterSet.decimalDigitCharacterSet()
With
let digitsSet = NSCharacterSet(charactersInString: "0123456789+-")
To preserve "+" signs and dashes. (Edit the string to include the characters you need.)
Related
I know there are answers available regarding this exception but nothing is working for me.
My code was working fine where I have to convert the text of a textfield into Int, but now I noticed that when I enter more than 10 characters it crashes.
I have set character limit of those text fields to 11, but it is crashing on the last number with the error: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Here is the code
#IBAction func Qty_EndEdit(_ sender: Any) {
print("For Total Cost")
if QtyText.text != ""{
if UnitPriceText.text != "" {
TotalCostText.text = String((QtyText.text?.numberValue)! * (UnitPriceText.text?.numberValue)!)
}
}
}
#IBAction func UnitPrice_EndEdit(_ sender: Any) {
print("For Total Cost")
if QtyText.text != ""{
if UnitPriceText.text != "" {
TotalCostText.text = String((QtyText.text?.numberValue)! * (UnitPriceText.text?.numberValue)!)
// CRASHING ON THE ABOVE LINE
}
}
}
extension String {
var numberValue:Int? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter.number(from: self) as! Int
}
}
Your crash is related to the fact that Int multiplication cannot handle such numbers, use Double.
Both of your actions do the same thing, try to place code that will be reused inside a func.
You also need to look at optionals and how to use them. Here's an example.
func updateTotalCost() {
if let quantity = Double(QtyText.text ?? ""),
let unitPrice = Double(UnitPriceText.text ?? "") {
TotalCostText.text = String(quantity * unitPrice)
} else {
// ???
TotalCostText.text = ""
}
}
#IBAction func Qty_EndEdit(_ sender: Any) {
updateTotalCost()
}
#IBAction func UnitPrice_EndEdit(_ sender: Any) {
updateTotalCost()
}
I'll dissect some of it:
QtyText.text ?? "" -> If QtyText.text is nil, replace it with an empty string, otherwise use the value in QtyText.text
Double(QtyText.text ?? "") -> Build an Double from aString, which is guaranteed non-nil (since we forced it to be an emptyString`, even in the case the QtyText.text was nil)
This Double() constructor may, itself, return nil if the string passed to the constructor does not evaluate to a valid Double (for example, the String "abcd" cannot evaluate to an Double). Therefore, we wrap this in an if let statement to protect ourselves against the Double() constructor failing to build an Double from our String:
if let quantity = Int(QtyText.text ?? "")
This is telling the compiler:
Only evaluate this as true if what I'm placing in the quantity variable is not nil.
Finally, we chain two if statements with a comma:
if let quantity = Int(QtyText.text ?? ""),
let unitPrice = Int(UnitPriceText.text ?? "") {
Inside this part of the if statement, we'll be guaranteed that quantity and unitPrice both are valid Doubles , and that we can perform the calculation.
I’m not sure you’ll be please with the result of String(myDoubleNumber) but then you should look into number formatting to get what you want.
I want to have a random emoji inside a Label and every tableViewCell should show a different, random picked emoji...
I tried following function, but sadly it's giving me following error: Value of type '[String]' has no member 'shuffled'
let emojis: [String] = {
let url = Bundle.main.url(forResource: "emojis", withExtension: "txt")!
let list = try! String(contentsOf: url).map { String($0) }
return list.shuffled()
}()
po String(UnicodeScalar(Array(0x1F300...0x1F3F0).randomElement()!)!)
As Carpsen says, the shuffled() function is only in Swift ≤ 4.2. In earlier versions of the language you have to write your own.
Here is an example implementation. I called my scrambled(), so it will work in any version of Swift including 4.2:
extension Array {
func scrambled () -> [Element] {
var source = self
var dest = Array<Element>()
for _ in 1...self.count {
let index = Int(arc4random_uniform(UInt32(source.count)))
dest.append(source.remove(at: index))
}
return dest
}
}
You could scramble the entire set of emojis, but it seems to me you'd be better off creating an array of random emoji when you first populate the data source of your table view. To do that you could use a function randomEmoji():
func randomEmoji() -> String {
let range = 0x1F300...0x1F3F0
let index = Int(arc4random_uniform(UInt32(range.count)))
let ord = range.lowerBound + index
guard let scalar = UnicodeScalar(ord) else { return "❓" }
return String(scalar)
}
So you'd create a stuct to hold all the data for a table view cell, and then populate each struct with a call to randomEmoji().
Note that the randomEmoji() function only chooses emoji in the unicode range from 0x1F300 to 0x1F3F0. Also, the approach of using randomEmoji() may cause duplicate emoji.
You can easily use this extension for emoji string :)
extension NSObject {
public var emojiString: String {
let pointer = Unmanaged.passUnretained(self).toOpaque()
// You can adjust your range
//let range = 0x1F600...0x1F64F
let range = 0x1F300...0x1F3F0
let index = (pointer.hashValue % range.count)
let ord = range.lowerBound + index
guard let scalar = UnicodeScalar(ord) else { return "❓" }
return String(scalar)
}
}
Just call cell.textLabel?.text = cell.emojiString and you're good to go.
Unless you are using Swift 4.2, the shuffled()is not available and the error message is spot on.
You could create your own shuffled() function.
It has been a long time since I have coded in iOS and I am upgrading the old app to swift 3. I am really struggling with one issue of using optional variables.
I have a textfield which is optional. I want it unwrapped into a non-optional Int so that I can use it in the other functions for calculation.
#IBOutlet weak var txtRuns: UITextField!
func sendScore()
{
let runs = txtRuns.text!
let overs = txtOvers.text!
let balls = txtBalls.text!
let wkts = txtWkts.text!
let target = txtTarget.text!
let totalOvers = txtTotalOvers.text!
let strData = "S|R\(runs)" + getOptionalScoreData(
runs: Int(runs),
wkts: Int(wkts),
overs: Int(overs),
balls: Int(balls),
target: Int(target),
totalOvers: Int(totalOvers)
)
}
func getOptionalScoreData(runs: Int, wkts: Int, overs: Int, balls: Int, target: Int, totalOvers: Int) -> String
{
if ( runs == 0 ) {
return getCurrentRunRate(runs: runs)
}
return "";
}
As you can see, I have so many functions to call and I want this textfield to turn into non-optional INT.
Now I have tried several options that I read over here but the error messages only change. The problem didn't solve.
Current ERROR is
The value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
Please help.
Thanks
.................
Please note that I don't think nested check is a nice idea here because all these variables are independent of each other. One can be nil but other can be still passed and used.
If I nest checks like this, it means that no other value will be passed if runs are nil.
if let runs = txtRuns.text, let runsInInt = Int(runs) {
if let overs = txtOvers.text, let oversInInt = Int(overs) {
if let wkts = txtWkts.text, let wktsInInt = Int(wkts) {
strData = "S|R\(runs)\(overs)\(wkts)" + getOptionalScoreData( runs: runsInInt, overs: oversInInt, wkts: wktsInInt)
}
If you are frequently getting value from UITextField as Int, you can add an extension as follows:
extension UITextField {
var intValue: Int {
get {
if let text = self.text {
return Int(text) ?? 0
}
return 0
}
set {
self.text = String(newValue)
}
}
}
You can add the above as private extension in your viewcontroller too. Now you can rewrite your code as:
func sendScore() {
let strData = "S|R\(txtRuns.intValue)\(overs.intValue)\(wkts.intValue)" + getOptionalScoreData(
runs: txtRuns.intValue,
wkts: wkts.intValue,
overs: overs.intValue,
balls: balls.intValue,
target: target.intValue,
totalOvers: totalOvers.intValue)
)
}
Int(runs) call constructor of Int following:
public init?(_ text: String, radix: Int = default)
Because String to Int might failed due to the String might not a valid integer.
How would you deal with it?
You can reference Sallu's comment.
user ! to guarantee the String in UITextField is absolute a valid integer, or app crash.
runs: Int(runs)!
user ?? to give a default value if the String in UITextField is not a valid integer.
runs: Int(runs) ?? 0
In the case the default value is 0
First, I initialize the variables to hold the stock data
var applePrice: String?
var googlePrice: String?
var twitterPrice: String?
var teslaPrice: String?
var samsungPrice: String?
var stockPrices = [String]()
I fetch current stock prices from YQL, and put those values into an array
func stockFetcher() {
Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let json = JSON(responseData.result.value!)
if let applePrice = json["query"]["results"]["quote"][0]["Ask"].string {
print(applePrice)
self.applePrice = applePrice
self.tableView.reloadData()
}
if let googlePrice = json["query"]["results"]["quote"][1]["Ask"].string {
print(googlePrice)
self.googlePrice = googlePrice
self.tableView.reloadData()
}
if let twitterPrice = json["query"]["results"]["quote"][2]["Ask"].string {
print(twitterPrice)
self.twitterPrice = twitterPrice
self.tableView.reloadData()
}
if let teslaPrice = json["query"]["results"]["quote"][3]["Ask"].string {
print(teslaPrice)
self.teslaPrice = teslaPrice
self.tableView.reloadData()
}
if let samsungPrice = json["query"]["results"]["quote"][4]["Ask"].string {
print(samsungPrice)
self.samsungPrice = samsungPrice
self.tableView.reloadData()
}
let stockPrices = ["\(self.applePrice)", "\(self.googlePrice)", "\(self.twitterPrice)", "\(self.teslaPrice)", "\(self.samsungPrice)"]
self.stockPrices = stockPrices
print(json)
}
}
}
in cellForRowAt indexPath function I print to the label
if self.stockPrices.count > indexPath.row + 1 {
cell.detailTextLabel?.text = "Current Stock Price: \(self.stockPrices[indexPath.row])" ?? "Fetching stock prices..."
} else {
cell.detailTextLabel?.text = "No data found"
}
I'm running into the issue of printing Current Stock Price: Optional("stock price"), with the word optional. I gather that this is because I'm giving it an array of optional values, but I sort of have to since I actually don't know if there will be data coming from YQL, one of the 5 stocks might be nil while the others have data. From reading other similar questions I can see that the solution would be to unwrap the value with !, but I'm not so sure how to implement that solution when it's an array with data that might be nil, and not just an Int or something.
How can I safely unwrap here and get rid of the word Optional?
First off:
Any time you repeat the same block of code multiple times and only increase a value from 0 to some max, it is a code smell. You should think about a different way to handle it.
You should use an array to do this processing.
How about a set of enums for indexes:
enum companyIndexes: Int {
case apple
case google
case twitter
case tesla
//etc...
}
Now you can run through your array with a loop and install your values more cleanly:
var stockPrices = [String?]()
Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let json = JSON(responseData.result.value!)
let pricesArray = json["query"]["results"]["quote"]
for aPriceEntry in pricesArray {
let priceString = aPriceEntry["ask"].string
stockPrices.append(priceString)
}
}
}
And to fetch a price from the array:
let applePrice = stockPrices[companyIndexes.apple.rawValue]
That will result in an optional.
You could use the nil coalescing operator (??) to replace a nil value with a string like "No price available.":
let applePrice = stockPrices[companyIndexes.apple.rawValue] ?? "No price available"
or as shown in the other answer:
if let applePrice = stockPrices[companyIndexes.apple.rawValue] {
//we got a valid price
} else
//We don't have a price for that entry
}
I'm writing this outside of Xcode (so there might be typos), but this kind of logic should work.
if self.stockPrices.count > indexPath.row + 1 {
var txt = "Fetching stock prices..."
if let price = self.stockPrices[indexPath.row] {
txt = price
}
cell.detailTextLabel?.text = txt
} else {
cell.detailTextLabel?.text = "No data found"
}
For safe unwrap use that code:
if let currentStockPrice = self.stockPrices[indexPath.row]
{
// currentStockPrice available there
}
// currentStockPrice unavailable
If you need to unwrap multiple variables in one if after another it may lead to unreadable code. In such case use this pattern
guard let currentStockPrice = self.stockPrices[indexPath.row]
else
{
// currentStockPrice is unavailable there
// must escape via return, continue, break etc.
}
// currentStockPrice is available
I have a weird issue trying to validate user input. I'm using a wrapper for a form framework and I want it to be able to validate user input.
The trouble is, when I call the closure with the userValue argument, it ends up being nil and all checks return false:
class FormRowWrap {
var tag: String
var row: AnyObject
var verification: (value: Any?) -> Bool
init(tag: String, row:AnyObject, verification:(Any?) -> Bool) {
self.tag = tag
self.row = row
self.verification = verification
}
}
class InsertViewController: FormViewController {
let rows = [
{
let tag = "Fuel Name"
let row = FormRowWrap(tag: tag,
row:TextRow(tag) {
$0.title = tag
// $0.value = last known user default
},
verification:({(value: Any?) -> Bool in
if let thing = value as? String {
//^----- the value in a **breakpoint here is nil**
//
if !thing.isEmpty {
return true
}
}
return false
}))
return row
}() as FormRowWrap,
{
let tag = "Price"
let row = FormRowWrap(tag: tag,
...
func formValuesAreValid(values: [String: Any?]) -> Bool {
var result = false
for rowWrap in self.rows {
let userValue = values[rowWrap.tag]
print("userValue \(userValue) forTag: \(values[rowWrap.tag])")
// ^---- this prints userValue **Optional(Optional("Ghh")) forTag: Optional(Optional("Ghh"))**
let entryIsValid = rowWrap.verification(value: userValue)
if (!entryIsValid) {
result = false
return result
}
}
result = true
return result
}
If I run rowWrap.verification(value:"test") it returns true, so I think it's an issue about properly unwrapping values.
Your function needs an Optional but userValue is an Optional inside another Optional:
Optional(Optional("Ghh"))
So when you force unwrap it with
let entryIsValid = rowWrap.verification(value: userValue!)
what happens actually is that you unwrap the first layer and get back an Optional:
Optional("Ghh")
which is what your function signature
(value: Any?) -> Bool
needs.
About why it's wrapped twice:
with
formValuesAreValid(values: [String: Any?])
the values in the values dictionary are Optionals, and then when you access the dictionary:
let userValue = values[rowWrap.tag]
you get yet another Optional - because accessing a dictionary always returns an Optional, so in this case values[rowWrap.tag] returns an "Optional Optional".
Then somewhere else you unwrap once thinking you'll get the value if let thing = value as? String but you get the inner Optional instead and your next check fails.
Found a way to make it work by force unwrapping the "userValue":
let entryIsValid = rowWrap.verification(value: userValue!)
I still don't understand why this works and why it doesn't work with the argument as wrapped optional.