Swift chaining optionals - swift

I'm running Yosemite public beta & Xcode6 Beta 4
Based on the OSX SceneKit template, I'm trying to determine what node was clicked on. Here is the mouseDown function, mostly from the template code.
The comment labeled #1 works, which if great, but I'm trying to understand why the code comments #2, #3, and #4 don't compile, or what the errors are really telling me.
Searching the errors I'm not finding results that appear to apply to my case.
the error for #2 seems to usually apply to type casting, and I wouldn't think there is any type casting going on here.
the error for #3 has me totally lost.
and the error for #4 seems like SCNNode doesn't have a name property, but it certainly does.
override func mouseDown(theEvent: NSEvent) {
/* Called when a mouse click occurs */
// check what nodes are clicked
let p = gameView.convertPoint(theEvent.locationInWindow, fromView: nil)
let hitResults = gameView.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]
// #1 This works
if let myNode: SCNNode = result.node? {
if myNode.name? == "Die" {
println("Node is named Die")
}
}
// #2 This does not work
// error: Could not find an overload for the 'node' that accepts the supplied arguments
if let myNode = result.node? {
if myNode.name? == "Die" {
println("Node is named Die")
}
}
// #3 This does not work either
// error: Type 'String?' does not confrom to protocol '_RawOptionSet'
if result.node?.name? == "Die" {
println("Node is named Die")
}
// #4 This does not work either
// error: 'SCNNode!' does not have a member named 'name'
if let myName = result.node?.name? {
if myName == "Die" {
println("Node is named Die")
}
}
// 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 = NSColor.blackColor()
SCNTransaction.commit()
}
material.emission.contents = NSColor.redColor()
SCNTransaction.commit()
}
super.mouseDown(theEvent)
}

The failures of #2, #3, and #4 are all because of the lack of type. You say:
the error for #2 seems to usually apply to type casting, and I wouldn't think there is any type casting going on here.
However, you declared result to be AnyObject while you're attempting to access it as an SCNNode, so there's certainly some type cast that needs to happen.
I've seen this before as well working with dictionaries. Not only did I get explicit about the type, I also tested the type beforehand:
var item: AnyObject? = nil
item = map["SWLFlexFormat"]
if let value: AnyObject = item {
configuration.formatter = getConfiguredFlexFormatter(configuration, item: value);
}
func getConfiguredFlexFormatter(configuration: LoggerConfiguration, item: AnyObject) -> LogFormatter? {
if let formatString: String = item as? String {
var formatter = FlexFormatter.logFormatterForString(formatString);
return formatter
}
return nil
}

Related

Forwarding function generic parameter to generic class type

I have created enum with associated value and I want to be able to dynamically update associated value. As far as I know Swift doesn't support that at the moment.
Because of that I used following approach:
enum PersonInfo {
class EnumValue<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
// Instead of using String or Bool or any other type directly, use EnumValue wrapper
case firstName(EnumValue<String>)
case lastName(EnumValue<String>)
case isAdult(EnumValue<Bool>)
}
I want to add function that would update EnumValue.value property in following way:
func updateAssociatedValue<V>(_ updateValue: V) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<V> else {
continue
}
value.value = updateValue
}
}
Problem is that this guard statement always fails (guard let value = associatedValue.value as? EnumValue<V>) and I can't figure it out why.
On the other hand, when I write updateAssociatedValue with typed type then things work properly:
// This works
func updateAssociatedValue(_ updateValue: String) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<String> else {
continue
}
value.value = updateValue
}
}
Things compile normally but during the runtime guard statement always fails. Am I using generic value in some incorrect way? Should I use somehow updateValue.Type or updateValue.self (I tried but it didn't work).
Example of usage:
var array: [PersonInfo] = [
.firstName(PersonInfo.EnumValue("John")),
.lastName(PersonInfo.EnumValue("Doe")),
.isAdult(PersonInfo.EnumValue(false))
]
print(array)
// John, Doe, false
array.first?.updateAssociatedValue("Mike")
print(array)
// Mike, Doe, false
I can always reassign enum value in array but if possible I want to avoid that. That's the reason for asking this question.

Value of optional type must be unwrapped to refer to member, BUT its for an implicitly unwrapped optional property

Why am I getting this? Its an implicitly unwrapped optional property. I don't think I should be getting this error. I tried cleaning my build folder and deleting derived data, and closing Xcode. Nothing worked.
var questionsWrap: QuestionsWrapper
func userMadeSelection(
_ responseId: Int,
_ myTheir: Question.Response.Selections.MyTheir,
_ choice: Question.Response.Selections.MyTheir.Choice
) {
guard let id = question.id else { return }
questionsWrap.saveSelection(
questionId: id,
responseID: responseId,
myTheir: myTheir,
choice: choice
) { success in
if !success {
AlertViewController<Any>.alertFailure(
message: "We made a mistake and we weren't able to save your selection."
)
}
}
singleQuestionDataSource.observableQuestion.value = question
print(#line, "userMadeSelectionImportance: \(responseId) \(myTheir) \(choice)")
if choice.changedBetweenPolarAndNeutral(
with: questionsWrap.question.theirInteractionStyle
) {
let presenter = Question.Importance.Presenter(question)
update(presenter?.importance)
questionCell?.importancePresenter = presenter
}
}
Method definition
func saveSelection(
questionId: Int,
responseID: Int,
myTheir: Question.Response.Selections.MyTheir,
choice: Question.Response.Selections.MyTheir.Choice,
context: Context,
successAction: SuccessAction? = nil
) {
guard let questionIndex = questions.index(of: questionId),
let responseIndex = questions[questionIndex].responses.index(of: responseID) else {
successAction?(false)
return
}
questions[questionIndex].responses[responseIndex].set(myTheir, choice, for: context)
let response = questions[questionIndex].responses[responseIndex]
URL.make(
my: response.choice(for: .my, UserDefaults.questionContext),
their: response.choice(for: .their, UserDefaults.questionContext),
forResponseID: responseID,
forQuestionID: questionId,
forContext: UserDefaults.questionContext
).get { jsonDict in
successAction?(jsonDict.success)
}
}
Unfortunately, I don't know what code to provide to recreate this issue atomically.
I am stepping outside of the norm and posting a screen shot to prove that the error is showing.
The usual reason for seeing this error message with an implicitly unwrapped Optional is that implicit unwrapping does not promulgate itself thru assignment. For example, this is legal:
var s : String!
print(s.count)
But this is not:
let s2 = s
print(s2.count) // value must be unwrapped
I figured it out, I changed the method signature for saveSelection to require another parameter, but I forgot to add a default value or add the argument in the call. I don't know why the compiler wasn't telling me that instead...

Function throws AND returns optional.. possible to conditionally unwrap in one line?

I am using an SQLite library in which queries return optional values as well as can throw errors. I would like to conditionally unwrap the value, or receive nil if it returns an error. I'm not totally sure how to word this, this code will explain, this is what it looks like:
func getSomething() throws -> Value? {
//example function from library, returns optional or throws errors
}
func myFunctionToGetSpecificDate() -> Date? {
if let specificValue = db!.getSomething() {
let returnedValue = specificValue!
// it says I need to force unwrap specificValue,
// shouldn't it be unwrapped already?
let specificDate = Date.init(timeIntervalSinceReferenceDate: TimeInterval(returnedValue))
return time
} else {
return nil
}
}
Is there a way to avoid having to force unwrap there? Prior to updating to Swift3, I wasn't forced to force unwrap here.
The following is the actual code. Just trying to get the latest timestamp from all entries:
func getLastDateWithData() -> Date? {
if let max = try? db!.scalar(eventTable.select(timestamp.max)){
let time = Date.init(timeIntervalSinceReferenceDate: TimeInterval(max!))
// will max ever be nil here? I don't want to force unwrap!
return time
} else {
return nil
}
}
Update: As of Swift 5, try? applied to an optional expression does not add another level of optionality, so that a “simple” optional binding is sufficient. It succeeds if the function did not throw an error and did not return nil. val is then bound to the unwrapped result:
if let val = try? getSomething() {
// ...
}
(Previous answer for Swift ≤ 4:) If a function throws and returns an optional
func getSomething() throws -> Value? { ... }
then try? getSomething() returns a "double optional" of the
type Value?? and you have to unwrap twice:
if let optval = try? getSomething(), let val = optval {
}
Here the first binding let optval = ... succeeds if the function did
not throw, and the second binding let val = optval succeeds
if the return value is not nil.
This can be shortened with case let pattern matching to
if case let val?? = try? getSomething() {
}
where val?? is a shortcut for .some(.some(val)).
I like Martin's answer but wanted to show another option:
if let value = (try? getSomething()) ?? nil {
}
This has the advantage of working outside of if, guard, or switch statements. The type specifier Any? isn't necessary but just included to show that it returns an optional:
let value: Any? = (try? getSomething()) ?? nil

Objects in Swift: Value of 'Object' has no member

Here's my doozy.
I've got this lovely little function in a file called functions.swift
//functions.swift
func latestActiveGoal() -> Object {
let realm = try! Realm()
let currentGoal = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn").last
return currentGoal!
}
which returns a Goal object. (A Goal might be wanting to lose weight, or stop being so inept at Swift).
In a different view controller, I want to access this object. Here's what I'm trying:
//viewController.swift
#IBOutlet weak var aimText: UILabel!
let funky = functions()
func getGoals(){
var currentGoal = funky.latestActiveGoal()
print(currentGoal)
aimText.text = currentGoal.Title
}
The print(CurrentGoal) output shows this:
Goal {
id = 276;
Title = Goal Title;
Aim = Aim;
Action = Nothing;
Active = 1;
CreatedOn = 2016-02-12 00:14:45 +0000;
}
aimText.text = currentGoal.Title and aimText = currentGoal.Title both throw the error:
Value of 'Object' has no member 'Title'
By printing the contents of the object, I can see the data, but can't figure out how. Any help greatly appreciated.
As the error message said, currentGoal is a value of Object type which doesn't have member Title.
This is because function latestActiveGoal returns Object instead of Goal. You just need to make it return Goal by change the return type:
func latestActiveGoal() -> Goal {
Just replace your functions with below code.
It will works perfect.
This fuction will check if goal available, then only it will return.
func latestActiveGoal() -> Object? {
let realm = try! Realm()
let currentGoals = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn")
if currentGoals.count > 0 {
return currentGoals.last;
}
return nil;
}
Your getGoals method will be as follow.
func getGoals(){
if let currentGoalObject = funky.latestActiveGoal() {
print(currentGoalObject)
let goal = currentGoalObject as! Goal
print(goal.Title)
aimText.text = goal.Title
}
}

Modifying struct instance using call to name in function parameter

I am attempting to use Parse to call up some variables and put them into a struct that is already initialized. The calling of the variables is happening smoothly and the data is available, but the inputing of the class into the function is not happening.
'unit' is a struct that has the name, hp, attack, etc. variables contained within it.
Is it not possible to pass along an instance of a struct and modify it's values like this? It would save me a lot of copy-pasting code to do it this way.
Thanks for your help!
func fetchStats(name: String, inout nameOfClass: unit) {
var unitStatArray = []
let query = PFQuery(className: "UnitStats")
query.whereKey("name", equalTo: name)
query.findObjectsInBackgroundWithBlock{(objects:[PFObject]?, error: NSError?)->Void in
if (error == nil && objects != nil){ unitStatArray = objects! }
nameOfClass.name = "\(unitStatArray[0].objectForKey("name")!)"
print("class name is \(nameOfClass.name)")
print("cannon name is \(cannon.name)")
nameOfClass.hitPoints = unitStatArray[0].objectForKey("hitPoints") as! Double
nameOfClass.hitPointsMax = unitStatArray[0].objectForKey("hitPointsMax") as! Double
nameOfClass.attack = unitStatArray[0].objectForKey("attack") as! Double
nameOfClass.defense = unitStatArray[0].objectForKey("defense") as! Double
nameOfClass.rangedAttack = unitStatArray[0].objectForKey("rangedAttack") as! Double
nameOfClass.rangedDefense = unitStatArray[0].objectForKey("rangedDefense") as! Double
nameOfClass.cost = unitStatArray[0].objectForKey("cost") as! Int
}
}
fetchStats("3-inch Ordnance Rifle", nameOfClass: &cannon)
This is an attempt to explain what I had in mind when writing my comment above.
Because there's an asynchronous call to findObjectsInBackgroundWithBlock, the inout won't help you here. The idea is to add a callback fetched like this:
func fetchStats(name: String, var nameOfClass: unit, fetched: unit -> ()) {
// your code as above
query.findObjectsInBackgroundWithBlock {
// your code as above plus the following statement:
fetched(nameOfClass)
}
}
This can be called with
fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
nameOfClass = newNameOfClass
}
(all of this code has not been tested)
The point is that you understand that your code is asynchronous (I know, I'm repeating myself). After you have called fetchStats you don't know when the callback (here: the assignment nameOfClass = newNameOfClass) will be executed. You cannot assume the assignment has been done after fetchStats has returned.
So whatever you need to do with the changed nameOfClass: the corresponding statements must go into the callback:
fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
// do whatever you want with the received newNameOfClass
}
Hope this helps.