Swift: Change object with property in array - swift

I am trying to amend an object in my AnyObject (savedProgram). I want to save Taking the advice from another thread, I've used 'as!' to convert it to [AnyObject]:
#IBAction func saveM(sender: UIButton) {
brain.variableValues["M"] = displayValue
savedProgram = brain.program as! [AnyObject]
var indexValue = savedProgram?.indexOfObject("M")
savedProgram?[indexValue!] = displayValue
It complains at the 4th row. I've also tried changing the original variable savedProgram to [AnyObject] but then I am not sure how to look up "M" and amend it.
Thanks for your help.

Related

Why cant I unwrap a UserDefault type string? [Swift 3.0 - Xcode 8]

I've stored data sent back by delegate as a user default and I'm trying to prepare it to send in a segue. I am able to send it in a segue, the problem I'm having is the data comes out in the form "optional[Data]" I know what optional means, but it doesn't let me unwrap UserDefault like an optional, even though it says it is optional?
Here's the code:
func DataToPass(ArrayName: [String]) {
print("Check ArrayContent--->",ArrayName)
var DCollect = [String]()
var CCollect = [String]()
DCollect.append(ArrayName[0])
CCollect.append(ArrayName[1])
let defaults = UserDefaults.standard
if let unwrap = defaults{
unwrap.set(DCollect, forKey: "DCollect")
unwrap.set(CCollect, forKey: "CCollect")
}
}
The error is:
"Initializer for conditional binding must have Optional type, not
'UserDefaults'" on line if let unwrap = defaults{
Why can't I unwrap it like this?
Another Attempt:
func DataToPass(ArrayName: [String]) {
print("Check",ArrayName)
UserDefaults.standard.set(ArrayName, forKey: "ToCollect")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "SegueInfo"{
let firstController = segue.destination as! ViewController
if let Tosend = UserDefaults.standard.value(forKey:
"ToCollect") as? String {
Collect.append(Tosend)
}
firstController.AllData = Collect
}
Nothing is appended this way, Tosend is empty?
To save something to user defaults:
UserDefaults.standard.set("It is a string", forKey: "meaningfulIdentifier")
To get something out from user defaults:
if let anyString = UserDefaults.standard.value(forKey: "meaningfulIdentifier") as? String {
// Do something with anyString
}
You only need optional binding when you are dealing with getting something out from user defaults. For the data part you need this optional binding, not the User Defaults itself.
Why does it tell me that I can't unwrap UserDefaults?
The error message is trying to explain to you that UserDefaults.standard is not of an Optional type. That means that it is not of type Optional<UserDefaults>, which means it can't use the if-let statement to unwrap it.
What you want to unwrap is not the UserDefaults object which is already provided as a singleton through the UserDefaults.standard property.
What can I unwrap?
What you want to unwrap are the variables you are trying to retrieve.
Those would be the optional types you'll have to deal with. It is Optional because it may already exist in the UserDefaults or it may not yet exist.
How do I retrieve a string array that I stored into UserDefaults?
Using the key DCollect, use the appropriate method (i.e. stringArray(forKey:) to retrieve it.
let defaults = UserDefaults.standard
if let dcollect = defaults.stringArray(forKey: "DCollect") {
// it already exists; now use the dcollect variable
} else {
// it does not exist; do something (i.e. add dcollect value?) if it does not exist yet
}
How do I store a string array into UserDefaults?
let defaults = UserDefaults.standard
let value : [String] = ["My", "Very", "Eager", "Pluto"]
standard.set(value, forKey: "DCollect")
I am not sure why you are trying to unwrap UserDefaults.standard because you don't need to. It is not an optional, therefore you get error from Xcode.
You can straight add the values to the de UserDefaults like this:
let u = UserDefaults.standard
u.set(["a", "b", "c"], forKey: "yourKey")
First, don't forget to import Foundation
Then try this code
func DataToPass(ArrayName: [String]) {
UserDefaults.standard.set(ArrayName, forKey: "ToCollect")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "SegueInfo"{
let firstController = segue.destination as! ViewController
if let Tosend = UserDefaults.standard.stringArray(forKey: "ToCollect") as? [String] {
Collect += Tosend
}
firstController.AllData = Collect
}
}

Swift - Using value from UISlider IBAction in another function

This is a super basic question that is troubling me.
I have a UIslider IBAction that is generating a Double (var = rounded). I want to use this double in viewDidLoad. but getting the error "Use of unresolved identifier 'rounded'"
#IBAction func sliderValueChanged(sender: UISlider) {
var currentValue = Double(sender.value)
var rounded = Double(round(100*currentValue)/100)
label.text = "\(rounded)"
}
override func viewDidLoad() {
super.viewDidLoad()
let fileURL = NSBundle.mainBundle().URLForResource("puppy", withExtension: "jpg")
let beginImage = CIImage(contentsOfURL: fileURL)
let filter = CIFilter(name: "CISepiaTone")
filter.setValue(beginImage, forKey: kCIInputImageKey)
filter.setValue(rounded, forKey: kCIInputIntensityKey)
let newImage = UIImage(CIImage: filter.outputImage)
self.imageView.image = newImage
}
filter.setValue(rounded, forKey: kCIInputIntensityKey) is where i am getting the error. I want to use the 'rounded' variable from the slider function here.
Any help in using one variable from one function in another function would be very much appreciated. I have ran into this a couple times without success. So once you all help me out here, it should fix my other issues as well.
Thanks
Declare an instance variable at the beginning of the class – outside any method – with the default value of the UISlider
var rounded : Double = <defaultValueOfTheSlider>
delete the keyword var before rounded in sliderValueChanged()
A few concepts that will help you out.
The issue you are having is that you are creating an instance inside a function and it is lost when the function returns. The reason for this is that the functions data is created on the stack, the stack is temporary memory your app's process uses.
To access the variable throughout the class you can declare it at the top of your class. This will then be allocated to the heap so that it's available for later access until removed from the heap(deallocated).

Type casting operator

In Swift guide that as published on ibooks, as! operator was not mentioned. But in online reference and in some example code, they (i mean Apple in both cases) used as! operator.
Is there a difference between as and as! operators? If there are, can you explain please?
edit: Im so tired that i wrongly typed "is", instead of "as". That is now corrected...
as? will do an optional downcast - meaning if it fails it will return nil
so "Blah" as? Int will return Int? and will be a nil value if it fails or an Int if it does not.
as! forces the downcast attempt and will throw an exception if the cast fails. Generally you will want to favour the as? downcast
//ex optional as?
let nine = "9"
if let attemptedNumber = nine as? Int {
println("It converted to an Int")
}
//ex as!
let notNumber = "foo"
let badAttempt = notNumber as! Int // crash!
( You may find that you that an update is sitting there for the swift guide. It is mentioned for sure in the online version https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html )
operator is the forcefully unwrapped optional form of the as? operator. As with any force unwrapping though, these risk runtime errors that will crash your app should the unwrapping not succeed.
Further, We should use as to upcast if you wish to not write the type on the left side, but it is probably best practice to write it with normal typing as shown above for upcasting.
Example:
You use the as keyword to cast data types. UIWindow rootViewController is of type UIViewController. You downcast it to UISplitViewController.
Another better example can be taken as follows.
var shouldBeButton: UIView = UIButton()
var myButton: UIButton = shouldBeButton as UIButton
The as? operator returns an optional, and then we use optional binding to assign it to a temporary constant, and then use that in the if condition, like we are doing in the below example.
let myControlArray = [UILabel(), UIButton(), UIDatePicker()]
for item in myControlArray
{
if let myLabel = item as? UILabel
{
var storeText = myLabel.text
}
else if let someDatePicker = item as? UIDatePicker
{
var storeDate = someDatePicker.date
}
}

What's the difference between "as?", "as!", and "as"?

Before I upgraded to Swift 1.2, I could write the following line:
if let width = imageDetails["width"] as Int?
Now it forces me to write this line:
if let width = imageDetails["width"] as! Int?
My question is, if I'm forced to write it as above, couldn't I just write the code below and it would do the same thing? Would it give me the same result in all values of imageDetails?
if let width = imageDetails["width"] as Int
The as keyword used to do both upcasts and downcasts:
// Before Swift 1.2
var aView: UIView = someView()
var object = aView as NSObject // upcast
var specificView = aView as UITableView // downcast
The upcast, going from a derived class to a base class, can be checked at compile time and will never fail.
However, downcasts can fail since you can’t always be sure about the specific class. If you have a UIView, it’s possible it’s a UITableView or maybe a UIButton. If your downcast goes to the correct type – great! But if you happen to specify the wrong type, you’ll get a runtime error and the app will crash.
In Swift 1.2, downcasts must be either optional with as? or “forced failable” with as!. If you’re sure about the type, then you can force the cast with as! similar to how you would use an implicitly-unwrapped optional:
// After Swift 1.2
var aView: UIView = someView()
var tableView = aView as! UITableView
The exclamation point makes it absolutely clear that you know what you’re doing and that there’s a chance things will go terribly wrong if you’ve accidentally mixed up your types!
As always, as? with optional binding is the safest way to go:
// This isn't new to Swift 1.2, but is still the safest way
var aView: UIView = someView()
if let tableView = aView as? UITableView {
// do something with tableView
}
Got this from a site: SOURCE
as
In Swift 1.2 and later, as can only be used for upcasting (or disambiguation) and pattern matching:
// 'as' for disambiguation
let width = 42 as CGFloat
let block = { x in x+1 } as Double -> Double
let something = 3 as Any? // optional wrapper can also be added with 'as'
// 'as' for pattern matching
switch item {
case let obj as MyObject:
// this code will be executed if item is of type MyObject
case let other as SomethingElse:
// this code will be executed if item is of type SomethingElse
...
}
as?
The conditional cast operator as? tries to perform a conversion, but returns nil if it can't. Thus its result is optional.
let button = someView as? UIButton // button's type is 'UIButton?'
if let label = (superview as? MyView)?.titleLabel {
// ...
}
as!
The as! operator is for forced type conversion.
Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.
// 'as!' for forced conversion.
// NOT RECOMMENDED.
let buttons = subviews as! [UIButton] // will crash if not all subviews are UIButton
let label = subviews.first as! UILabel
The correct idiom that should do exactly what you want (in all versions of Swift at least upto and including 1.2) is the as? optional cast.
if let width = imageDetails["width"] as? Int
The optional cast returns an optional (Int? in this case) and is tested at runtime. Your original code probably forced a cast to the optional type.

Error using subscript of optional array

When using this code:
let result: AnyObject! = hitResults[0]
I am getting the following error:
[AnyObject]? does not have a member named subscript
Containing function for context:
func handleTap(gestureRecognize: UIGestureRecognizer) {
// retrieve the SCNView
let scnView = self.view as SCNView
// check what nodes are tapped
let p = gestureRecognize.locationInView(scnView)
let hitResults = scnView.hitTest(p, options: nil)
// check that we clicked on at least one object
if hitResults?.count > 0 {
// retrieved the first clicked object
let result: AnyObject! = hitResults[0]
// get its material
let material = result.node!.geometry?.firstMaterial
// highlight it
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
// on completion - unhighlight
SCNTransaction.setCompletionBlock {
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
material?.emission.contents = UIColor.blackColor()
SCNTransaction.commit()
}
material?.emission.contents = UIColor.redColor()
SCNTransaction.commit()
}
}
Does anyone know what the issue here is?
This happens because hitTest returns an optional array, so you need to unwrap before using it.
Instead of checking that the hitResults has a count > 0, you could check that there the first object exists, and then proceed to using that object
if let firstHit = scnView.hitTest(p, options: nil)?.first {
// safely use firstHit here...
}
You can't use a subscript on an optional array. [AnyObject]? Is an optional array of type AnyObject. If you are sure that hitResults is non-nil, you can unwrap it with ! then use a subscript.
let result: AnyObject! = hitResults![0]
Since hitResults is an [AnyObject]?, you can't call subscript on it without unwrapping it first. The safest way to do this is using optional binding, like so:
// check that we clicked on at least one object
if hitResults?.count > 0 {
// retrieved the first clicked object
if let result: AnyObject = hitResults?[0] {
/* safely use result here */
}
}
Or, even better, you can use optional binding with the first property of Array which returns the first element in the array if the Array is not empty, or nil:
// check that we clicked on at least one object and retrieve it
if let result = hitResults?.first {
/* safely use result here */
}