How can Xcode successfully access data the program cannot? - swift

I'm running Xcode 9, Swift 4, High Sierra. In my code I have a mapping that, cut out and stripped down, includes this as the first lines in the closure:
let foo = graph.edges.map { (edge: Edge<Node>) -> (Int, Int) in
print(edge)
let fromvertex = (edge.from as? VertexElement)
.
.
.
}
There's a breakpoint on the line let from vertex ...
When run, the code is successful up to the breakpoint, which is where the problem starts. If at that point I execute po edge.from in the debugger, I get the expected result. If I step one line, and so execute the assignment, I get Thread 1: EXC_BAD_ACCESS (code=2, address=*whatever*). I can then execute po edge.from or even po (edge.from as? VertexElement) at that point and still get the expected results.
How can this be? There's no optional involved in edge.from, and I'd think the data is there correctly since the debugger finds it.
Edge is (as you'd expect) a generic class, with the first part of the definition being this:
public class Edge<T>: NSObject, NSCoding, Codable where T: Hashable {
public typealias Weight = SimWeight
public let from: Vertex<T>
public let to: Vertex<T>
public var weight: Weight? = nil
.
.
.
}
and then, the first part of Vertex, also a generic, looks like this:
public struct Vertex<T>: Equatable, Codable where T: Hashable {
public var data: T
public let index: Int
public var gridX: Int?
public var gridY: Int?
.
.
.
}
Node is another, concrete class (not a generic)
Finally, I have address sanitization turned on.
All that said, I neither can see where the exception is coming from, nor why the debugger can get the data when the program cannot. Any ideas what's wrong (or even how to debug it?)

I can only suspect a compiler problem, because it's fixed now by reorganizing the code. Specifically, I created this method within Edge:
func indirectEdge() throws -> IndirectEdge {
guard let fromvx = self.from as? VertexElement,
let tovx = self.to as? VertexElement,
let weight = self.weight else {throw TypeError.notVertex}
return IndirectEdge(from: fromvx.index, to: tovx.index, weight: weight)
}
and then changed the code at the top of this post to be:
guard let indirectEdges = try? graph.edges.map({ (edge) throws -> IndirectEdge in
return try edge.indirectEdge()
}) else {throw SimFileError.unimplementedOperation(why: "Don't know how to encode this graph")}
.
.
.
At that point everything began to work, without the exceptions. (yes, I changed the later code to accept the IndirectEdge; that code is further on and shouldn't have any effect.) What I expected was that the exception would move into the indirectEdge method, but that's not what happened. It's good the code works now, but scary that I can't figure out why.

Related

Reference Types/Subclassing, and Changes to Swift 4 Codable & encoder/decoders

I'm struggling to understand class/reference type behavior and how this relates to changes as I try to upgrade and reduce code using Codable in Swift 4.
I have two classes – a SuperClass with all of the data that will be persistent and that I save to UserDefaults (a place name & string with coordinates), and a SubClass that contains additional, temporary info that I don't need (weather data for the SuperClass coordinates).
In Swift 3 I used to save data like this:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
// subClassArray is of type [SubClass] and contains more data per element.
superClassArray = subClassArray
let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
UserDefaults.standard.set(superClassData, forKey: " superClassData")
}
SuperClass conformed to NSObject & NSCoding
It also included the required init decoder & the encode function.
It all worked fine.
In trying to switch to Swift 4 & codable I've modified SuperClass to conform to Codable.
SuperClass now only has one basic initializer and none of the encoder/decoder stuff from Swift 3. There is no KeyedArchiving happening with this new approach (below). SubClass remains unchanged. Unfortunately I crash on the line where I try? encoder.encode [giving a Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)]. My assumption is that the encoder is getting confused with identical reference types where one is SuperClass and one SubClass (subClassArray[0] === superClassArray[0] is true).
I thought this might work:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
superClassArray = subClassArray
// assumption was that the subclass would only contain parts of the superclass & wouldn't produce an error when being encoded
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(superClassArray){
UserDefaults.standard.set(encoded, forKey: " superClassArray ")
} else {
print("Save didn't work!")
}
}
Then, instead of creating an empty superClassArray, then using:
superClassArray = subClassArray, as shown above, I replace this with the single line:
let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name: $0.name, coordinates: $0.coordinates)}
This works. Again, assumption is because I'm passing in the values inside of the class reference type & haven't made the superClassArray = subClassArray. Also, as expected, subClassArray[0] === superClassArray[0] is false
So why did the "old stuff" in Swift 3 work, even though I used the line superClassArray = subClassArray before the let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
? Am I essentially achieving the same result by creating the array in Swift 4 that was happening with the old Swift 3 encoder/decoder? Is the looping / recreation
Thanks!
Polymorphic persistence appears to be broken by design.
The bug report SR-5331 quotes the response they got on their Radar.
Unlike the existing NSCoding API (NSKeyedArchiver), the new Swift 4 Codable implementations do not write out type information about encoded types into generated archives, for both flexibility and security. As such, at decode time, the API can only use the concrete type your provide in order to decode the values (in your case, the superclass type).
This is by design — if you need the dynamism required to do this, we recommend that you adopt NSSecureCoding and use NSKeyedArchiver/NSKeyedUnarchiver
I am unimpressed, having thought from all the glowing articles that Codable was the answer to some of my prayers. A parallel set of Codable structs that act as object factories is one workaround I'm considering, to preserve type information.
Update I have written a sample using a single struct that manages recreating polymorphic classes. Available on GitHub.
I was not able to get it to work easily with subclassing. However, classes that conform to a base protocol can apply Codable for default encoding. The repo contains both keyed and unkeyed approaches. The simpler is unkeyed, copied below
// Demo of a polymorphic hierarchy of different classes implementing a protocol
// and still being Codable
// This variant uses unkeyed containers so less data is pushed into the encoded form.
import Foundation
protocol BaseBeast {
func move() -> String
func type() -> Int
var name: String { get }
}
class DumbBeast : BaseBeast, Codable {
static let polyType = 0
func type() -> Int { return DumbBeast.polyType }
var name:String
init(name:String) { self.name = name }
func move() -> String { return "\(name) Sits there looking stupid" }
}
class Flyer : BaseBeast, Codable {
static let polyType = 1
func type() -> Int { return Flyer.polyType }
var name:String
let maxAltitude:Int
init(name:String, maxAltitude:Int) {
self.maxAltitude = maxAltitude
self.name = name
}
func move() -> String { return "\(name) Flies up to \(maxAltitude)"}
}
class Walker : BaseBeast, Codable {
static let polyType = 2
func type() -> Int { return Walker.polyType }
var name:String
let numLegs: Int
let hasTail: Bool
init(name:String, legs:Int=4, hasTail:Bool=true) {
self.numLegs = legs
self.hasTail = hasTail
self.name = name
}
func move() -> String {
if numLegs == 0 {
return "\(name) Wriggles on its belly"
}
let maybeWaggle = hasTail ? "wagging its tail" : ""
return "\(name) Runs on \(numLegs) legs \(maybeWaggle)"
}
}
// Uses an explicit index we decode first, to select factory function used to decode polymorphic type
// This is in contrast to the current "traditional" method where decoding is attempted and fails for each type
// This pattern of "leading type code" can be used in more general encoding situations, not just with Codable
//: **WARNING** there is one vulnerable practice here - we rely on the BaseBeast types having a typeCode which
//: is a valid index into the arrays `encoders` and `factories`
struct CodableRef : Codable {
let refTo:BaseBeast //In C++ would use an operator to transparently cast CodableRef to BaseBeast
typealias EncContainer = UnkeyedEncodingContainer
typealias DecContainer = UnkeyedDecodingContainer
typealias BeastEnc = (inout EncContainer, BaseBeast) throws -> ()
typealias BeastDec = (inout DecContainer) throws -> BaseBeast
static var encoders:[BeastEnc] = [
{(e, b) in try e.encode(b as! DumbBeast)},
{(e, b) in try e.encode(b as! Flyer)},
{(e, b) in try e.encode(b as! Walker)}
]
static var factories:[BeastDec] = [
{(d) in try d.decode(DumbBeast.self)},
{(d) in try d.decode(Flyer.self)},
{(d) in try d.decode(Walker.self)}
]
init(refTo:BaseBeast) {
self.refTo = refTo
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let typeCode = try container.decode(Int.self)
self.refTo = try CodableRef.factories[typeCode](&container)
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let typeCode = self.refTo.type()
try container.encode(typeCode)
try CodableRef.encoders[typeCode](&container, refTo)
}
}
struct Zoo : Codable {
var creatures = [CodableRef]()
init(creatures:[BaseBeast]) {
self.creatures = creatures.map {CodableRef(refTo:$0)}
}
func dump() {
creatures.forEach { print($0.refTo.move()) }
}
}
//: ---- Demo of encoding and decoding working ----
let startZoo = Zoo(creatures: [
DumbBeast(name:"Rock"),
Flyer(name:"Kookaburra", maxAltitude:5000),
Walker(name:"Snake", legs:0),
Walker(name:"Doggie", legs:4),
Walker(name:"Geek", legs:2, hasTail:false)
])
startZoo.dump()
print("---------\ntesting JSON\n")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let encData = try encoder.encode(startZoo)
print(String(data:encData, encoding:.utf8)!)
let decodedZoo = try JSONDecoder().decode(Zoo.self, from: encData)
print ("\n------------\nAfter decoding")
decodedZoo.dump()
Update 2020-04 experience
This approach continues to be more flexible and superior to using Codable, at the cost of a bit more programmer time. It is used very heavily in the Touchgram app which provides rich, interactive documents inside iMessage.
There, I need to encode multiple polymorphic hierarchies, including different Sensors and Actions. By storing signatures of decoders, it not only provides with subclassing but also allows me to keep older decoders in the code base so old messages are still compatible.

Is it possible to get the KVC-String from Swift 4 KeyPath?

For a project I am currently working on, it would be very useful to get the KVC-String from a KeyPath instance my method is receiving. Short example:
struct Person {
var name: String
}
let propertyCache = ["name": "something"]
func method<T>(_ keypath: KeyPath<Person, T>) -> T? {
let kvcName = keypath.kvc
return propertyCache[kvcName]
}
This might seem not very useful, but in my project it is :) I found a property on KeyPath called _kvcKeyPathString which is also public, but it returns nil every time I tried.
Or is their maybe a possibility to use reflection there? Thanks in advance for ideas/solutions!
I don't know of a pure Swift way to get the name of the property as a string yet.
But, if you add the #objc attribute to the property then _kvcKeyPathString will actually have a value instead of always being nil. Also, since Swift structs can't be represented in Objective-C, this method only works for classes.
A minimal working example usage:
class SomeClass {
#objc var someProperty = 5
}
let keyPath = \SomeClass.someProperty
print(keyPath._kvcKeyPathString)

The following code works when I manually pass the value but fails when I unwrap the value

I just started learning Swift as my first coding language. My current challenge is trying to automate the transitions from a current level setup as LevelOne.sks to another level also created in Xcode level editor as LevelTwo.sks. What I'm attempting to do is trigger a transition to the next level with the following set of code.
In my base scene I have this function to send the player to the next level
private func goToNextLevel(nextLevel: String) {
//When hard coding the arguments as...
//loadScene(withIdentifier: .levelTwo)
//The level two scene loads and is playable...however,
//When trying to change the level argument in code
// by passing a nextLevel variable
// the optional unwraps nil and crashes the app.
loadScene(withIdentifier: SceneIdentifier(rawValue: nextLevel)!)
}
This is then passed to a SceneLoadManager File
enum SceneIdentifier: String {
case levelOne = "LevelOne"
case levelTwo = "LevelTwo"
// case levelThree = "LevelThree"
}
private let sceneSize = CGSize(width: 768, height: 1024)
protocol SceneManager { }
extension SceneManager where Self: SKScene {
func loadScene(withIdentifier identifier: SceneIdentifier) {
let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
let nextLevel = SKScene(fileNamed: identifier.rawValue)
nextLevel?.scaleMode = .aspectFill
self.view?.presentScene(nextLevel!, transition: reveal)
}
I think this has something to do with how I'm trying to set nextLevel. Currently I'm setting this up as follows
let nxtLvl = String?
nxtLvl = ".levelOne"
goToNextLevel(nextLevel: nxtLvl)
Hopefully you can make sense of what I'm trying to achieve and that I'm at least close to being on the right track here. Any help would be greatly appreciated. Thanks!
What could really help you to solve :
SceneIdentifier.levelOne.rawValue returns this -> "LevelOne"
BUT
SceneIdentifier(rawValue : "LevelOne") returns -> SceneIdentifier.levelOne
See the difference ?
In the first you get the string , in that case what you want
And in the second you get the "left member" (I don't know the therm)
An example to see clearer if you have an enum :
enum SceneIdentifier: String {
case case1 = "LevelOne"
case case2 = "LevelTwo"
}
SceneIdentifier(rawValue : "LevelOne") !
returns this : SceneIdentifier.case1
and SceneIdentifier.case1.rawValue
returns this : "levelOne"
Since you are initializing with a raw value, you need to actually use the raw value.
let nxtLvl = String?
nxtLvl = "LevelOne" //this is the rawValue of the .levelOne case
goToNextLevel(nextLevel: nxtLvl)
Alternatively, you could change your API so that goToNextLevel simply accepts a SceneIdentifier like this.
//tip: use the parameter name as part of the method name
private func goTo(nextLevel: SceneIdentifier) {
//Then calling the function
goTo(nextLevel: .levelOne)
Though, that might not be appropriate in the larger scope of your API.

Xcode6.3.2 Swift bug with static constants

I am trying to figure out why I am having constant compile problems with this type of construct in Xcode 6.3.2.
class Foo {
static let CONSTANT_NAME = "CONSTANT_STRING"
...
func bar () -> String {
var s = String(format:"%s,%d\n", CONSTANT_NAME, 7)
return s
}
...
}
As I understand the language, this should be perfectly legal code however Xcode is constantly (hah-pun) having issues with it raising the error
"there is no member CONSTANT_NAME in class Foo"
If I get lucky and force it to clean, and then rebuild it will some times sort itself out and work. Other times, even doing that, then trying an open/close project will still not resolve the issue.
So, I guess my implicit follow up question (if the answer to the above is - it is legal code) is: is the Xcode Swift compiler that buggy that even basic things like this are likely to cause problems? If so, swift seems to be in a pretty bad state.
static is class property, that means you have to call it like this ClassName.property
class Foo {
static let CONSTANT_NAME = "CONSTANT_STRING"
func bar () -> String {
var s = String(format:"%s,%d\n", Foo.CONSTANT_NAME, 7)
return s
}
}
That is not a bug. That is what it should be. A class property "belongs" to the class.
If you want your code work without using ClassName, do not use static
class Foo {
let CONSTANT_NAME = "CONSTANT_STRING"
func bar () -> String {
var s = String(format:"%s,%d\n",CONSTANT_NAME, 7)
return s
}
}
More details in the Apple Documentation
The static let syntax is legal and valid. The issue is that you must fully qualify that variable when you access it:
var s = String(format:"%s,%d\n", Foo.CONSTANT_NAME, 7)
The compiler error is a bit obtuse, but it is telling the truth... CONSTANT_NAME is not a member, but a type property of class Foo: Swift Type Properties
I hear you about saving key strokes. I've personally been trying to make my Swift code as idiomatic as possible by milking every short cuts but when you find code like this, you should be glad that the compiler asks you to keep on the safe side:
class Foo {
static let CONSTANT = "hello"
func bar() -> String {
let CONSTANT = "bye"
return CONSTANT // I know which one! Thanks Swift!
}
}
println(Foo.CONSTANT)
println(Foo().bar())

Generic Type in Swift as Return Value

I would like to implement some code so that I can call something like:
NSUserDefaults("key1", "value1")
let s = NSUserDefaults("key1") // "value1" expected
NSUserDefaults("key2", 2.01)
let s = NSUserDefaults("key2") // 2.01 expected
I have some code in concept as below, but obviously it's not going to work. So my question is, instead of writing a series of functions like class func bool(key: String, _ v: Bool? = nil) -> Bool? is there any way to take the advantage of generic please?
extension NSUserDefaults {
class func object<T: AnyObject>(key: String, _ v: T? = nil) -> T? {
if let obj: T = v {
NSUserDefaults.standardUserDefaults().setObject(obj, forKey: key)
NSUserDefaults.standardUserDefaults().synchronize()
} else {
return NSUserDefaults.standardUserDefaults().objectForKey(key) as T?
}
return v
}
}
Your syntax is going to wind up being very poor. This line can't work as written:
let s = NSUserDefaults("key1") // "value1" expected
Swift has to pick a type for s at compile time, not run time. So the only type it can assign here is Any (not even AnyObject is expansive enough if you want to return Double since Double is not AnyObject).
That means you have to explicitly call out let s : Any = ... (because Swift wisely won't let you create Any implicitly), and then you're going to wind up with an Any that you have to type-check somehow. When you're done, you're going to come full circle to objectForKey().
Even if you could get this syntax working, you shouldn't try to overload a single function syntax to do opposite things. That's very confusing. If you were going to build an extension like this, you should probably make it a subscript. That way you'd say defaults["key1"] and defaults["key2"] = 2.01. That's something may be able to build (though there will still be type annotation headaches required to deal with AnyObject?).