How do I work with pointers to ios types? I want t...
# ios
m
How do I work with pointers to ios types? I want to access CGPath, but Android Studio only seems to present me with CPointer<CGPath> and doesn't help me much past there, and I haven't found anything on the web to show me how to work with that in Kotlin. I figured "apply" would go in the right direction and then "it" would be a CGPath object. Is that right? If so, what then?
s
If you have a function that is taking a pointer to a CGPATH and setting it to a value, you can access the value with ptr.pointed. See the pointers section of https://kotlinlang.org/docs/native-c-interop.html. If you just need to make one, use a function like CGPathCreateMutable. Note, you’re in charge of releasing it when you’re done with it.
🙏 1
m
I'm creating and manipulating them in Kotlin. I found CGPathCreateMutable, it returns me a CGMutablePathRef that I then want to create paths in, and add paths to, and that's where I need (want?) the functions for CGPath. Android Studio doesn't offer me that on the end of MyPathRef.pointed.ptr. I have been adding paths with CGPathAddxyz, but it doesn't seem to be producing non-zero paths so I thought I'd try using the functions (does Apple talk about them as methods?) of the (CGPath) class, which is why I thought I want the pointer... As for clearing them, I'm putting them into Kotlin classes. Will they get released when the classes go away or do I have to do that?
s
A CGPath is a C struct and has no methods. When you’re browsing Apple’s docs make sure that you’re looking at the Objective-C versions and not the Swift versions. There’s usually a toggle. Swift does some interfacing magic to make them feel more like Swift style structs with methods but that isn’t available in Kotlin. You do have to release them yourself since you’re dealing with lower level Core Graphics apis. It’s been a while since I dealt with them in ObjC but the typical set of steps was, create the path, add it to the CGContext and then release it. You could keep it around as a property if you wanted and be sure to release it in a destructor. Kotlin doesn’t have them so it makes keeping the pointer around more difficult.
🙏 1
m
D*mn! 😞 Yes, I was looking at the Swift version. It's not (very) obvious. That makes it look (to me) just too hard to be worth doing in Kotlin with Objective-C. Doing it in Swift will be almost identical to doing it in Kotlin for Android. The two "Path" classes are almost identical. But that means keeping two separate (quite large) chunks of code with just this "minor" difference. How long until we get access to Swift from Kotlin? 🤨
s
Yeah, honestly I’d say it is just easier/less friction to have your core code in Kotlin and use Swift for the UI part. At my company it is Swift & UIKit -> Kotlin framework with the beginnings of Swift & SwiftUI -> Kotlin framework. Some of the Kotlin code has expect/actual classes that deal with ObjC apis and it is okay but not that great. We have some code that accesses the key chain and that is truly awful to look at. TBF it wouldn’t pretty in ObjC or Swift either.
m
Most of the path preparation is just arithmetic, so I was hoping to do that in common code and then just use them in the UI. I'll give it a bit more of a try, it'd be so much better if it worked, before giving up. 🌻 Thanks for your help @Sam
s
Worst case, you could define a set of operations (moveTo, lineTo, arc, etc) that get calculated in common code and then interpreted and transformed into native paths by Swift & Kotlin. You could also define a Path interface that had those types of methods and implement it in Swift/iOS and Kotlin/Android with each calling into the native Path api. Then it is just a matter of passing that into the code that creates the paths.
m
Yeah, that (latter) is what I'm trying to do... hmm, no I'm not. I'm trying to expect/actuall(ise) a path class. I could keep my code in commonMain and implement a Path class in Swift? How would that work?
s
You would need to convert your class into an interface.
Copy code
expect class CommonPath { 
    // drawing methods
}
interface class CommonPath {
    // drawing methods
}
class CommonPoint(x: double, y: double)
In Swift you would implement it.
Copy code
class SwiftPath extends CommonPath {
    private let path = UIBezierPath()
    // drawing methods
   override func lineTo(point: CommonPoint) {
       path.addLineTo(point.toCGPoint())
   }
}

extension CommonPoint {
    func toCGPoint() -> CGPoint {
        return CGPoint(x: CGFloat(self.x), CGFloat(self.y)
    }
}
The problem you’ll have is there’s no way to instantiate the classes implementing the interface from within common code since the implementations are outside of the common code. You’ll need to provide the path to your class that generates the points or some type of factory that can be invoked that creates them.
Copy code
class ShapeMaker(private val pathCreator: () -> CommonPath){
    fun makePath() {
        val path = pathCreator()
        ...
    }
    // Or
    fun makePath(path: CommonPath) { ... }
}
In the swift calling code:
Copy code
let shapeMaker = ShapeMaker() { SwiftPath() }
// Or
shapeMaker.makePath(path: SwiftPath())
m
Thanks @Sam 👍. I'll have a fiddle around with that and see where I can get to with it.
👍 1
Ok, after a lot of lying down in a darkened room thinking, I think I can see a way forward with this. A question though @Sam, is there any reason not to use Swift's Path structure rather than UI or CG versions?
s
When coding in Swift, you will use either UIBezierPath or CGPath. It just depends on what you’re trying to accomplish and what api’s you’re working with. UIBezierPath inherits from NSObject and is a higher level api. Most of the lower level Core Graphics apis want a CGPath. UIBezierPath does have a
cgPath
property on it that you can use where necessary. Most of the custom drawing I’ve done, I start with a UIBezierPath and just use the cgPath property when calling lower level apis.
FYI, the
SwiftPath
class name I used above was just an example name. You would call it whatever made sense to you.
m
Hmm, am I looking at unhelpful docs again? https://swiftontap.com/path
s
That’s for SwiftUI. If you’re doing the UI in UIKit, you want https://developer.apple.com/documentation/uikit/uibezierpath.
m
Starting from (really!) scratch, SwiftUI looks like the way to go... no?
s
I’ve just been getting into SwiftUI in the last couple months. It’s definitely the future but it is a whole other animal similar to the Jetpack Compose vs the old XML style. If you’re targeting iOS 14 or 15, you should be able to go straight SwiftUI.
m
I'm still getting used to the iOS world. That's not too limiting a choice, is it?
s
SwiftUI is a weird beast IMO. I think they started out thinking that they could just wrap a bunch of UIKit views and call it good but they’re finding out some of those views don’t behave well or do what they need so they’re slowly replacing them with ones better suited to SwiftUI. Unfortunately they never backport any of that stuff so you’re stuck with a lot of hacks found on Stackoverflow or just wrapping widgets in
if #available(iOS 15.0, *) {
blocks. It’s not the best.
😞 1