receiving right type in function - swift

I try to build some NestedView, which loads view inside view inside... depends of objects tree. So I made a short test in Playground:
class First {}
class Second:First {}
class Dodd {
func takeIt<Foo, Bar>(content:[Foo], type: Bar.Type) {
print (Foo.self, Bar.self)
print (content as? [Bar] ?? [])
}
}
let arrayOfFirst:[First] = [First()]
let arrayOfSecond:[Second] = [Second(), Second(), Second()]
let dodd = Dodd()
let firstType = First.self
let secondType = Second.self
dodd.takeIt(content: arrayOfSecond, type: firstType)
dodd.takeIt(content: arrayOfFirst, type: secondType)
It produces nice and predictable output:
Second First
[__lldb_expr_77.Second, __lldb_expr_77.Second, __lldb_expr_77.Second]
First Second
[]
Great.
But if I try tu use exactly same mechanism in a bit more complicated environment results are less satisfying.
This is a function of some ViewController, whatever:
func addSubviews<HeadType>(for content:[HeadType]) {
Swift.print("ADD SUBVIEW FOR \(HeadType.self)")
func takeNext <ParentType, ChildType>(
parentArray: [ParentType],
pathToChild: AnyKeyPath,
type: ChildType.Type?) -> [ChildType]
{
Swift.print ("\nInside takeNext Child Type <\(ChildType.self)>\n")
let result:[ChildType] = parentArray.flatMap { ( parentElement ) -> ChildType? in
parentElement [keyPath:pathToChild] as? ChildType}
return result
}
let interfaceHead = InterfaceElements(type: HeadType.self)
Swift.print ("\tParentArrayContent: \(HeadType.self) ")
for (label, path) in interfaceHead.paths {
if let firstObject = content.first, let objectAtKeyPath = firstObject[ keyPath:path ] {
Swift.print ("\t\tpath: \(path)")
Swift.print ("\t\ttype: \(InterfaceElements(element: objectAtKeyPath).type.self)")
Swift.print("\t\tLabel", label)
let childType = InterfaceElements(element: objectAtKeyPath).type
let elements = takeNext(
parentArray: content,
pathToChild: path,
type: childType)
let controller = NestedStackViewController(for: elements)
}
}
}
Output:
ADD SUBVIEW FOR Joint
ParentArrayContent: Joint
path: Swift.ReferenceWritableKeyPath<HyperGlyph.Joint, Swift.ImplicitlyUnwrappedOptional<HyperGlyph.Position>>
type: Optional(HyperGlyph.Position)
Label position
Inside takeNext Child Type <NSManagedObject>
ADD SUBVIEW FOR NSManagedObject
ParentArrayContent: NSManagedObject
inited NestedStack for NSManagedObject
inited NestedStack for Joint
First loop is nice, Joint is Joint, Position type was found, sent to takeNext, but inside takeNext function Position became NSManagedObject type. Where could be trick?

Only one way to do it was to explicitly pass a type.
let elements = takeNext(
parentArray: content,
pathToChild: path,
type: SomeType.self) // << Here.
It made a program a bit more complicated, more switches, but it works in nice strong-type way now.

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.

Swift - Changing constant doesn't show error

I'm reading a tutorial from a book. I can't attach the book here. In a chapter an UIImage constant is declared and its value is assigned in next lines. It is not a var neither optional. It runs successfully. How does it work?
extension ViewController: MKMapViewDelegate {
private func addAnnotations() {
for business in businesses {
guard let yelpCoordinate = business.location.coordinate else {
continue
}
let coordinate = CLLocationCoordinate2D(latitude: yelpCoordinate.latitude,
longitude: yelpCoordinate.longitude)
let name = business.name
let rating = business.rating
let image:UIImage //Constant non-optional
switch rating {
case 0.0..<3.5:
image = UIImage(named: "bad")!
case 3.5..<4.0:
image = UIImage(named: "meh")!
case 4.0..<4.75:
image = UIImage(named: "good")!
case 4.75..<5.0:
image = UIImage(named: "great")!
default:
image = UIImage(named: "bad")!
}
let annotation = BusinessMapViewModel(coordinate: coordinate,
name: name,
rating: rating, image: image)
mapView.addAnnotation(annotation)
}
}
}
First, you should know that it is perfectly fine in Swift to declare a variable and assign it a value on the next line, as long as you don't refer to that variable before it is assigned.
let a: Int
... // you can't use "a" here
a = 10 // OK!
Look at the switch statement after the variable declaration. A switch statement must be exhaustive, meaning that at least one case of it will be run. In this switch statement, every case has a single statement that assigns to image, and there are no fallthroughs. From these observations, both us and the compiler can conclude that image will be assigned (and assigned only once) after the switch statement, hence you can use it in the line:
let annotation = BusinessMapViewModel(coordinate: coordinate,
name: name,
rating: rating, image: image)
From the Swift 5.1 reference, here
When a constant declaration occurs in the context of a function or method, it can be initialized later, as long as it is guaranteed to have a value set before the first time its value is read.

Compiler choosing the wrong initializer

I have an EXC_BAD_ACCESS(code=2 ...) error that keeps popping up that I need some help with. I believe I've managed to pin down the source of the malformed pointer, but I'm at a loss as to how to fix it.
Apparently the swift compiler is choosing the wrong initializer for one of my classes. According to Instruments, sometimes when the class Description is initialized, it calls the initializer for LyricBlock. Not all the time, just sometimes. It does this regardless of whether the compiler is set to -Onone or -O whole-module-optimization.
Here's what the two classes look like:
class LyricBlock: Node, LeftDelimited, RightDelimited {
var leftDelimiter: Delimiter
var rigthDelimiter: Delimiter
init(start: Int, body: Range<Int>, end: Int) {
self.leftDelimiter = Delimiter(range: start..<body.lowerBound)
self.rightDelimiter = Delimiter(range: body.upperBound..<end)
super.init(range: body)
}
}
class Description: Node, LeftDelimited, RightDelimited {
var leftDelimiter: Delimiter
var leftDelimiter: Delimiter
init(start: Int, body: Range<Int>, end: Int) {
self.leftDelimiter = Delimiter(range: start..<body.lowerBound)
self.rightDelimiter = Delimiter(range: body.upperBound..<end)
super.init(range: body)
}
}
As you can see, LyricBlock and Description inherit from a Node base class and share a couple protocols in common, but otherwise they have nothing to do with each other.
Some possibly relevant code:
class Node {
weak var parent: Node?
var next: Node?
var firstChild: Node?
weak var lastChild: Node?
let offset: Int
internal(set) var length: Int
init(range: Range<Int>) {
self.offset = range.lowerBound
self.length = range.upperBound - range.lowerBound
}
func addChild(_ child: Node) {
if firstChild == nil {
firstChild = child
} else {
lastChild?.next = child
}
lastChild = child
child.parent = self
}
}
class Parser {
// ...
func processLine(in buffer: Buffer) {
// Parse the current line as a block node.
var block = blockForLine(in: buffer)
// Try to find an appropriate container node. If none can be found, block will be replaced with Description.
let container = appropriateContainer(for: &block, in: buffer)
container.addChild(block)
// Edge case to parse first-line lyrics
if let cueBlock = block as? CueBlock {
if let lyricBlock = scanForLyric(in: buffer, at: cueBlock.direction.range.lowerBound) {
let lyricContainer = LyricContainer(range: lyricBlock.range.lowerBound..<endOfLineCharNumber)
lyricContainer.addChild(lyricBlock)
cueBlock.replaceDirection(with: lyricContainer)
parseInlines(for: lyricBlock, in: buffer)
}
}
// Parse inlines as appropriate
switch block {
case is FacsimileBlock, is Description, is LyricBlock:
parseInlines(for: block, in: buffer)
// ...
}
}
func blockForLine(in buffer: Buffer) -> Node {
let whitespace = buffer.scanForFirstNonspace(at: charNumber, limit: endOfLineCharNumber)
// ...
let endWhitespace = buffer.scanBackwardForFirstNonspace(at: endOfLineCharNumber, limit: wc)
let description = Description(start: charNumber, body: whitespace..< endWhitespace, end: endOfLineCharNumber)
return description
}
func appropriateContainer(for block: inout Node, in buffer: Buffer) -> Node {
switch block {
// These block types can only ever be level-1
case is Header, is Description, is EndBlock, is HorizontalBreak:
return root
// ...
case is LyricBlock:
guard let cueContainer = root.lastChild as? CueContainer else { break }
guard let cueBlock = cueContainer.lastChild as? CueBlock else { break }
guard let direction = cueBlock.direction as? LyricContainer else { break }
direction.extendLengthToInclude(node: block)
cueBlock.extendLengthToInclude(node: direction)
cueContainer.extendLengthToInclude(node: cueBlock)
return direction
default:
break
}
let whitespace = buffer.scanForFirstNonspace(at: charNumber, limit: endOfLineCharNumber)
let endWhitespace = buffer.scanBackwardForFirstNonspace(at: endOfLineCharNumber, limit: wc)
// Invalid syntax, time to fail gracefully
block = Description(start: charNumber, body: whitespace..< endWhitespace, end: endOfLineCharNumber)
return root
}
func parseInlines(for stream: Node, in buffer: Buffer) {
// ... scans buffer for inlines and enques them in queue
while let next = queue.dequeue() {
let nextRange = next.rangeIncludingMarkers
if nextRange.lowerBound > j {
let lit = Literal(range: j..<nextRange.lowerBound)
stream.addChild(lit)
}
stream.addChild(next)
j = nextRange.upperBound
}
if j < endOfLineCharNumber {
let lit = Literal(range: j..<nodeRange.upperBound)
stream.addChild(lit)
}
}
// ...
}
As a side note, I wondered if I might be running into a mangling issue with the class signatures and tried making rightDelimiter and leftDelimiter properties of Node instead of using protocols. This resulted in the compiler calling my Identifier initializer instead. I don't know what that proves. Frankly I'm at a loss. Help?
After some more tinkering I was able to at least partially solve this problem.
It turns out that, for whatever reason, ARC craps out on linked lists above a certain size. I was able to confirm this by creating a generic linked list in a new project and slowly stress testing it with progressively longer lists.
class LLNode<T> {
var value: T?
var next: LLNode<T>?
weak var previous: LLNode<T>?
}
class LList<T> {
var head: LLNode<T>
var tail: LLNode<T>
// boring old linked list implementation
}
let list = List<Int>()
for i in 0..<10000 {
list.append(i)
}
Sure enough, after about 10,000 nodes I started getting EXC_BAD_ACCESS again on the generic list, but only when list was deinitialized. This matched the behavior I was getting exactly with the parser above. Since my code uses linked lists to model children in a tree (two dimensions of reference counting!), ARC was having to resolve all those references on its own -- and failing. Why it crashes like that is still beyond my ability to explain, but that at least explains the source of the crash.
To confirm, I created another linked list in C and just made a Swift wrapper around it, with all of the garbage collection being implemented in C.
import liblist
class List<T> {
var list: UnsafeMutablePointer<llist>
// Swift interface with list
deinit {
list_free(list)
}
}
The wrapper was able to handle every size I threw at it -- as much as 500,000 nodes, at which point I felt satisfied and stopped testing.
If anyone is interested in the full code I used to get around this problem, I've created a Swift package at https://github.com/dmcarth/List

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.