Swift Optional error - ios

Keeping this question up to shame myself, but no help is necessary. I just didn't understand what was going on. I thought the simulator was crashing, but it turns out I'd somehow accidentally set a breakpoint, and that was what was causing it to return to Xcode.
Feel free to ignore, but maybe leaving this up will help someone else new to Xcode.
I'm in the process of learning Swift, and taking an introductory course from lynda.com. I have run into an error that I'm sure is due to the changes in Swift since the course was produced (the instructor is on Xcode 6.0.1 while I am using 6.4). I would love some help figuring it out so I can proceed.
The example project is building a simple calculator. The step I'm on is creating the code to take a button click (any of the numbers on the calculator's UI), and display it in the UILabel that is the calculator's display.
This is the function that is apparently causing the issue:
var valueString:String! = ""
#IBAction func tappedNumber(sender: UIButton) {
var str:String! = sender.titleLabel!.text
valueString = valueString.stringByAppendingString(str)
label.text = valueString
}
It doesn't show up as an error in Xcode, but when I run the simulator, when I click on one of the number buttons, the simulator crashes back to Xcode, and the line var str:String! = sender.titleLabel!.text is highlighted.
From the little bit I've already picked up, I'm guessing the error has to do with the way Swift changed how it deals with optionals. But I just don't know enough to be sure, or how to fix it.
Any help would be much appreciated!

Try this:
var str:String! = sender.titleLabel?.text

valueString and str do not need to be optionals if they are never nil.
Try:
var valueString: String = ""
#IBAction func tappedNumber(sender: UIButton) {
if let label = sender.titleLabel {
let str = label.text
valueString = valueString.stringByAppendingString(str)
}
label.text = valueString
}

Related

XCode8 / Swift will not let me set a textField's text equal to a String?

I am very new to iOS application programming (started learning just under a week ago!) so I'm sure what I'm asking is very obvious!
So I am working on an Application for my work study position and cannot for the life of me figure out what I'm doing wrong. Basically, all I want to do is check to make sure each field is filled in before allowing the user to carry on. I did a similar thing in another view controller to check to see if what was entered is equal to the password I set for the program (I am sure there is a better way to do this, but because I'm self learning and it's my first application, I'm not worried too much. I have a whole year to polish it!)
Here is the code for that (if you have suggestions that may improve it, feel free, but for the most part, I'm just trying to figure out why this worked and the other code is not)
#IBAction func enterPassword(_ sender: Any) {
pw = password.text!
let message = "Incorrect Password"
if(pw == "RISE")
{
performSegue(withIdentifier: "ToMenu", sender: self)
}
else {
incorrect.text = message
}
}
please note that the variable "pw" is a global variable and is declared outside of the class. Password and incorrect are both labels previously declared as well.
Here is the code that is in question. I do not understand why I am getting an error for this. it is "thread 1: exc_bad_instruction". I'm honestly not even sure what this error means so if you happen to know what it means, please enlighten me! I included all the code for the viewController with the issue.
import UIKit
var studName = " "
class CreateViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var ID: UITextField!
#IBOutlet weak var YR: UITextField!
#IBOutlet weak var test: UILabel!
#IBAction func endCreate(_ sender: Any) {
studName = name.text!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
What am I doing wrong here? I am really uncertain and confused. I am pretty good at coding in Java and C++ so I never expected to have issues learning but it's totally different and is taking a lot longer than I expected (It took me about a week to learn C#...).
Thanks for any help!
EDIT:
(I need 10 reputation to post more than two links, so I deleted the error. lol I guess I'll go answer some questions or something)
Click here to see StoryBoard
Click here to see Connection to NAME (note I reconnected it and used all caps, error is still an issue)
if there is any other information that may help you understand, please let me know! Thanks!
I'm assuming here that you are experiencing the crash on this line
studName = name.text!
? (If not could you please include the stack trace to indicate where you are getting an issue?)
It looks like either name is nil, or text is nil both of which are being force unwrapped here. So either you haven't connected this text field properly in interface builder, or there is just no text in it.
My general rule is (and I'm sure people will disagree with this):
NEVER use !
Such a simple rule :)
I reason this that, force unwrapping an optional variable is never (very, very rarely) required, and implies an assumption of a value that was clearly intended to be allowed to be nil (otherwise it would not be optional). Typically, if you find yourself using a ! on one of your own variables, consider refactoring the variable to not be optional, otherwise make heavy use of optional binding and/or guard statements to avoid forcing things.
(My one main caveat for this is creating instance variables that require self as a parameter for initialisation. Where the instance variable is an implicitly unwrapped optional, but is set immediately after the call to super's initialiser, and never again)
Also, while it works and is valid code, it is not typical to use global variables for things like studName, this should be an instance variable in your controller.
Do feel free to shout if you have any other questions about this (or anything iOS/swift). If you're just getting started, I can't recommend http://web.stanford.edu/class/cs193p highly enough! A thorough rundown of all core iOS technologies and design patterns.
EDIT for comment
In this case you could avoid using this ! by making your variable optional, you could declare studName using
var studName: String? = " "
this makes it an optional variable which will allow you to assign a nil value to it.
If you are sure you don't want it to be nil, you could also do your assignment as
studName = name.text ?? " "
This will check the value of name.text and if it is not nil assign to studName, otherwise it will assign the value on the right " ". It essentially allows you to provide an default value for an assignment that is not optional. It is shorthand for:
if let text = name.text {
studName = text
} else {
studName = " "
}
Great start. Here is how I would write it:
#IBAction func enterPassword(_ sender: Any) {
//Make sure we trim off any white space or new lines and then optionally bind the text in the text field.
guard let passwordText = self.password.text?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) else {
self.incorrect.text = "Please enter a password."
return
}
//Make sure text is not a blank string eg: ""
guard !passwordText.isEmpty else {
self.incorrect.text = "Please enter a password."
return
}
//Make sure it is the password we want.
guard passwordText == "ThePassword" else {
self.incorrect.text = "Not the correct password."
return
}
//Success! Do other stuff here. Like perform a segue.
}
And just for good measure and a little fun. Here is a great extension on UITextField to provide a nice little shake animation if anything is invalid.
extension UITextField {
func shake(times T: Int, distance D: CGFloat) {
UIView.animate(withDuration: 0.06, animations: {
self.transform = CGAffineTransform(translationX: D, y: 0)
}) { (didComplete) in
UIView.animate(withDuration: 0.06, animations: {
self.transform = CGAffineTransform.identity
}, completion: { (didComplete) in
guard T > 0 else {
return
}
self.shake(times: T - 1, distance: D)
})
}
}
}
George Green is right. Force unwrapping in Swift is considered a code smell. Always optionally bind an optional value. Makes for a little more verbose code but keeps everything safe.
There you go... here I See the problem. I removed Outlet to the TextField and immediately I saw that error.
Please make sure you connect the IBOutlet properly and it works perfectly as shown below.
Hope this helps you.
Firstly,
Take an Outlet for the TextField as shown below
// MARK:- IBOutlets
#IBOutlet var passwordTextField: UITextField!`
Take a Button & assign IBAction for the the button, "passwordCheck" in this case.
You can compare TextField text to string as shown below
// MARK:- IBActions
#IBAction func passwordCheck(_ sender: Any) {
if passwordTextField.text == "RISE" {
print("perform Segue")
} else {
print("Do nothing")
}
}
Let me know if you have any doubts...

App crashes if playlist count = 0 (Do empty playlists have a persistentid?)

My app is crashing if a playlist is empty (no songs). My app works for all non-empty playlists. It seems like there isn't a persistentid for an empty playlist, but I think I am wrong on that.
let qryPlaylists = MPMediaQuery.playlistsQuery()
var selectedPlaylistTitle: String!
var selectedPlaylistPersistentID: UInt64!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath: NSIndexPath = playlistsTableView.indexPathForSelectedRow!
let rowItem = qryPlaylists.collections![indexPath.row]
let playlistSize = rowItem.count
print("playlistSize = ", playlistSize)
selectedPlaylistTitle = rowItem.valueForProperty(MPMediaPlaylistPropertyName) as! String
print("selectedPlaylistTitle = ", selectedPlaylistTitle)
// Will crash on this next line if the playlistSize = 0
selectedPlaylistPersistentID = rowItem.items[0].persistentID
// If I use following line instead, it will crash on any playlist
// selectedPlaylistPersistentID = rowItem.valueForProperty(MPMediaPlaylistPropertyPersistentID) as! UInt64
// This line will never be printed
print("selectedPlaylistPersistentID = ", selectedPlaylistPersistentID)
}
Thanks in advance for any help!
If an array such as items is empty, it has no index 0 and will crash if you refer to it, with an out-of-bounds error. So if you don't want to crash, don't do that. You already know that items is empty, because rowItem.count told you so; as you said yourself, playlistSize is 0 in that case.
A simple-minded way to look at it is this: the largest legal index in an array is one less than its count.
Another issue you asked about is that this line always crashes:
selectedPlaylistPersistentID = rowItem.valueForProperty(MPMediaPlaylistPropertyPersistentID) as! UInt64
The problem here is that you are apparently using Swift 2.x. (You should have said that in your question; I deduce it, though, from the fact that valueForProperty has not changed to value(forProperty:), which is what it is called in Swift 3.)
In Swift 2, you cannot cast directly down to UInt64. (I am surprised that the compiler does not draw your attention to this fact with a warning.) Thus, the cast fails and you crash. You need to cast down to NSNumber and then take that NSNumber's unsignedLongLongValue.
And while you are doing this, you really should stop using exclamation marks in your code. When I say "cast down", I mean "cast down safely". My own Swift 2 code for doing this sort of thing looks like this:
if let id = (rowItem.valueForProperty(MPMediaItemPropertyAlbumPersistentID) as? NSNumber)?.unsignedLongLongValue {
// ...
}

Swift - iterating through characters in string causes memory leak

this is my very first post! I'm relatively new to Swift, and don't have a computer science background, so I'm still prone to a lot of newbie problems - but I've been able to solve them all so far, partly from browsing the excellent answers on StackOverflow. However, I've come across something that's really stumped me, and I've spend a whole week trying to solve it with no luck.
What I'm trying to do is take text in Chinese from a UITextView and then convert this to an array of individual Chinese characters, which is then used for various processing and analysis. However, this causes a leak.
In this greatly simplified example, which reproduces the same leak, there is a TextView and a Button; when the user presses the button, the function makeArray is called, which converts the text to an array of characters (actually Strings of single characters, because I need it to be strings for some of the stuff I will do with it). The class TextProcessing that contains this function is used as a singleton (yeah, I know that apparently singletons are supposed to be bad, for reasons I don't fully understand, but for various reasons involving other parts of the code it works best when there is a single instance of this class), and the text from the UITextView is passed into it, where it's then converted to the array, as you can see below:
class ViewController: UIViewController {
#IBOutlet weak var textBox: UITextView!
#IBOutlet weak var doneButton: UIButton!
#IBAction func pressDoneButton(_ sender: Any) {
let textToAnalyze = textBox.text!
TextProcessing.textProcessing.makeArray(textToAnalyze)
}
}
class TextProcessing {
static let textProcessing = TextProcessing()
private let language = "Chinese"
private var sourceTextArray: [String]!
func makeArray (_ sourceText: String) {
if language == "Chinese" {
sourceTextArray = sourceText.characters.map { String($0) }
} else if language == "English" {
sourceTextArray = sourceText.components(separatedBy: " ")
}
// then do some stuff with this array
}
}
When I run this on the Leaks Instruments I get leaks of "Malloc 16 Bytes" and "CFString", with the number of instances of each being roughly the same as the number of array elements (thus the number of characters in the string). When I look at the Call Tree and drill down, the problem line is "sourceTextArray = sourceText.characters.map { String($0) }".
By the way, this happens with relatively long texts - with short ones, either there's no problem or Instruments doesn't detect it.
However, if I make an array by separating the string into words according to spaces, like I would want in a language like English, there's no leak - so if I change the language variable to "English" in the example code, it works fine (but of course doesn't give me the array that I want). I thought that maybe the problem was in the "map" method, since it uses a closure and it's easy to have leaks with closures, but when I try other ways of putting it into an array of characters, such as using a for loop and iterating over each character that way, it still has the same problem.
If, instead of getting the text from the UITextView, I do this instead:
class ViewController: UIViewController {
#IBOutlet weak var textBox: UITextView!
#IBOutlet weak var doneButton: UIButton!
#IBAction func pressDoneButton(_ sender: Any) {
let textToAnalyze = "blah blah put a long string of Chinese text here"
TextProcessing.textProcessing.makeArray(textToAnalyze)
}
}
there's no problem. Likewise, if in the makeArray function, if I ignore sourceText and instead do this:
func makeArray (_ sourceText: String) {
if language == "Chinese" {
let anotherText = "blah blah some text here"
sourceTextArray = anotherText.characters.map { String($0) }
}
// then do some stuff with this array
}
there's also no leak. So, something about the combination of getting the string from the text box, passing it into the function, and then putting it into an array of characters is causing the leak.
I've spent countless hours scouring the internet and reading everything about ARC in Swift, and I've tried all sorts of stuff with weak/unowned etc. and nothing seems to work. What's going on here and how could this be solved?
Edit:
So it appears that this might just be a problem with Simulator and/or Instruments. When I run it on the device, and just monitor memory usage in xcode debug, there's no increase even when doing it 100+ times, so I guess it's OK...it still seems weird that it would show a leak in Instruments though.
It's instruments bug(there is a lot of issues). Your code is OK.
I just filed a bug report (FB7684067).
The following simple macOS command line application will grow to over 1GB in only a few minutes:
import Foundation
let line = "124;5678;90123"
while true {
let fields = line.components(separatedBy: ";")
assert(fields[1] == "5678")
}

Swift returning fatal error after pressing button and getting its currentTitle

So, I am following a video class of Switf for iOS 8 on iTunes (https://itunes.com/StanfordSwift if anyone is interested, its free), and well, now of course iOS 9 available and I think I am having a problem for the different versions and I cannot find ANYTHING on the web about this. I hope that anyone reading this can help me out a bit.
The project is about a Calculator, so I have in the UI the buttons with the numbers, these buttons sends its value (currentTitle) to my appendDigit function. Xcode does not mark any error, but still, at runtime when I press the button I get a fatal error, and honestly, I don't understand what is going on.
I tried setting and IF statement for when its available currentTitle or not, but well, this doesn't help at all since I need to be getting the buttons number.
Here is the code:
#IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
display.text = display.text! + digit
}else{
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
}
The line that is causing the error is: let digit = sender.currentTitle!
Here is also a screenshot of what I am getting, seems that the button is sending the value empty... but as you can see, there are the numbers!
Thank you everyone !
There are numbers on the buttons, you claimed. But I think some of them, or one of them, don't.
The crash is caused by unwrapping an Optional value, which is the following line:
let digit = sender.currentTitle!
By looking at your layout in storyboard, I think you are connecting a lot of buttons to the same IBAction method, which is perfectly fine.
But as mentioned, some of them or one of them, whose currentTitle have not been set either programmatically or in storyboard ("title"), hence the crash.
So, please double check whether one of them or some of them whose currentTitle have not been set.
I was able to re-produce the error by deliberately leaving out currentTitle in one of the buttons. The crash occurred exactly like the one you're experiencing:
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Or, one could proceed in a safer way:
#IBAction func appendDigit(sender: UIButton)
{
if let digit = sender.currentTitle
{
if userIsInTheMiddleOfTypingANumber
{
display.text = display.text! + digit
}
else
{
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
}
}

Using UIButton Text as Text Input - Swift

Hello I have a profilelbl variable as below which is a uibutton. I want the text of the button to be an input in my database (parse). But I couldn't figured it out. I tried lots of things but still getting error:
#IBOutlet weak var profileLbl: UIButton!
var notification = PFObject(className: "notifications")
notification["actionReceiverName"] = profilelbl.text /*not working*/
/* also tried
notification["actionReceiverName"] = sender.profilelbl.text
notification["actionReceiverName"] = profilelbl.title */
you can do it easy like that
if let button = profilelbl as? UIButton {
if let title = button.titleForState(.Normal) {
println(title)
notification["actionReceiverName"] = title
}
}
Using UI objects to save/load data is a very bad idea. Using user-visible strings programmatically is an even worse idea. #ÖzgürErsil answered the question you asked, but the better answer to your question is "Don't do that. Ever."
Here are 2 examples where your approach will fail:
If 6 months later you want to change your UI and rename your button,
you won't remember that the button title is used in code and your
code will break. To that you would have to alter your database to
use a different string value.
If you decide to localize your app for foreign
languages, the button titles will come up in the local language, and
your code will break. There is no clean way to fix this problem,
since each local language would use a different version of the
button title.
It would be better to put unique tag numbers on your buttons, then look up text strings using the tags and pass those strings to your database.
Say you have button tags starting at 100.
You'd use code like this:
let buttonStrings = ["button1", "button2", "button3"]
let baseButtonTag = 100;
#IBAction func handleButton(sender: UIButton)
{
let tag = sender.tag
if tag >= baseButtonTag && tag < baseButtonTag + buttonStrings.count
{
let index = sender.tag - baseButtonTag
let buttonString = buttonStrings[index];
//Now use buttonString with your database as desired.
}
}

Resources