Can't unwrap optional without forced unwrapping/nil error - xcode

I'm working on a text editor and am having trouble with string optionals. I want to use a textView's string method; since it's an optional, Xcode insists that I unwrap it. When I use forced unwrapping (which is what Xcode recommends) I get runtime errors; I'd prefer to use optional chaining so that nil values don't cause a crash. But I can't get optional chaining to work.
To get open and save working, I'm trying to use self.textViewOne.string = self.text in windowControllerDidLoadNib and self.text = self.textViewOne.string in dataOfType. But I get crashes of "unexpectedly found nil while unwrapping an Optional value". Documentation tells me I should use if-let or even if-var to do this properly, but I can't; when I try to add if-let or if-var, I get an "Expected pattern" error, probably because the self.text variable already exists - but I don't know how else to unwrap properly.
In dataOfType I even tried to unwrap it with a kludgey regular if-then statement:
if ((self.textViewOne.string) != nil)
{
self.text = self.textViewOne.string
}
else
{
self.text = ""
}
but even that doesn't work: Xcode still insists on a ! after self.textViewOne.string, and with or without the ! I still get a "fatal error: unexpectedly found nil while unwrapping an Optional value".
EDIT: Here's the complete code for the Document class as it currently stands (including a bit of tinkering after the original post, but still getting the error):
import Cocoa
class Document: NSDocument {
#IBOutlet var textViewOne: NSTextView!
#IBOutlet var textViewTwo: NSTextView!
var text = ""
override init() {
super.init()
// Add your subclass-specific initialization here.
}
override func windowControllerDidLoadNib(aController: NSWindowController) {
// The window has loaded, and is ready to display.
// Take the text that we loaded earlier and display it in the text field
super.windowControllerDidLoadNib(aController)
self.textViewOne.string = self.text
}
override class func autosavesInPlace() -> Bool {
return true
}
override var windowNibName: String? {
// Returns the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this property and override -makeWindowControllers instead.
return "Document"
}
override func dataOfType(typeName: String?, error outError: NSErrorPointer) -> NSData? {
// Convert the contents of the text field into data, and return it
if (self.textViewOne == nil)
{
println ("self.textViewOne is nil.")
}
if let someText = self.textViewOne.string {
self.text = someText
} else {
self.text = ""
}
return self.text.dataUsingEncoding( NSUTF8StringEncoding, allowLossyConversion: false)
}
override func readFromData(data: NSData, ofType typeName: String?, error outError: NSErrorPointer) -> Bool {
// Attempt to load a string from the data; if it works, store it in self.text
if data.length > 0
{
let string = NSString( data: data, encoding: NSUTF8StringEncoding)
self.text = string!
}
else
{ self.text = "" }
return true
}
}

What about using an if let to unwrap a non-nil value from self.textViewOne?
if let someText = self.textViewOne.string {
self.text = someText
} else {
self.text = ""
}

Related

Applescript Xcode, I need to read a file and show changes continuously on a label

The code i have updates the label when i change the TextField content, but it's not what I need:
property aTextField : missing value
property aTextLabel : missing value
on controlTextDidChange_(aNotification)
log aTextField's stringValue
aTextLabel's setStringValue: aTextField's stringValue
end textDidChange_
I need something like:
property aTextLabel : missing value
on applicationWillFinishLaunching_(aNotification)
repeat
set MyFileTextContent to (do shell script "cat /Users/Johann/Desktop/myLabelValue.txt")
aTextLabel's setStringValue:MyFileTextContent
end repeat
end applicationWillFinishLaunching_
But obviously this does not work, but I do not know how to achieve my goal.
Warning: I don't write AppleScripts. But this will work for simple cases. Note that most (all ?) implementations of tail -f use a delay, then stat the file for changes and then if there are changes it reads the file. So keep a variable to show the old contents and test against that.
on applicationWillFinishLaunching_(aNotification)
set oldContents to ""
repeat
set MyFileTextContent to (do shell script "tail /Users/Johann/Desktop/myLabelValue.txt")
set aTextLabel to MyFileTextContent
if MyFileTextContent is not equal to oldContents
set oldContents to MyFileTextContent
set aTextField to MyFileTextContent
end if
delay 5
end repeat
end applicationWillFinishLaunching
You will need to adjust the setting of aTextLabel. You will need to experiment with the delay. Finally, note that I used a tail rather than cat. Adjust according to your needs._
I can only give you an example using swift. Hopefully this will get you on track. This is a simpler approach that uses Timer.scheduledTimer. This is the complete code for a ViewController. The storybook should have a label (NSTextField) called fileContents. There is a start and end button. Create a text file called mytext.txt on your desktop for testing. If you are having problems getting it to work, I could post the entire Xcode project on github.
//
// ViewController.swift
// monitorFile
//
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var fileContents: NSTextField!
var monitorTimer: Timer!
var homeDir : URL!
var desktopFile : URL!
// This keeps us from updating the display when content didn't change
var oldContent: String!
override func viewDidLoad() {
super.viewDidLoad()
homeDir = FileManager.default.homeDirectoryForCurrentUser
desktopFile = homeDir.appendingPathComponent("Desktop", isDirectory: true).appendingPathComponent("mytext").appendingPathExtension("txt")
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
override func viewWillDisappear() {
if (monitorTimer != nil) {
endTimer()
}
}
func startMonitor() {
if (monitorTimer == nil) {
// timeInterval is a double representing seconds
monitorTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateDisplay), userInfo: nil, repeats: true)
}
}
#objc func updateDisplay() {
let newContent = readIt(location: desktopFile)
if (newContent != oldContent) {
oldContent = newContent
fileContents.stringValue = newContent
}
}
func endTimer() {
if (monitorTimer != nil) {
monitorTimer.invalidate()
}
}
func readIt(location: URL) -> String {
var contents = "No content"
do {
contents = try String(contentsOf: location)
} catch {
print("Failed reading from URL: \(desktopFile), Error: " + error.localizedDescription)
}
return contents
}
#IBAction func stopMonitor(_ sender: NSButton) {
endTimer()
}
#IBAction func startMonitor(_ sender: NSButton) {
startMonitor()
}
}
The solution:
property NSTimer : class "NSTimer"
property myTextField : missing value
on log1:sender
performSelector_withObject_afterDelay_("log1:", missing value, 1.0)
set myText to (do shell script "cat texFile.txt")
myTextField's setStringValue:myText
end log1:
on applicationWillFinishLaunching:aNotification
performSelector_withObject_afterDelay_("log1:", me, 0)
end applicationWillFinishLaunching:

Found nil while unwrapping an optional value - Swift 2

I am getting this error which I cannot explain:
#IBOutlet weak var licencePlateLabel: UILabel!
var editItem: CarValues? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let editedCar = self.editItem {
if let licencePlate = self.licencePlateLabel {
licencePlate.text? = editedCar.licencePlate!//this gives me nil
}
else {
print("value was nil")
}
print(editedCar.licencePlate!)//this is giving me the correct value
}
if I replace the
if let licencePlate = self.licencePlateLabel {
licencePlate.text! = editedCar.licencePlate!
}//this throws an error "found nil......"
even if I do this I m still getting the "found nil..."
func configureView() {
licencePlateLabel.text = "test"
[...]
}
BUT if I put the above on viewDidLoad then it works fine
override func viewDidLoad() {
licencePlateLabel.text = "test"
[...]
}
What is going on in this code?
EDIT
I am passing the value of the editItem from the detailView to the EditView like this:
#IBAction func editButtonTapped(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let editScreen = storyBoard.instantiateViewControllerWithIdentifier("ID_EditViewController")
self.navigationController?.pushViewController(editScreen, animated: true)
let controller = EditViewController()
controller.editItem = detailItem
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
You don't unwrap properties to set them, only to read from them. So your code can simply be:
if let licencePlate = self.licencePlateLabel {
licencePlate.text = editedCar.licencePlate
}
Note that because licencePlate.text is an optional value anyway, there is also no need to unwrap editedCar.licencePlate. It's ok to use its value whether it is nil or contains a String.

unexpectedly found nil while unwrapping an Optional value(optional binding)

import UIKit
import Parse
class HomePageViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var homPageTableView: UITableView!
var imageFiles = [PFFile]()
var imageText = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// DO any additional setup after loading the view
var query = PFQuery(className: "Posts")
query.orderByAscending("createdAt")
query.findObjectsInBackgroundWithBlock {
(posts : [AnyObject]?, error : NSError?) -> Void in
if error == nil {
//success fetxhing objects
println(posts?.count)
for post in posts! {
self.imageFiles.append(post["imageFile"] as! PFFile) ---------error here
self.imageText.append(post["imageText"] as! String)
}
println(self.imageFiles.count)
}else{ println(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
As a title, It is keep saying "unexpectedly found nil while unwrapping an Optional value" at the line I draw.
There are a lot of questions about this, it is hard to read code for me, And even if I undertood, mine didn't go right.
where should I use optional binding?
And can u explain it with really easy example what is optional binding?
Thank you
Your post dictionary does not contain an imageFile key. When you access a dictionary using post["imageFile"] the result is the value (if the key exists) and nil (if the key does not exist). You can distinguish between these cases using
if let imageFile = post["imageFile"],
let imageText = post["imageText"] {
self.imageFiles.append(imageFile as! PFFile)
self.imageText.append(imageText as! String)
} else {
print("imageFile and/or imageText missing from \(post)")
}
You need to unwrap your posts before looping through them because posts: [AnyObject]? is optional.
if error == nil {
if let postData = posts{
//success fetxhing objects
println(postData?.count)
for post in postData! {
self.imageFiles.append(post["imageFile"] as! PFFile)
self.imageText.append(post["imageText"] as! String)
}
println(self.imageFiles.count)
}
}else{
println(error)
}

Continuously update a UILabel in Swift

I have a function, shown below, that I would like to continuously update. It is taking data from a webpage, and every so often that webpage is updated to reflect current information. Is there a way that I can catch this update and reflect that in my application? I'm pretty new to Swift and iOS programming. Some of the code made seem very bizarre, but it currently works for whatever song is playing when you first open the app (that is, it updates the text to show that song playing but doesn't update later).
let url = NSURL(string: "http://api.vicradio.org/songs/current")!
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
if error != nil {
return
}
let name = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
var songName = ""
var artistName = "by "
var quoteNumber = 0
for character in name.characters {
if character == "\"" {
quoteNumber++
}
if quoteNumber == 3 && character != "\"" {
songName += String(character)
} else if quoteNumber == 7 && character != "\"" {
artistName += String(character)
}
}
if (songName != "no song metadata provided") {
self.SongNowText.text = songName
self.ArtistNowText.text = artistName
self.SongNowText.setNeedsDisplay()
self.ArtistNowText.setNeedsDisplay()
} else if (songName == "no song metadata provided") {
self.SongNowText.text = "The Best of What's Next!"
self.ArtistNowText.text = "only on VIC Radio"
}
}
task!.resume()
It looks like the URL you're accessing there is an API endpoint putting out JSON. I highly recommend using NSJSONSerialization.JSONObjectWithData to parse the response body into a dictionary and use that instead of rolling your own solution by counting quote marks.
The callback to dataTaskWithURL is executed on a background thread. Avoid updating the UI on anything besides the main thread because it can cause problems. Use dispatch_async to execute your UI update function on the main thread as in the example.
All you can do with this API is send it requests and read the responses. You can poll the endpoint at a regular interval while the app is open and get decent results from that. NSTimer is one way to do that, and it requires you put the method you want to execute repeatedly in a class inheriting from NSObject because it depends on Objective-C style message sending.
Throw this in a playground and try it:
import Cocoa
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()
class RadioDataAccessor : NSObject {
private let callback: [String : AnyObject] -> Void
init(callback: [String : AnyObject] -> Void) {
self.callback = callback
super.init()
NSTimer.scheduledTimerWithTimeInterval(5.0, target: self,
selector: "updateData", userInfo: nil, repeats: true)
// just so it happens quickly the first time
updateData()
}
func updateData() {
let session = NSURLSession.sharedSession()
let url = NSURL(string: "http://api.vicradio.org/songs/current")!
session.dataTaskWithURL(url) { data, response, error in
if error != nil {
return
}
var jsonError = NSErrorPointer()
let json = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.allZeros,
error: jsonError) as? [String : AnyObject]
if jsonError != nil {
return
}
dispatch_async(dispatch_get_main_queue()) { self.callback(json!) }
}.resume()
}
}
RadioDataAccessor() { data in
println(data)
}
You may want to save the timer to a variable and expose a function that lets you invalidate it.

Swift Xcode 6: How do I use multiple functions in one statement?

I am a newcomer in xcode and swift, and I am having a problem with using two IBActions to allow a button to be enabled. I have 2 text fields, and I have a button that is disabled. I want the button to be enabled when both of the text fields are filled in. How can I do this? So far I have declared two functions with IBActions for the two text fields:
#IBAction func yourWeightEditingDidBegin(sender: AnyObject) {
}
#IBAction func calorieNumberEditingDidBegin(sender: AnyObject) {
}
Thanks!
One way is to use the UITextFieldDelegate function instead of IBOutlets:
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
if yourWeight.text != "" && calorieNumber.text != "" {
button.enabled = true
}
return true
}
I, too, implement UITextFieldDelegate, but I use shouldChangeCharactersInRange. This way, the status of that button changes as the user types:
If dealing with only two text fields, it looks like:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// get the value this text field will have after the string is replaced
let value: NSString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
// get the value of the other text field
let otherValue = textField == yourWeight ? calorieNumber.text : yourWeight.text
// only enable done if these are both non-zero length
doneButton.enabled = (value != "" && otherValue != "")
return true
}
Clearly, for this to work, you must specify the delegate for both of those text fields (either in IB or programmatically). I also generally marry the above with the "auto-enable return key" option for the text fields, too.
You don't. Instead, try something like this
var weightIsFilled = false
var calorieNumberIsFilled = false
#IBAction func yourWeightEditingDidBegin(sender: AnyObject) {
if valueOfWeightTextFieldIsValid() {
self.weightIsFilled = true
}
if self.weightIsFilled && self.calorieNumberIsFilled {
self.enableButton()
}
}
#IBAction func calorieNumberEditingDidBegin(sender: AnyObject) {
if valueOfCalorieNumberTextFieldIsValid() {
self.calorieNumberIsFilled = true
}
if self.weightIsFilled && self.calorieNumberIsFilled {
self.enableButton()
}
}
You also may want to be using IBAction functions that are called when the textfield's value changes, not when editing begins on it.

Resources