I program an "encryption app" that encrypts texts in a simple way. This is the code:
let smallLetters: String = "abcdefghijklmnopqrstuvwxyz"
let bigLetters: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let input: String = encryptTextField.text!
encryptTextField.text! = ""
var value: String = ""
for c in input {
let otherChars: String = " !§$%&/()=?´`#+'*-_.:,;<>^°#€{}≠¿|][¢¶“¡≤≈ç√∫~µ#€öäüÜÖÄ"
for i in otherChars {
if c == i {
value += String(i)
print("i")
}
}
var dCount: Int = 0
var eCount: Int = 0
var dFound: Bool = false
var eFound: Bool = false
for d in smallLetters {
if !dFound {
if c == d {
dFound.toggle()
let y: Int = smallCount - dCount
var z: Int = 1
for i in smallLetters {
if y == z {
print("[\(c) -> \(i)]")
value += String(i)
}
z += 1
}
} else {
dCount += 1
}
}
}
for e in bigLetters {
if !eFound {
if c == e {
eFound.toggle()
let y: Int = bigCount - eCount
var z: Int = 1
for i in bigLetters {
if y == z {
print("[\(c) -> \(i)]")
value += String(i)
}
z += 1
}
} else {
eCount += 1
}
}
}
}
let maximumChars: Int = 15
var string: String = ""
var i: Int = 1
var b: Bool = false
for c in value {
if !b {
if i <= maximumChars {
string += String(c)
} else {
string += "..."
b = true
}
i += 1
}
}
let alert: UIAlertController = UIAlertController(title: "Encoded!", message: "Your input is now encrypted / decrypted.", preferredStyle: UIAlertController.Style.alert)
let cancel: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel, handler: { (alert: UIAlertAction!) in
self.encryptTextField.text! = value
print("OK")
self.encryptTextField.placeholder = "Enter a text to encrypt..."
})
let copy: UIAlertAction = UIAlertAction(title: "Copy!", style: UIAlertAction.Style.default, handler: { (alert: UIAlertAction!) in
print("Copy!")
let pasteboard: UIPasteboard = UIPasteboard.general
pasteboard.string! = value
self.encryptTextField.placeholder = "'" + string + "' was copied!"
})
alert.addAction(cancel)
alert.addAction(copy)
present(alert, animated: true, completion: nil)
So that I can quickly insert the encrypted text into another app (e.g. WhatsApp) I use this code (simplified):
let pasteboard: UIPasteboard = UIPasteboard.general
pasteboard.string! = "Hello World!"
The code works in the simulator: The encrypted text is copied and I can paste it with a double click (see picture).
Paste encrypted text with a double click
But when I run the app on my mobile phone (iPhone 8), the app crashes at this point!
Does anyone know a solution or at least know why?
The solution here is to not force unwrap optional variables in your code.
I highly recommend to read more about Optionals in Swift here.
let input: String = encryptTextField.text!
The text in a UITextField can be nil. The data type is a String optional(String?)
Instead of the above, use if let statements to safely unwrap optionals!
if let input = encryptTextField.text{
// Your logic/code to process input as String goes here
}
Similarly, You can remove the force unwrapping("!") done here.
1. encryptTextField.text! = "". Just use encryptTextField.text = ""
2. pasteboard.string! = value You can remove the force unwrapping("!") done here.
3. pasteboard.string! = "Hello World!"
Basically only force unwrap variables if you for sure know that the value that the optional variable holds is not nil!
Force unwrapping optional variables that hold a nil value will crash you app!
Related
I am trying to get The Information that is added to display in a label on the main screen. I want it to calculate the profit but i get the error " Value of type 'String' has no member 'text' "
#IBAction func addButtonTapped(_ sender: Any) {
let alert = UIAlertController(title: "Product Information", message: nil, preferredStyle: .alert)
alert.addTextField(configurationHandler: { (itemTextField) -> Void in
itemTextField.placeholder = "Item"
})
alert.addTextField(configurationHandler: { (priceTextField) -> Void in
priceTextField.placeholder = "Price"
})
alert.addTextField(configurationHandler: { (salePriceTextField) -> Void in
salePriceTextField.placeholder = "Sale Price"
})
alert.addAction(UIAlertAction(title: "Add", style: .default) { (action) in
let item = alert.textFields?[0].text ?? ""
let price = alert.textFields?[1].text ?? ""
let salesPrice = alert.textFields?[2].text ?? ""
let itemPriceLabel = Double(price.text!)
let salePriceLabel = Double(salesPrice.text!)
if itemPriceLabel != nil && salePriceLabel != nil {
let profitValue = Double (itemPriceLabel! - salePriceLabel!)
ProfitLabel.text = profitValue
}
let product = Product(item: item, price: price, salesPrice: salesPrice)
self.addProduct(product)
})
self.present(alert, animated: true, completion: nil)
storeData()
}
Your price and salesPrice variables are already string only. But you’ve tried to access text field in those variables which is not present in String class in the next statement.
let itemPriceLabel = Double(price)
let salePriceLabel = Double(salesPrice)
And assign your value as follows.
ProfitLabel.text = String(profitValue)
Basically, both price and salesPrice are already Strings (because they took the text from the labels), not labels.
All you need to do is convert them to straight to doubles, no need for .text.
So instead of:
let itemPriceLabel = Double(price.text!)
let salePriceLabel = Double(salesPrice.text!)
Do:
let itemPriceLabel = Double(price)
let salePriceLabel = Double(salesPrice)
There are a few mistakes in the alert action and the naming of the variables is confusing, replace it with
alert.addAction(UIAlertAction(title: "Add", style: .default) { _ in
let itemText = alert.textFields?[0].text ?? ""
let priceText = alert.textFields?[1].text ?? ""
let salesPriceText = alert.textFields?[2].text ?? ""
if let price = Double(priceText),
let salesPrice = Double(salesPriceText),
!itemText.isEmpty {
let profitValue = price - salesPrice
ProfitLabel.text = String(profitValue)
let product = Product(item: itemText, price: price, salesPrice: salesPrice)
self.addProduct(product)
} else {
print("Enter something in 'item' and enter numeric values in 'price' and 'sales price'")
// add error handling
}
}
#IBAction func addToCart(sender: AnyObject) {
let itemObjectTitle = itemObject.valueForKey("itemDescription") as! String
let alertController = UIAlertController(title: "Add \(itemObjectTitle) to cart?", message: "", preferredStyle: .Alert)
let yesAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default) { (action) in
var tabArray = self.tabBarController?.tabBar.items as NSArray!
var tabItem = tabArray.objectAtIndex(1) as! UITabBarItem
let badgeValue = "1"
if let x = badgeValue.toInt() {
tabItem.badgeValue = "\(x)"
}
}
I don't know why I can't just do += "(x)"
Error:
binary operator '+=' cannot be applied to operands of type 'String?' and 'String'
I want it to increment by 1 each time the user selects "Yes". Right now obviously it just stays at 1.
You can try to access the badgeValue and convert it to Integer as follow:
Swift 2
if let badgeValue = tabBarController?.tabBar.items?[1].badgeValue,
nextValue = Int(badgeValue)?.successor() {
tabBarController?.tabBar.items?[1].badgeValue = String(nextValue)
} else {
tabBarController?.tabBar.items?[1].badgeValue = "1"
}
Swift 3 or later
if let badgeValue = tabBarController?.tabBar.items?[1].badgeValue,
let value = Int(badgeValue) {
tabBarController?.tabBar.items?[1].badgeValue = String(value + 1)
} else {
tabBarController?.tabBar.items?[1].badgeValue = "1"
}
To delete the badge just assign nil to the badgeValue overriding viewDidAppear method:
override func viewDidAppear(animated: Bool) {
tabBarController?.tabBar.items?[1].badgeValue = nil
}
Works with Swift 2:
let tabController = UIApplication.sharedApplication().windows.first?.rootViewController as? UITabBarController
let tabArray = tabController!.tabBar.items as NSArray!
let alertTabItem = tabArray.objectAtIndex(2) as! UITabBarItem
if let badgeValue = (alertTabItem.badgeValue) {
let intValue = Int(badgeValue)
alertTabItem.badgeValue = (intValue! + 1).description
print(intValue)
} else {
alertTabItem.badgeValue = "1"
}
I'm creating an iphone app using swift 2. I got the app to be able to read barcodes and display the barcode. Also I learned core data and using it to import a .csv file with a bunch of product information which includes the barcode. Now when a barcode is scanned, I want it to search through the file and display the information relating to it. Here are parts of my code, how will I go about doing this? To be honest, I have programmers block right now and had for the past two days. Any help or guidance will be appreciated.
Here is the CSV Parser:
func parseCSV (contentsOfURL: NSURL, encoding: NSStringEncoding) -> [(title:String, price:String, weight:String, box_length:String, box_width:String, box_height:String, sku:String, barcodeNum:String )]? {
// Load the CSV file and parse it
let delimiter = ","
var items:[(title:String, price:String, weight:String, box_length:String, box_width:String, box_height:String, sku:String, barcodeNum:String )]?
do {
let content = try String(contentsOfURL: contentsOfURL, encoding: encoding)
print(content)
items = []
let lines:[String] = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) as [String]
for line in lines {
var values:[String] = []
if line != "" {
// For a line with double quotes
// we use NSScanner to perform the parsing
if line.rangeOfString("\"") != nil {
var textToScan:String = line
var value:NSString?
var textScanner:NSScanner = NSScanner(string: textToScan)
while textScanner.string != "" {
if (textScanner.string as NSString).substringToIndex(1) == "\"" {
textScanner.scanLocation += 1
textScanner.scanUpToString("\"", intoString: &value)
textScanner.scanLocation += 1
} else {
textScanner.scanUpToString(delimiter, intoString: &value)
}
// Store the value into the values array
values.append(value as! String)
// Retrieve the unscanned remainder of the string
if textScanner.scanLocation < textScanner.string.characters.count {
textToScan = (textScanner.string as NSString).substringFromIndex(textScanner.scanLocation + 1)
} else {
textToScan = ""
}
textScanner = NSScanner(string: textToScan)
}
// For a line without double quotes, we can simply separate the string
// by using the delimiter (e.g. comma)
} else {
values = line.componentsSeparatedByString(delimiter)
}
// Put the values into the tuple and add it to the items array
//(title:String, price:String, weight:String, box_length:String, box_width:String, box_height:String, sku:String, barcodeNum:String )
let item = (title: values[0], sku: values[1], price: values[2], weight: values[3], box_length: values[4], box_width: values[5], box_height: values[6], barcodeNum: values[7])
items?.append(item)
}
}
} catch {
print(error)
}
return items
}
When Barcode is detected:
func barcodeDetected(code: String) {
print(code)
if(barcodeDelegate != nil){
barcodeDelegate!.barcodeFound(code)
}
// Let the user know we've found something.
let alert = UIAlertController(title: "Found a Barcode!", message: code, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Search", style: UIAlertActionStyle.Destructive, handler: { action in
// Remove the spaces.
let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
// EAN or UPC?
// Check for added "0" at beginning of code.
let trimmedCodeString = "\(trimmedCode)"
var trimmedCodeNoZero: String
if trimmedCodeString.hasPrefix("0") && trimmedCodeString.characters.count > 1 {
trimmedCodeNoZero = String(trimmedCodeString.characters.dropFirst())
// Send the doctored UPC to DataService.searchAPI()
DataService.searchCode(trimmedCodeNoZero)
} else {
// Send the doctored EAN to DataService.searchAPI()
DataService.searchCode(trimmedCodeString)
}
self.navigationController?.popViewControllerAnimated(true)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
Dataservice.swift file
import Foundation
class DataService {
static let dataService = DataService()
static func searchCode(codeNumber: String) {
}
}
I can post more of my project if it helps, but I feel like this is already too much information, anything will help at this point on how to tackle this.
I have the following string I would like to edit:
var someString = "I wan't this text {something I don't want}"
I would like to remove all the text contained in the two braces, no matter how long that text is. I have been using the follow code to remove a section of a String when I know the range:
extension String {
mutating func deleteCharactersInRange(range: NSRange) {
let mutableSelf = NSMutableString(string: self)
mutableSelf.deleteCharactersInRange(range)
self = mutableSelf
}
}
However, I do not know the range in my problem. Any ideas?
Working with strings and ranges can be quite challenging when mixing NSString and NSRange with Swift's String and Range.
Here is a pure Swift solution.
var someString = "I wan't this text {something I don't want}"
let rangeOpenCurl = someString.rangeOfString("{")
let rangeCloseCurl = someString.rangeOfString("}")
if let startLocation = rangeOpenCurl?.startIndex,
let endLocation = rangeCloseCurl?.endIndex {
someString.replaceRange(startLocation ..< endLocation, with: "")
}
With a RegEx pattern to match anything enclosed with curly brackets:
var sourceString: String = "I wan\'t this text {something I don't want}"
let destinationString = sourceString.stringByReplacingOccurrencesOfString("\\{(.*?)\\}", withString: "", options: .RegularExpressionSearch)
print(destinationString)
This will print "I wan't this text " without the double quotes.
extension String {
func getCurlyBraceRanges() -> [NSRange] {
var results = [NSRange]()
var leftCurlyBrace = -1
for index in 0..<self.characters.count {
let char = self[self.startIndex.advancedBy(index)]
if char == Character("{") {
leftCurlyBrace = index
} else if char == Character("}") {
if leftCurlyBrace != -1 {
results.append(NSRange(location: leftCurlyBrace, length: index - leftCurlyBrace + 1))
leftCurlyBrace = -1
}
}
}
return results
}
mutating func deleteCharactersInRange(range: NSRange) {
let mutableSelf = NSMutableString(string: self)
mutableSelf.deleteCharactersInRange(range)
self = String(mutableSelf)
}
mutating func deleteCharactersInRanges(ranges: [NSRange]) {
var tmpString = self
for i in (0..<ranges.count).reverse() {
tmpString.deleteCharactersInRange(ranges[i])
print(tmpString)
}
self = tmpString
}
}
var testString = "I wan't this text {something I don't want}"
testString.deleteCharactersInRanges(testString.getCurlyBraceRanges())
Output: "I wan't this text "
#IBAction func addToCart(sender: AnyObject) {
let itemObjectTitle = itemObject.valueForKey("itemDescription") as! String
let alertController = UIAlertController(title: "Add \(itemObjectTitle) to cart?", message: "", preferredStyle: .Alert)
let yesAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default) { (action) in
var tabArray = self.tabBarController?.tabBar.items as NSArray!
var tabItem = tabArray.objectAtIndex(1) as! UITabBarItem
let badgeValue = "1"
if let x = badgeValue.toInt() {
tabItem.badgeValue = "\(x)"
}
}
I don't know why I can't just do += "(x)"
Error:
binary operator '+=' cannot be applied to operands of type 'String?' and 'String'
I want it to increment by 1 each time the user selects "Yes". Right now obviously it just stays at 1.
You can try to access the badgeValue and convert it to Integer as follow:
Swift 2
if let badgeValue = tabBarController?.tabBar.items?[1].badgeValue,
nextValue = Int(badgeValue)?.successor() {
tabBarController?.tabBar.items?[1].badgeValue = String(nextValue)
} else {
tabBarController?.tabBar.items?[1].badgeValue = "1"
}
Swift 3 or later
if let badgeValue = tabBarController?.tabBar.items?[1].badgeValue,
let value = Int(badgeValue) {
tabBarController?.tabBar.items?[1].badgeValue = String(value + 1)
} else {
tabBarController?.tabBar.items?[1].badgeValue = "1"
}
To delete the badge just assign nil to the badgeValue overriding viewDidAppear method:
override func viewDidAppear(animated: Bool) {
tabBarController?.tabBar.items?[1].badgeValue = nil
}
Works with Swift 2:
let tabController = UIApplication.sharedApplication().windows.first?.rootViewController as? UITabBarController
let tabArray = tabController!.tabBar.items as NSArray!
let alertTabItem = tabArray.objectAtIndex(2) as! UITabBarItem
if let badgeValue = (alertTabItem.badgeValue) {
let intValue = Int(badgeValue)
alertTabItem.badgeValue = (intValue! + 1).description
print(intValue)
} else {
alertTabItem.badgeValue = "1"
}