swift: display mkdirection instructions in real time - swift

Using mapKit, I would like to guide the user on the road by displaying the instruction for the current step. Hence, once the step is completed, the instruction is updated for the corresponding (next) step.
From what I know, mapKit doesn't allow you to do so straight. I have found this post but it is in Obj-C and I am using swift. So far my code looks like this:
for step in self.carRoute!.steps {
var laStep: MKRouteStep = step as! MKRouteStep
var mapPointForEndOfStep: MKMapPoint = laStep.polyline(laStep.polyline.pointCount - 1) as! MKMapPoint
}
But I get this error: '(_) -> _' is not identical to 'MKPolyline'
Anyone knows how to set the last point of each step? Or is there some kind of magic that would tell what step the user is on. Or if he is out of the directions indicated by the app?

I found the solution myself. So if anyone ever encounter the same problem, I translated the Obj-c code found here in swift :
let thisStep: MKRouteStep = (self.carRoute?.steps[self.stepsCounter])!
let pointsArray = thisStep.polyline.points()
let lastPoint = pointsArray[thisStep.polyline.pointCount - 1]
let theLoc: CLLocationCoordinate2D = MKCoordinateForMapPoint(lastPoint)
self.theRegion = CLCircularRegion(center: theLoc, radius: 50, identifier: "turningPoint")

Related

No exact matches in call to initializer with UnsafeMutablePointer

I'm trying to make an unsafeMutablePointer with a context, and I'm getting the error "No exact matches in call to initializer". Not sure what is going on here. Working off this answer.
let context = UIGraphicsGetCurrentContext()
var pixels:Any?
if let data = context?.data
{
pixels = UnsafeMutablePointer<CUnsignedChar>(data) // No exact matches in call to initializer
}
This answer was written 5 years ago, which is too old in the Swift world.
I could not find a right answer searching with No exact matches in call to initializer with UnsafeMutablePointer, but there would be many articles about pointer handling when Swift changed the way how to convert the pointee type of pointers.
Please try this one:
let context = UIGraphicsGetCurrentContext()
var pixels: UnsafeMutablePointer<CUnsignedChar>? = nil
if let data = context?.data {
pixels = data.assumingMemoryBound(to: CUnsignedChar.self) //<-
}
(You should better not use Any as far as you can.)

Rules for type check in Swift builds?

I’ve wanted to speed up my build times, so one of the steps was using Other Swift Flags and
-Xfrontend -warn-long-function-bodies=100
-Xfrontend -warn-long-expression-type-checking=100
But I’m not really sure how type checks work. For example, here’s a simple func for creating random CGFloat. Type check for it is over 200ms
static func randomColorValue() -> CGFloat {
return CGFloat(Int.random(in: 0...255))/255.0
}
But on changing to something like this
static func randomColorValue() -> CGFloat {
let rnd = Int.random(in: 0...255)
let frnd = CGFloat(rnd)
let result = frnd/255.0
return result
}
or like this
static func randomColorValue() -> CGFloat {
let rnd : Int = Int.random(in: 0...255)
let frnd : CGFloat = CGFloat(rnd)
let result : CGFloat = frnd/255.0
return result
}
type check is still over 200ms.
What's wrong here? Is there any set of rules and best practices for dealing with build times?
My Mac is a bit older (2012), maybe that's the problem?
EDIT:
After turning off -warn-long-function-bodies problematic line appeared, and that is
CGFloat(rnd)
It appears that casting Int to Float, Double or CGFloat shows slowing down of 150ms.
Note that warn-long-function-bodies is unsupported (it was added as an experimental flag). If you remove it, I find that the expression time is often reported as twice as fast, which makes be believe that using the two measurements together is causing interference. Measurement takes time, too. warn-long-expression is a supported option.

Is there any way to know how Swift calculates hashValue?

I recently found that the code below ends up with hash collision.
FYI, I'm using XCode 9.4.1 (9F2000), which uses Swift 4.1.2
import Foundation
let lhs = "あいうえおあいう21あいうえ"
let rhs = "あいうえおあいう22あいうえ"
let percentEncodedLhs = lhs.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let percentEncodedRhs = rhs.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let lhsHashValue = lhs.hashValue
let rhsHashValue = rhs.hashValue
let lhsPercentHashValue = percentEncodedLhs.hashValue
let rhsPercentHashValue = percentEncodedRhs.hashValue
print(lhsHashValue == rhsHashValue)
print(lhsPercentHashValue == rhsPercentHashValue)
/*
Output:
false
true
*/
I know that hash collision can happen in some circumstance, but I couldn't find how Swift calculates hashValue for String.
For example, Java calculates hashCode of String like:
https://docs.oracle.com/javase/6/docs/api/java/lang/String.html#hashCode()
Is there any official explanation or maybe some assumptions?
but I couldn't find how Swift calculates hashValue for String
Why couldn’t you? Swift is open source. If you are interested, read the source.
https://github.com/apple/swift/blob/111499d2bfc58dc12fcb9cd1ce1dda7978c995b7/stdlib/public/core/StringHashable.swift

getBoundingBoxSize crashing sometimes

I have a strange problem with using getBoundingBoxSize on SCNText geometry - it sometimes causes a crash - EXC_BAD_ACCESS (code=1). Can't figure out why. I use it on main thread.
This happens on iOS 12. Can someone help me resolve this?
let node = node as! AugmentedRealityView3DObjectNode
let mediaNode = mediaNode as! SCNNode
let fontScalling: Float = 0.5
let marginScalling: Float = 0.9
let planeGeometry = mediaNode.geometry as! SCNPlane
let textNode = mediaNode.childNodes.first!
let mediaTextGeometry = textNode.geometry as! SCNText
mediaTextGeometry.containerFrame = CGRect(withSize: CGSize(fromSize3D: node.augmentedRealityView.sizeForMainNode(node: node)) / CGFloat(fontScalling * marginScalling), centeredInContainerOfSize: .zero)
let centerPoint = SCNVector3(getBoundingCenterPoint: mediaTextGeometry.boundingBox)
textNode.position = SCNVector3(-centerPoint.x, -centerPoint.y, -centerPoint.z) * fontScalling * marginScalling
textNode.scale = SCNVector3(qubicVector: fontScalling * marginScalling)
// TODO: This causes crashes sometines in iOS 12.
let boundingBoxSize = SCNVector3(getBoundingBoxSize: mediaTextGeometry.boundingBox) * fontScalling / marginScalling
planeGeometry.width = CGFloat(boundingBoxSize.x)
planeGeometry.height = CGFloat(boundingBoxSize.y)
This is still broken 2.5 years later in SceneKit in iOS 14.2. The best I can tell, you cannot invoke .boundingBox on a node with SCNText geometry until after the node has been rendered at least once. My guess is that something is not initialized until the render loop and boundingBox fails to check for it being uninitialized.
My workaround is the usual hack of putting the .boundingBox in a DispatchQueue.main.asyc {} block so (hopefully) the node & geometry have been initialized. Depending on your app, this may not be feasible.
You are crashing in the Objective-C memory management system, which usually indicates some kind of heap corruption. I would guess that some object has been prematurely deallocated, while references to it still exist. This is super common, and is often called a dangling pointer.
I'd suggest checking out the Zombies tool in Instruments. It can help find this kinds of errors. Good luck!

"Can't unwrap Optional.None" and I can't figure out why

This produces "fatal error: Can't unwrap Optional.None" and I don't seem to get why
var motionManager = CMMotionManager()
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdates()
var accelerationData = motionManager.accelerometerData
var accel = accelerationData.acceleration.x
If anyone can help me out, that would be great.
The issue is accelerationData is nil and you aren't checking for this. From the docs:
If no accelerometer data is available, the value of this property is nil.
You should check to make sure there is actually data before calling methods on it like this
if let accelerationData = motionManager.accelerometerData {
var accel = accelerationData.acceleration.x
}
That will ensure that if there is no data your app won't crash. Now to make sure you get some data.
You aren't getting any data because you're asking for data immediately after you initialize the core motion manager. You can show this by waiting a few seconds before checking. You can add NSThread.sleepForTimeInterval(3) right above the if let and run the project and it will enter the if let. Make sure you are using an actual device though, the simulator won't generate any motion data.