I’ve been enjoying round with ChatGPT for just a few days now, that I used to be in a position to create an account. In fact I needed to have it generate some limericks, some poems and responses to emails. However probably the most burning query on my thoughts is that this: will AI take my job?
I discovered that ChatGPT has a variety of syntax and conceptual data about Swift and SwiftUI. So I wished to place it to the check to see how sensible it might be in fulfilling my requests. I used to be taking over the function of the mentor and ChatGPT was mainly a pupil that took my unique request and refined it over a number of iterations till I ended up with a phenomenal easy resolution.
For a present mission I wanted a view that shows a circle however I didn’t wish to draw it however as an alternative use a CAShapeLayer and a property that may replace the colour of the form. So I had in my thoughts already an thought how I’d do it, now the query was, how would I switch this psychological image from my thoughts to the AI’s “thoughts”.
Me: Create a UIKit view that makes use of CAShapeLayer to show a stuffed circle
ChatGPT: Right here is an instance of how you can create a UIKit view with a stuffed circle displayed utilizing a CAShapeLayer in Swift:
import UIKit
class CircleView: UIView {
override func draw(_ rect: CGRect) {
let circleLayer = CAShapeLayer()
let circlePath = UIBezierPath(arcCenter: CGPoint(x: rect.dimension.width / 2, y: rect.dimension.top / 2), radius: rect.dimension.width / 2, startAngle: 0.0, endAngle: CGFloat.pi * 2, clockwise: true)
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.crimson.cgColor
layer.addSublayer(circleLayer)
}
}
I instantly seen a number of issues right here.
At this level the consequence was in high quality lower than most responses I’d see on Stack Overflow. The sport was on!
Me: That is nonsense as a result of it might add a brand new sublayer each time that the draw operate is being referred to as!
ChatGPT: I apologize for that mistake. Right here is an up to date instance that solely provides the CAShapeLayer as soon as:
import UIKit
class CircleView: UIView {
override init(body: CGRect) {
tremendous.init(body: body)
setupCircle()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupCircle()
}
non-public func setupCircle() {
let circleLayer = CAShapeLayer()
let circlePath = UIBezierPath(arcCenter: CGPoint(x: bounds.dimension.width / 2, y: bounds.dimension.top / 2), radius: bounds.dimension.width / 2, startAngle: 0.0, endAngle: CGFloat.pi * 2, clockwise: true)
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.crimson.cgColor
layer.addSublayer(circleLayer)
}
}
It moved the creation of the form layer right into a setup operate which was referred to as from each inits. The one with body is used from code, the one with decoder is used when loading the view from a XIB or storyboard. This was an surprising side-benefit.
I seen although that this wouldn’t replace the trail when the bounds of the view would change.
Me: It additionally must replace the circle path when the bounds of the view change.
ChatGPT: Right here’s an up to date model of the code that updates the circle path when the bounds of the view change:
import UIKit
class CircleView: UIView {
non-public let circleLayer = CAShapeLayer()
override init(body: CGRect) {
tremendous.init(body: body)
setupCircle()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupCircle()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateCirclePath()
}
non-public func setupCircle() {
layer.addSublayer(circleLayer)
updateCirclePath()
}
non-public func updateCirclePath() {
let circlePath = UIBezierPath(arcCenter: CGPoint(x: bounds.dimension.width / 2, y: bounds.dimension.top / 2), radius: bounds.dimension.width / 2, startAngle: 0.0, endAngle: CGFloat.pi * 2, clockwise: true)
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.crimson.cgColor
}
}
Attention-grabbing! It additionally is aware of that layoutSubviews is a good place to do one thing when format modifications. Subsequent to the setup operate it additionally added an operate that updates the form.
These profitable refinements emboldened me. How about making the fill coloration be configurable from outdoors the view through a property? Additionally, whereas is technically appropriate to attract a circle through an arc bezier path, that solely provides me a top level view.
Me: Substitute the arc bezier path with one for an oval that additionally will get stuffed with the UIColor set within the fillColor property.
ChatGPT: Right here’s an up to date model of the code that attracts an oval form as an alternative of a circle and fills it with the colour set within the fillColor
property:
import UIKit
class OvalView: UIView {
non-public let ovalLayer = CAShapeLayer()
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func setupOval() {
layer.addSublayer(ovalLayer)
updateOvalPath()
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
ovalLayer.path = ovalPath.cgPath
ovalLayer.fillColor = UIColor.crimson.cgColor
}
}
Hm, it says that it did it, specifically including a fillColor
property, however that’s nowhere to be discovered. So I attempted to say it in a greater manner.
Me: Don’t arduous code the fillColor, however make it a property of the view that updates the overLayer when set.
ChatGPT: Right here’s an up to date model of the code the place the fill coloration just isn’t hardcoded and will be set as a property of the view:
import UIKit
class OvalView: UIView {
non-public let ovalLayer = CAShapeLayer()
var fillColor: UIColor = UIColor.crimson {
didSet {
updateOvalPath()
}
}
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func setupOval() {
layer.addSublayer(ovalLayer)
updateOvalPath()
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
ovalLayer.path = ovalPath.cgPath
ovalLayer.fillColor = fillColor.cgColor
}
}
I had mistyped “overLayer”, however ChatGPT nonetheless understood my request and executed it flawlessly. This was already fairly good, however an professional programmer wouldn’t be happy with this and hold sprucing the consequence till it shines.
Me: Separate the replace of the fill coloration from updating the form path. The trail ought to solely be up to date when the bounds change and the colour solely when the property modifications.
ChatGPT: Right here’s an up to date model of the code the place the fill coloration and the form path are separated, and the form path is simply up to date when the bounds change:
import UIKit
class OvalView: UIView {
non-public let ovalLayer = CAShapeLayer()
var fillColor: UIColor = UIColor.crimson {
didSet {
ovalLayer.fillColor = fillColor.cgColor
}
}
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func setupOval() {
layer.addSublayer(ovalLayer)
updateOvalPath()
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
ovalLayer.path = ovalPath.cgPath
}
}
Ha! I noticed one other mistake! This manner the fill coloration wouldn’t stay the default and solely be up to date if the property is modified.
Me: That is lacking the preliminary setting of the fill coloration.
ChatGPT: Right here’s an up to date model of the code that units the preliminary worth of the fillColor
property:
import UIKit
class OvalView: UIView {
non-public let ovalLayer = CAShapeLayer()
var fillColor: UIColor = UIColor.crimson {
didSet {
ovalLayer.fillColor = fillColor.cgColor
}
}
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func setupOval() {
layer.addSublayer(ovalLayer)
updateOvalPath()
ovalLayer.fillColor = fillColor.cgColor
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
ovalLayer.path = ovalPath.cgPath
}
}
That’s appropriate. Now we’re had been at a degree the place I used to be not so sure as a result of I don’t create CAShapeLayers day-after-day. We have to set the form layers body in some way, don’t we?
Me: I imagine that this code is lacking an replace for the body of the sublayer.
ChatGPT: Right here’s an up to date model of the code that units the body of the ovalLayer
sublayer:
import UIKit
class OvalView: UIView {
non-public let ovalLayer = CAShapeLayer()
var fillColor: UIColor = UIColor.crimson {
didSet {
ovalLayer.fillColor = fillColor.cgColor
}
}
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func setupOval() {
layer.addSublayer(ovalLayer)
updateOvalPath()
ovalLayer.fillColor = fillColor.cgColor
}
non-public func updateOvalPath() {
ovalLayer.body = bounds
let ovalPath = UIBezierPath(ovalIn: bounds)
ovalLayer.path = ovalPath.cgPath
}
}
Yeah that appears higher, if we set the body each time layoutSubviews is named then that ought to work. At this level I had a thought that I wished to Senior-Programmer’splain to the poor and overwhelmed AI…
Me: layoutSubviews might be the improper place to replace the layer body and oval path as a result of that belongs to the view format movement, however there’s a separate format movement for layers. put it there.
ChatGPT, unflinchingly: Right here’s an up to date model of the code the place the body of the ovalLayer
and the oval path are up to date within the layoutSublayers
operate as an alternative of layoutSubviews
:
import UIKit
class OvalView: UIView {
non-public let ovalLayer = CAShapeLayer()
var fillColor: UIColor = UIColor.crimson {
didSet {
ovalLayer.fillColor = fillColor.cgColor
}
}
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSublayers(of layer: CALayer) {
tremendous.layoutSublayers(of: layer)
updateOvalPath()
}
non-public func setupOval() {
layer.addSublayer(ovalLayer)
ovalLayer.fillColor = fillColor.cgColor
}
non-public func updateOvalPath() {
ovalLayer.body = bounds
let ovalPath = UIBezierPath(ovalIn: bounds)
ovalLayer.path = ovalPath.cgPath
}
}
Impressed, once more! It is aware of about layoutSublayers.
Then I believed some extra. I used to be frightened in regards to the circle not being correctly up to date in UIView animations. In the event you set a layer body in layoutSubviews then the contents jumps to the brand new location and dimension with out animation, as a result of UIView properties are implicitly animated, however layer properties should not.
And why do we’d like the essential CALayer in any respect, after we could make the form layer be the view’s essential layer? Ha! Now I knew how you can stump the AI!
Me: It will even be higher to make the form layer the primary layer of the view as an alternative of establishing a separate sublayer for it.
ChatGPT: Right here’s an up to date model of the code the place the CAShapeLayer
is ready as the primary layer of the OvalView
as an alternative of a separate sublayer:
import UIKit
class OvalView: UIView {
var fillColor: UIColor = UIColor.crimson {
didSet {
layer.fillColor = fillColor.cgColor
}
}
override init(body: CGRect) {
tremendous.init(body: body)
setupOval()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
setupOval()
}
override func layoutSublayers(of layer: CALayer) {
tremendous.layoutSublayers(of: layer)
updateOvalPath()
}
non-public func setupOval() {
layer.fillColor = fillColor.cgColor
layer.masksToBounds = true
}
non-public func updateOvalPath() {
layer.body = bounds
layer.cornerRadius = bounds.width / 2
}
}
Gotcha! Gone is the form layer solely to get replaced by setting the layer’s cornerRadius
to half of the width of the view. And an expertly inserted masksToBounds
makes this work.
Good attempt, however let me inform you what’s improper with this:
Me: That doesn’t work when the view just isn’t sq.. I would like you to create the layer for the view through returning CAShapeLayer because the layerClass
of the view.
ChatGPT blushing: Inside server error
Was it one thing I mentioned? No, in all probability not. ChatGPT’s servers get overloaded regularly. Once I pressed the “Regenerate Response” button I acquired a correct response:
ChatGPT: Right here’s an up to date model of the code the place CAShapeLayer
is returned because the layer class for the OvalView
:
import UIKit
class OvalView: UIView {
var fillColor: UIColor = UIColor.crimson {
didSet {
layer.fillColor = fillColor.cgColor
}
}
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
(layer as! CAShapeLayer).path = ovalPath.cgPath
layer.fillColor = fillColor.cgColor
}
}
Good! We’re again in enterprise. I pasted this code into Xcode solely to discover a syntax error I had missed from solely glancing at it.
Me: That can produce a syntax error as a result of the layer within the fillColor setter just isn’t identified to be a CAShapeLayer to the compiler. It is advisable solid it to 1.
ChatGPT: Right here’s an up to date model of the code that casts the layer
property to a CAShapeLayer
within the fill coloration setter:
import UIKit
class OvalView: UIView {
var fillColor: UIColor = UIColor.crimson {
didSet {
(layer as! CAShapeLayer).fillColor = fillColor.cgColor
}
}
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
(layer as! CAShapeLayer).path = ovalPath.cgPath
(layer as! CAShapeLayer).fillColor = fillColor.cgColor
}
}
Technically appropriate now, however nonetheless in want of polish.
Me: Refactor these `(layer as! CAShapeLayer)` right into a property
ChatGPT: Right here’s an up to date model of the code that makes use of a computed property to keep away from casting layer
to CAShapeLayer
a number of occasions:
import UIKit
class OvalView: UIView {
var fillColor: UIColor = UIColor.crimson {
didSet {
shapeLayer.fillColor = fillColor.cgColor
}
}
non-public var shapeLayer: CAShapeLayer {
return layer as! CAShapeLayer
}
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override func layoutSubviews() {
tremendous.layoutSubviews()
updateOvalPath()
}
non-public func updateOvalPath() {
let ovalPath = UIBezierPath(ovalIn: bounds)
shapeLayer.path = ovalPath.cgPath
shapeLayer.fillColor = fillColor.cgColor
}
}
Ah, now good… we’ve got a brand new shapeLayer
property that drive casts the layer property right into a CAShapeLayer
. I put the code into my mission, it compiled with out subject and displayed the crimson circles I lusted for as requested.
Me: Thanks, that’s the very best reply for my preliminary query.
ChatGPT: You’re welcome! I’m glad I may assist. You probably have any extra questions, be happy to ask!
Analysis
Much like tales the place ChatGPT sat college exams with unhealthy however passing grades it additionally performs across the junior degree for software program builders. It is ready to produce the proper consequence if an skilled developer has the endurance and abilities to guided it over a number of iterations.
It does require a variety of hand-holding. Inputting your necessities through chat requires that you know the way to precise all these ideas in a manner that the AI can parse. I do have a variety of expertise coaching Junior Engineers and a decade of programming UIKit taught me how you can put issues that they are often understood.
I’d have spent a fraction of the time if I had simply coded this view like I envisioned it from the beginning. However the level of this change is to not show that I’m higher at programming than ChatGPT. It’s to show that Chat GPT is extra succesful than discovering pattern code on the Web. It’s not even internet search on sterioids. It’s clearly far more than that as a result of it will possibly perceive ideas and synthesize a consequence from that together with making refinements that an professional consumer is guiding it to do.
This can be a first knowledge level. There was none earlier than it as a result of public entry to ChatGPT was solely activated just a few days in the past. So it’s superb that one thing is feasible at present that wasn’t doable earlier than.
The Future
Within the least I would love ChatGPT constructed into Xcode: as a option to write new code. And I don’t imply dictating code, however talking in ideas like proven above. How about having this in Xcode? “Hello Xcode, I want a brand new view that does this and that”. If that had been extra streamlined and sooner that may instantly be helpful. Particularly as a result of it additionally improves your individual studying curve. Having to speak ideas makes you your self perceive them higher. What you train, you study!
I can see a future the place ChatGPT turns into an increasing number of productive to the eventual diploma of a Junior Developer. With this I imply any person who can conceptional execute the itty gritty which you, as a Senior Developer, give it path in a basic imaginative and prescient you preserve. Just like the captain of a ship who sees the ice bergs and is aware of the place the ship ought to be going, and ChatGPT is the whole crew.
Then the step after that – within the far future – is to mix ChatGPT with one thing that may perceive sketches and mockups and practical descriptions of apps. That is the place the Senior Developer brains are and can nonetheless be wanted for a very long time. To translate the consumer’s needs and designs into software program ideas.
We haven’t touched on the query what is going to occur if someday all Juniors will be changed by AI. If there aren’t any new Juniors then there might be no one to mature into Seniors sooner or later. Possibly our coaching must change to deal with mastering conceptionalizing and excessive degree idea synthesis and leaving the mundane creation of code to AI as an alternative.
Additionally revealed on Medium.
Associated
Classes: Instruments