I am trying to subclass GKGraphNode2D to also include different penalties for different terrains (in the costToNode method). When I create a new GKGraph with an array of my new subclass and call findPathFromNode on the GKGraph, it ignores my costToNode method entirely.
The code looks like:
public class PenaltyGraphNode: GKGraphNode2D {
public let penalty: Float
init(position: CGPoint, penalty: Float) {
self.penalty = penalty
let point = vector_float2(Float(position.x), Float(position.y))
super.init(point: point)
}
override public func costToNode(node: GKGraphNode) -> Float {
guard let penaltyNode = node as? PenaltyGraphNode else { return super.costToNode(node) }
return super.costToNode(node) * (1 + penaltyNode.penalty)
}
}
And where I am trying to use the GKGraph (node creation removed for brevity sake):
let penaltyNodes: [PenaltyGraphNode] = [penaltyNode1, penaltyNode2, ...]
let graph = GKGraph(nodes: penaltyNodes)
let path = graph.findPathFromNode(startNode, toNode: endNode) as? [PenaltyGraphNode]
The costToNode in my subclass is never getting called, I made sure that all of the nodes that I create in the graph and add to it later are all PenaltyGraphNodes, but it still won't call costToNode. When I print the graph's nodes property, it shows them as GKGraphNode2D objects only, not my subclass.
Am I missing something obvious here?
Related
Is it possible to create a keypath referencing a method? all examples are paths to variables.
I'm trying this:
class MyClass {
init() {
let myKeypath = \MyClass.handleMainAction
...
}
func handleMainAction() {...}
}
but it does not compile saying Key path cannot refer to instance method 'handleMainAction()
KeyPaths are for properties. However, you can do effectively the same thing. Because functions are first class types in swift, you can create a reference to handleMainAction and pass it around:
//: Playground - noun: a place where people can play
import UIKit
import XCTest
import PlaygroundSupport
class MyClass {
var bar = 0
private func handleMainAction() -> Int {
bar = bar + 1
return bar
}
func getMyMainAction() -> ()->Int {
return self.handleMainAction
}
}
class AnotherClass {
func runSomeoneElsesBarFunc(passedFunction:() -> Int) {
let result = passedFunction()
print("What I got was \(result)")
}
}
let myInst = MyClass()
let anotherInst = AnotherClass()
let barFunc = myInst.getMyMainAction()
anotherInst.runSomeoneElsesBarFunc(passedFunction: barFunc)
anotherInst.runSomeoneElsesBarFunc(passedFunction: barFunc)
anotherInst.runSomeoneElsesBarFunc(passedFunction: barFunc)
This will work fine, and you can pass "barFunc" to any other class or method and it can be used.
You can use MyClass.handleMainAction as an indirect reference. It gives you a block that take the class instance as the input parameter, and returns corresponding instance method.
let ref = MyClass.handleMainAction //a block that returns the instance method
let myInstance = MyClass()
let instanceMethod = ref(myInstance)
instanceMethod() //invoke the instance method
The point is you can pass around / store the method reference just like what you did with a key path. You just need to supply the actual instance when you need to invoke the method.
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.
I would like to write some highly optimized generic functions in Swift 3 that work on any floating point type, so I came out with something like this, which works:
#inline(__always) public func radsToDegs<T: BinaryFloatingPoint>(_ angle: T) -> T {
let radsToDegsConstant: T = 180/T(M_PI)
return angle*radsToDegsConstant
}
let a: CGFloat = CGFloat(M_PI)
let b = 3.14
let c: Float = 3.14
let d: Double = 3.14
radsToDegs(a) // -> 180.0
radsToDegs(b) // -> 179.9087476710785
radsToDegs(c) // -> 179.9087
radsToDegs(d) // -> 179.9087476710785
But I would like to have the radsToDegsConstant precomputed, and the following code fails to compile for a good reason: it's due to the fact that radsToDegs is generic:
// error: type 'OptimizedConstants' cannot be nested in generic function 'radsToDegs'
#inline(__always) public func radsToDegs<T: BinaryFloatingPoint>(_ angle: T) -> T {
struct OptimizedConstants {
static let radsToDegsConstant: T = 180/T(M_PI)
}
return angle*OptimizedConstants.radsToDegsConstant
}
So my only remaining solution is to not go for a generic function, but instead extend just the type that I know I'm most interested in, and declare the computed constant outside the function:
private let radsToDegsConstant = 180/CGFloat(M_PI)
public extension CGFloat {
#inline(__always) public func radsToDegs() -> CGFloat {
return self*radsToDegsConstant
}
}
let x: CGFloat = CGFloat(M_PI)
x.radsToDegs() // 180.0
But I still wonder if the Swift compiler would be smart enough to avoid computing radsToDegsConstant each time the function it's called, on the former generic implementation.
So that's my question: is that optimized? Or is there a trick, or a compiler directive that I may use to still get both the benefits from the generic function and the precomputed value that the extension form delivers?
I came to the conclusion that precomputing these kind of constants can not be done for generic functions in Swift 3. Here is the path that I have followed: I started by trying the suggestions from #Leo Dabus in the previous comments (see this question: How can I convert from degrees to radians?), and came up with the following code, that fails at runtime:
private let radsToDegsConstant = 180.0 / .pi
public extension FloatingPoint {
var degsToRads: Self { return self * (radsToDegsConstant as! Self) }
#inline(__always) public func radsToDegs() -> Self {
return self*(radsToDegsConstant as! Self)
}
}
Double.pi.degsToRads
Double.pi.radsToDegs()
Float(M_PI).radsToDegs() // Error: Could not cast value of type 'Swift.Double'to 'Swift.Float'
Float.pi.radsToDegs() // Error: Could not cast value of type 'Swift.Double'to 'Swift.Float'
CGFloat.pi.radsToDegs() // Error: Could not cast value of type 'Swift.Double'to 'CoreGraphics.CGFloat'
The problem here comes from the fact that the constant radsToDegsConstant is assumed to be a Double by the compiler, and then the forced cast as! fails at runtime. Note also that I tried both the function and the property (var) syntax, just for science.
Of course, stored properties can not be added to extensions, so the following code is also invalid:
public extension FloatingPoint {
let radsToDegsConstant: Self = Self(180.0 / .pi) // Error: extensions may not contain stored properties
var degsToRads: Self { return self * (radsToDegsConstant as! Self) }
#inline(__always) public func radsToDegs() -> Self {
return self*(radsToDegsConstant as! Self)
}
}
Note: Swapping the let by a var changes nothing, as it should be.
So finally I decided to get the property syntax from the mentioned link, but focused on CGFloat only, since I just need that one working as fast as possible. So here is the optimized extension:
import CoreGraphics
public let CG_PI = CGFloat(M_PI)
public let CG_PI_2 = CGFloat(M_PI_2)
public let CG_PI_4 = CGFloat(M_PI_4)
public let CG_2PI = CGFloat(2*M_PI)
private let degsToRadsConstant = CG_PI/180 // Optimization: precomputed value.
private let radsToDegsConstant = 180.0/CG_PI // Optimization: precomputed value.
public extension CGFloat {
public var radsToDegs: CGFloat {
#inline(__always) get { return self*radsToDegsConstant }
}
public var degsToRads: CGFloat {
#inline(__always) get { return self*degsToRadsConstant }
}
}
A few more notes:
final methods are not supported in extensions (that could help a bit).
.pi in Swift 3: No, it's not a mistake to not have used that one, since in the library I'm building I use a lot things like M_PI_2 and the other π constants with CGFloat. So casting them to CGFloat in compile time, and using them with a similar naming convention to the one provided by the M_XX constants is perfectly okay on this specific case. And the problem with .pi is that, as far as I know, Swift libraries do not provide these other (absolutely needed for me) values. So for consistency, I just keep using this, and it works fast and with the proper types. I just don't buy all the style stuff from the Swift guys.
Finally, if someone still answers this with a trick I'm not seeing (that allows to precompute the constants and still use generic functions, as I did ask), that will be great, and accepting the new solution as the correct answer will be a pleasure...
I have a superclass where a vector is being created as a member variable.
class GameScene: SKScene, SKPhysicsContactDelegate {
var floatingBlockPositions: [CGPoint] = [CGPointMake(130.0, 70.0)]
}
I am overriding it in a subclass like so:
class StreetFight: GameScene {
override var floatingBlockPositions: [CGPoint] {
get {
return [CGPointMake(400.0, 70.0), CGPointMake(250.0, 95.0)]
}
set (newValue) {
newValue
}
}
}
I have a class method that mutates it to set another parameter.
temp = 0
for _ in floatingBlockPositions {
floatingBlockPositions[temp].y = floatingBlockPositions[temp].y + positionsForFirstBlock.y + (groundBlockSize.height / 2)
let aerialBlock = SKSpriteNode(imageNamed: "aerialBlock")
aerialBlock.position = floatingBlockPositions[temp]
temp++
}
After debugging, I see that the setter in the subclass is being called, but it is not being set properly because the equals operator in the function is trying to set only one part of one of the CGPoints in the vector, which the setter is not set up to handle, no pun intended. How can I write the setter in the subclass so it recognizes what part of floatingBlockPositions needs to be set and sets it. If possible, should I overload the method, or is there a better way to do it?
You've changed floatingBlockPositions from a stored property to a computed variable (because it has a get). Therefore, you cannot store a new value for it. However, you can create a separate private var and set that, and then in get, if that is set, return that new value.
I have a Realm Object which has several relationships, anyone has a good code snippet that generalizes a copy method, to create a duplicate in the database.
In my case i just wanted to create an object and not persist it. so segiddins's solution didn't work for me.
Swift 3
To create a clone of user object in swift just use
let newUser = User(value: oldUser);
The new user object is not persisted.
You can use the following to create a shallow copy of your object, as long as it does not have a primary key:
realm.create(ObjectType.self, withValue: existingObject)
As of now, Dec 2020, there is no proper solution for this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your Realm Model Object classes conform to codable
class Dog: Object, Codable{
#objc dynamic var breed:String = "JustAnyDog"
}
Create this helper class
class RealmHelper {
//Used to expose generic
static func DetachedCopy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need detached / true deep copy of your Realm Object, like this:
//Suppose your Realm managed object: let dog:Dog = RealmDBService.shared.getFirstDog()
guard let detachedDog = RealmHelper.DetachedCopy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
detachedDog.breed = "rottweiler"
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our realm object. Just make sure all your Realm Model Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.
I had a similar issue and found a simple workaround to get a copy of a realm object. Basically you just need to make the object conform to the NSCopying protocol, something like:
import RealmSwift
import Realm
import ObjectMapper
class Original: Object, NSCopying{
dynamic var originalId = 0
dynamic var firstName = ""
dynamic var lastName = ""
override static func primaryKey() -> String? {
return "originalId"
}
init(originalId: Int, firstName: String, lastName: String){
super.init()
self.originalId = originalId
self.firstName = firstName
self.lastName = lastName
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = Original(originalId: originalId, firstName: firstName, lastName: lastName)
return copy
}
}
then you just call the "copy()" method on the object:
class ViewController: UIViewController {
var original = Original()
override func viewDidLoad() {
super.viewDidLoad()
var myCopy = original.copy()
}
}
The nice thing about having a copy is that I can modify it without having to be in a realm write transaction. Useful when users are editing some data but didn't hit save yet or simply changed their mind.
Since this problem is still alive I post my solution which works but still needs to be improved.
I've created an extension of Object class that has this method duplicate that takes an object objOut and fills the flat properties by looking at self. When a non-flat property is found (aka a nested object) that one is skipped.
// Duplicate object with its flat properties
func duplicate(objOut: Object) -> Object {
// Mirror object type
let objectType: Mirror = Mirror(reflecting: self);
// Iterate on object properties
for child in objectType.children {
// Get label
let label = child.label!
// Handler for flat properties, skip complex objects
switch String(describing: type(of: child.value)) {
case "Double", "Int", "Int64", "String":
objOut.setValue(self.value(forKey: label)!, forKey: label)
break
default:
break
}
}
return objOut
}
Inside the Manager class for my Realms I have the method copyFromRealm() that I use to create my copies of objects.
To give you a practical example this is the structure of my Appointment class:
Appointment object
- flat properties
- one UpdateInfo object
- flat properties
- one AddressLocation object
- flat properties
- one Address object
- flat properties
- one Coordinates object
- flat properies
- a list of ExtraInfo
- each ExtraInfo object
- flat properties
This is how I've implemented the copyFromRealm() method:
// Creates copy out of realm
func copyFromRealm() -> Appointment {
// Duplicate base object properties
let cpAppointment = self.duplicate(objOut: Appointment()) as! Appointment
// Duplicate UIU object
cpAppointment.uiu = self.uiu?.duplicate(objOut: UpdateInfo()) as? UpdateInfo
// Duplicate AddressLocation object
let cpAddress = self.addressLocation?.address?.duplicate(objOut: Address()) as? Address
let cpCoordinates = self.addressLocation?.coordinates?.duplicate(objOut: Coordinates()) as? Coordinates
cpAppointment.addressLocation = self.addressLocation?.duplicate(objOut: AddressLocation()) as? AddressLocation
cpAppointment.addressLocation?.address = cpAddress
cpAppointment.addressLocation?.coordinates = cpCoordinates
// Duplicate each ExtraInfo
for other in self.others {
cpAppointment.others.append(other.duplicate(objOut: ExtraInfo()) as! ExtraInfo)
}
return cpAppointment
}
I wasn't able to find out a good and reasonable way to work with nested objects inside my duplicate() method. I thought of recursion but code complexity raised too much.
This is not optimal but works, if I'll find a way to manage also nested object I'll update this answer.
Swift 5+
Creates a Realm managed copy of an existing Realm managed object with ID
extension RLMObject {
func createManagedCopy(withID newID: String) -> RLMObject? {
let realmClass = type(of: self)
guard let realm = self.realm, let primaryKey = realmClass.primaryKey() else {
return nil
}
let shallowCopy = realmClass.init(value: self)
shallowCopy.setValue(newID, forKey: primaryKey)
do {
realm.beginWriteTransaction()
realm.add(shallowCopy)
try realm.commitWriteTransaction()
} catch {
return nil
}
return shallowCopy
}
}