<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Max Kalik on Medium]]></title>
        <description><![CDATA[Stories by Max Kalik on Medium]]></description>
        <link>https://medium.com/@maxkalik?source=rss-58757e2cf638------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*V_AuHWEWNXq1eARbtKWGkw.gif</url>
            <title>Stories by Max Kalik on Medium</title>
            <link>https://medium.com/@maxkalik?source=rss-58757e2cf638------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 09 May 2026 12:42:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@maxkalik/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[UI Throwback: Creating a Retro Progress Bar for iOS using CALayers]]></title>
            <link>https://medium.com/@maxkalik/ui-throwback-creating-a-retro-progress-bar-for-ios-using-calayers-9539baf65fc3?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/9539baf65fc3</guid>
            <category><![CDATA[uikit]]></category>
            <category><![CDATA[calayer]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[macos]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Sun, 24 Dec 2023 13:41:55 GMT</pubDate>
            <atom:updated>2023-12-24T13:41:55.090Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fk8pWGvQu9NNlRAeas44cg.jpeg" /><figcaption>Retro Progress Bar</figcaption></figure><p><strong><em>This article was originally published on </em></strong><a href="https://hackernoon.com/ui-throwback-creating-a-retro-progress-bar-for-ios-using-calayers"><strong><em>Hackernoon</em></strong></a><strong><em>. It has been duplicated here for those who prefer to read articles on Medium. Enjoy your reading.</em></strong></p><h4><strong>TLDR</strong></h4><p>This guide explores the creation of a retro-style progress bar in iOS, using CALayers for a nostalgic touch. It covers the step-by-step process, from designing each layer to implementing animations, offering insights into combining old-school aesthetics with modern iOS development. Ideal for developers and UI designers, the article provides both technical depth and creative inspiration for unique UI components.</p><p><strong>Source Code:</strong></p><p><a href="https://github.com/maxkalik/RetroProgressBar">GitHub - maxkalik/RetroProgressBar: A progress bar inspired by old versions of iOS and Mac OS X</a></p><p>I love building UI components. It gives me a kind of aesthetic pleasure to see and use them after making a build. This also combines with my love for the MacOS/iOS ecosystem overall. Recently, I was tasked with creating a custom progress bar using UIKit. It’s not a big deal, but probably 99% of <a href="https://hackernoon.com/tagged/ios-app-development?ref=hackernoon.com">iOS developers</a> have made one from scratch at some point in their careers. In my case, I wanted to do a bit more than just create a couple of CALayers with animations based on a value range from 0 to 1. I was aiming for a solution that would be suitable for many scenarios where a progress bar is needed.</p><p>I want to throw myself back to the days when UI components in the Apple world looked like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*2tCP25A-0myhosJRwx-AFQ.jpeg" /><figcaption>source: <a href="https://www.smashingmagazine.com/2010/08/free-wireframing-kits-ui-design-kits-pdfs-and-resources/">https://www.smashingmagazine.com/2010/08/free-wireframing-kits-ui-design-kits-pdfs-and-resources/</a></figcaption></figure><p>Reflecting on the evolution of UI design, it’s been almost a decade since UI elements in the Apple world adopted a flat design with the introduction of iOS 7 in 2014. So, let’s indulge in nostalgia for the earlier versions of Mac OS and iOS and create a beautiful progress bar together.</p><p>If you enjoy using UIKit but are still unsure about how to manipulate CALayers, this article will be useful for you. Here, we will go through typical layout, animation, and drawing challenges, providing insights and solutions to help you along the way.</p><h4>From UIView deep into CALayers API.</h4><p>The progress bar might seem straightforward, but if we dive deep into the details, you’ll understand that it requires a lot of sublayers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*jzJUUvTWVsqYyoSfuyeJ3A.gif" /><figcaption>Retro Progress Bar</figcaption></figure><p>I’m not going to delve into CALayersit in detail here. You can check out this article where I briefly described what CALayers are and how they work: <a href="https://hackernoon.com/rolling-numbers-animation-using-only-calayers?ref=hackernoon.com">https://hackernoon.com/rolling-numbers-animation-using-only-calayers</a>.</p><p>So, let’s break it into small layers:</p><ol><li><strong>General background Layer</strong></li><li><strong>Mask layer (Animatable part)</strong></li><li><strong>Progress background Layer</strong></li><li><strong>Glare Layer</strong></li><li><strong>Shimmering Layer</strong></li></ol><p>The background layer has two additional child layers:</p><ol><li><strong>Inner shadow Layer</strong></li><li><strong>Border Gradient Layer</strong></li></ol><p>Let’s structure it properly:</p><pre><strong>RetroProgressBar</strong> [UIView]<br>  |--<strong>General background</strong> [CALayer]<br>     |--<strong>Inner Shadow</strong> [CALayer]<br>     |--<strong>Border Gradient</strong> [CALayer]<br>  |--<strong>Progress Background</strong> Layer [CALayer]<br>  |--<strong>Animated Mask Layer</strong> [CALayer]<br>  |--<strong>Glare</strong> [CALayer]<br>  |--<strong>Shimering</strong> [CALayer]</pre><p>Briefly, here’s how it should work: The Progress Bar should react to a value ranging from 0.0 to 1.0. When the value changes, we need to adjust the width of a layer based on this value:</p><pre>bounds.width * value</pre><p>Using this simple calculation, we can modify the width using CATransaction. But before we do this, we need to understand which layers should change when the value changes. Let’s revisit the structure and identify the layers that need to be responsive to value changes.</p><pre><strong>RetroProgressBar</strong> [UIView]<br>  |--<strong>General background</strong> [CALayer]<br>     |--<strong>Inner Shadow</strong> [CALayer]<br>     |--<strong>Border Gradient</strong> [CALayer]<br>  |--<strong>Progress Background Layer</strong> [CALayer]<br>  |--<strong>Animated Mask Layer</strong> [CALayer] : [Animated Width]<br>  |--<strong>Glare</strong> [CALayer] : [Animated Width]<br>  |--<strong>Shimering</strong> [CALayer] : [Animated Width]</pre><p>It appears that we need to develop a protocol that can be adopted by the layers that need animated width based on value.</p><h4>ProgressAnimatable protocol</h4><p>In protocol, we need just two methods: change the layer’s width with animation and without.</p><pre>protocol ProgressAnimatable: CALayer {<br>    <br>    /// Sets the layer&#39;s width instantly to the specified value.<br>    func setToWidth(_ width: CGFloat)<br>    <br>    /// Animates the layer&#39;s width to the specified value.<br>    func animateToWidth(_ width: CGFloat,<br>                        duration: TimeInterval,<br>                        animationType: CAMediaTimingFunctionName,<br>                        completion: (() -&gt; Void)?)<br>}</pre><p>Implementation:</p><pre>extension ProgressAnimatable {<br><br>    func setToWidth(_ width: CGFloat) {<br>        guard width &gt;= 0 else { return }<br>        <br>        removeAllAnimations()<br>        <br>        CATransaction.begin()<br>        CATransaction.setDisableActions(true)<br> <br>        self.bounds.size.width = width<br><br>        CATransaction.commit()<br>    }<br>    <br>    func animateToWidth(_ width: CGFloat,<br>                        duration: TimeInterval,<br>                        animationType: CAMediaTimingFunctionName = .easeInEaseOut,<br>                        completion: (() -&gt; Void)? = nil) {<br>        guard width &gt;= 0, width != self.bounds.width else {<br>            completion?()<br>            return<br>        }<br>        <br>        let animation = CABasicAnimation(keyPath: &quot;bounds.size.width&quot;)<br>        animation.fromValue = bounds.width<br>        animation.toValue = width<br>        animation.duration = duration<br>        animation.fillMode = .forwards<br>        animation.isRemovedOnCompletion = false<br>        animation.timingFunction = CAMediaTimingFunction(name: animationType)<br>        <br>        CATransaction.begin()<br>        CATransaction.setCompletionBlock {<br>            self.bounds.size.width = width<br>            completion?()<br>        }<br>        <br>        self.add(animation, forKey: &quot;widthChange&quot;)<br><br>        CATransaction.commit()<br>    }<br>}</pre><p>Both methods use CATransaction. The first method might seem odd because we only need to change a value without animation. So, why is CATransaction necessary?</p><p>Try creating a basic CALayer and changing its width or height without any animation. When you run a build, you’ll notice the CALayer changes with an animation. How is this possible? In Core Animation, changes to layer properties (such as bounds, position, opacity, etc.) are typically animated based on the default action associated with that property. This default behavior is intended to provide smooth visual transitions without the need for explicit animations for every property change.</p><p>This means that we need to explicitly disable actions in CALayer. By setting CATransaction.setDisableActions(true), you ensure that the layer&#39;s width is instantly updated to the new value without any intermediate animated transition.</p><p>The second method offers a flexible and reusable way to animate the width of any layer conforming to the ProgressAnimatable protocol. It provides control over the animation&#39;s duration and pacing and includes an option for a completion action. This makes it well-suited for various animation needs within the UIKit framework.</p><p>Our candidates to conform to this protocol will be three layers: Animated Mask Layer, Glare Layer, and Shimmering Layer. Let’s go ahead and implement it.</p><h4>ProgressMaskLayer</h4><p>It’s time to conform to our protocol, and the most important layer in this setup will be our general CAShapeLayer. Why do we need this mask? Why can&#39;t it just be a CALayer with an animated width? As an iOS Engineer, especially one focusing on front-end development, you often need to anticipate potential use cases for your components. In my instance, there is a Progress Background Layer. This isn&#39;t an animated CALayer, but what if it were animated with something like CAReplicatorLayer? If you&#39;re concerned about performance, you&#39;d likely consider that such a layer should be rendered once and then simply perform its animation. That&#39;s why this mask can be extremely useful. It allows for efficient rendering while still offering the flexibility needed for various animation scenarios.</p><pre>final class ProgressMaskLayer: CAShapeLayer, ProgressAnimatable {<br><br>    override init() {<br>        super.init()<br>        <br>        backgroundColor = UIColor.black.cgColor<br>        anchorPoint = CGPoint(x: 0, y: 0.5)<br>    }<br><br>    override init(layer: Any) {<br>        super.init(layer: layer)<br>    }<br><br>    required init?(coder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br>}</pre><p>Let’s take a look at this line:</p><pre>anchorPoint = CGPoint(x: 0, y: 0.5)</pre><p>This line is crucial for our Progress Bar. By default, CALayers are centered, with the anchorPoint located at the center of the layer, not on the middle of the left edge as commonly assumed. The anchorPoint is the point around which all transformations of the layer occur. For a layer used as a progress bar, this setting typically means that when the layer&#39;s width changes, it expands equally in both directions from the anchorPoint. However, if we adjust the anchorPoint to the middle of the left edge, the layer will expand only to the right while the left edge remains fixed in place. This behavior is essential for a progress bar, ensuring that the growth of the bar appears from one side rather than both.</p><pre>private let progressMaskLayer = ProgressMaskLayer()<br>private let progressBackgroundLayer = ProgressBackgroundLayer()<br><br>public init() {<br>        super.init(frame: .zero)<br><br>        layer.addSublayer(progressMaskLayer)<br>        layer.addSublayer(progressBackgroundLayer)<br>        <br>        progressBackgroundLayer.mask = progressMaskLayer<br><br>}</pre><p>In the snippet above, we initialize two main parts of the Progress Bar: ProgressMaskLayer and ProgressBackgroundLayer. Let’s take a look at the second one.</p><pre>final class ProgressBackgroundLayer: CAGradientLayer {<br>    <br>    override init() {<br>        super.init()<br><br>        locations = [0, 0.5, 1]<br>    }<br><br>    override init(layer: Any) {<br>        super.init(layer: layer)<br>    }<br><br>    required init?(coder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br>    <br>    override func layoutSublayers() {<br>        super.layoutSublayers()<br><br>        colors = [<br>            backgroundColor?.lightened(by: 0.3),<br>            backgroundColor,<br>            backgroundColor?.darkened(by: 0.5)<br>        ].compactMap({ $0 })<br>    }<br>}</pre><p>As you can see, it’s a CAGradientLayer featuring three colors. This implementation transforms an injected color into a vertical gradient, enhancing the visibility of the progress bar and giving it a more voluminous appearance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/547/1*-hlEpquSkHZB7XCHy6HTUQ.png" /><figcaption>Gradient</figcaption></figure><p>To make this possible, I prepared two methods in a CGColor extension. For fun, I chose not to convert CGColor back to UIColor, but to play solely within the realm of CGColor:</p><pre>extension CGColor {<br>    func darkened(by value: CGFloat) -&gt; CGColor {<br>        guard let colorSpace = self.colorSpace,<br>              let components = self.components,<br>              colorSpace.model == .rgb,<br>              components.count &gt;= 3 else {<br>            return self<br>        }<br><br>        let multiplier = 1 - min(max(value, 0), 1)<br>        let red = components[0] * multiplier<br>        let green = components[1] * multiplier<br>        let blue = components[2] * multiplier<br>        let alpha = components.count &gt; 3 ? components[3] : 1.0<br><br>        return CGColor(<br>            colorSpace: colorSpace,<br>            components: [red, green, blue, alpha]<br>        ) ?? self<br>    }<br>    <br>    func lightened(by value: CGFloat) -&gt; CGColor {<br>        guard let colorSpace = self.colorSpace,<br>              let components = self.components,<br>              colorSpace.model == .rgb,<br>              components.count &gt;= 3 else {<br>            return self<br>        }<br><br>        let red = min(components[0] + value, 1.0)<br>        let green = min(components[1] + value, 1.0)<br>        let blue = min(components[2] + value, 1.0)<br>        let alpha = components.count &gt; 3 ? components[3] : 1.0<br>        <br>        return CGColor(<br>            colorSpace: colorSpace,<br>            components: [red, green, blue, alpha]<br>        ) ?? self<br>    }<br>}</pre><p>As you can see, it’s not too complicated. We just need to getcolorSpace, components (red, green, blue, and alpha), and model. This means that based on the value, we can calculate a new color: either darker or lighter.</p><h4><strong>Glare</strong></h4><p>Let’s move towards making our Progress Bar more 3D-ish. Now we need to add another layer, which we’ll call Glare.</p><p>It’s a straightforward implementation, but it should conform to the ProgressAnimatable protocol.</p><pre>final class ProgressGlareLayer: CALayer, ProgressAnimatable {<br>    <br>    override init() {<br>        super.init()<br><br>        backgroundColor = UIColor.white.withAlphaComponent(0.3).cgColor<br><br>        anchorPoint = CGPoint(x: 0, y: 0.5)<br>    }<br><br>    override init(layer: Any) {<br>        super.init(layer: layer)<br>    }<br><br>    required init?(coder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cg8BDMMGbAmnBSqYvsi3KA.png" /><figcaption>With glare</figcaption></figure><p>To keep things simple, we only need the background color here to be white with an alpha of 0.3.</p><h4>Little Nice Shimmering</h4><p>I know some might say that MacOS X or iOS before version 6 didn’t have UI components with shimmering effects like this. But, as I said, this is like a movie inspired by a real story. This shimmering accentuates the 3D effect and makes it more visible.</p><pre>final class ShimmeringLayer: CAGradientLayer, ProgressAnimatable {<br><br>    let glowColor: UIColor = UIColor.white<br>    <br>    private func shimmerAnimation() -&gt; CABasicAnimation {<br>        let animation = CABasicAnimation(keyPath: &quot;locations&quot;)<br>        animation.fromValue = [-1.0, -0.5, 0.0]<br>        animation.toValue = [1.0, 1.5, 2.0]<br>        animation.duration = 1.5<br>        animation.repeatCount = Float.infinity<br>        return animation<br>    }<br>    <br>    private func opacityAnimation() -&gt; CABasicAnimation {<br>        let animation = CABasicAnimation(keyPath: &quot;opacity&quot;)<br>        animation.fromValue = 1.0<br>        animation.toValue = 0.0<br>        animation.duration = 0.1 // Quick transition to transparent<br>        animation.beginTime = shimmerAnimation().duration<br>        animation.fillMode = .forwards<br>        animation.isRemovedOnCompletion = false<br>        return animation<br>    }<br>    <br>    private func animationGroup() -&gt; CAAnimationGroup {<br>        let pauseDuration = 3.0 // Duration of pause between shimmering<br>        let shimmerAnimation = shimmerAnimation()<br>        let opacityAnimation = opacityAnimation()<br>        let animationGroup = CAAnimationGroup()<br>        <br>        animationGroup.animations = [shimmerAnimation, opacityAnimation]<br>        animationGroup.duration = shimmerAnimation.duration + pauseDuration<br>        animationGroup.repeatCount = Float.infinity<br>        <br>        return animationGroup<br>    }<br><br>    override init() {<br>        super.init()<br><br>        setupLayer()<br>    }<br><br>    override init(layer: Any) {<br>        super.init(layer: layer)<br>    }<br><br>    required init?(coder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br>    <br>    override func layoutSublayers() {<br>        super.layoutSublayers()<br><br>        startAnimation()<br>    }<br>    <br>    private func setupLayer() {<br>        let lightColor = UIColor.white.withAlphaComponent(0.7).cgColor<br>        let darkColor = UIColor.white.withAlphaComponent(0).cgColor<br>        <br>        colors = [<br>            darkColor,<br>            lightColor,<br>            darkColor<br>        ]<br>        <br>        anchorPoint = CGPoint(x: 0, y: 0.5)<br>        locations = [0.0, 0.5, 1.0]<br>        startPoint = CGPoint(x: 0.0, y: 0.5)<br>        endPoint = CGPoint(x: 1.0, y: 0.5)<br><br>        shadowColor = glowColor.cgColor<br>        shadowRadius = 5.0<br>        shadowOpacity = 1<br>        shadowOffset = .zero<br>    }<br>    <br>    private func startAnimation() {<br>        if animation(forKey: &quot;shimmerEffect&quot;) == nil {<br>            let animationGroup = animationGroup()<br>            add(animationGroup, forKey: &quot;shimmerEffect&quot;)<br>        }<br>    }<br>}</pre><p>Here, we need two animations (Opacity and Location) combined into a CAAnimationGroup. Also, using CAAnimationGroup allows us to arrange a pause between animation sessions. This feature is quite useful because clients might want to control this property as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Bo6Im3f-CinGWkFPG3UmyA.png" /><figcaption>With shimmering</figcaption></figure><p>As you can see in the picture, this adds more 3D effects, but it’s not quite enough. Let’s move forward.</p><h4>Background Layer</h4><p>We need to work with our static background layer, which lies beneath the animated Progress Line. If you recall, we have two additional sublayers there: the Inner Shadow and the Border Gradient.</p><pre>|--<strong>General background</strong> [CALayer]<br>     |--<strong>Inner Shadow</strong> [CALayer]<br>     |--<strong>Border Gradient</strong> [CALayer]</pre><p>I would say these two layers were quite popular in search queries several years ago, possibly due to UI trends. Let’s create our own versions and immortalize them in this article for future generations :)</p><h4>Inner Shadow Layer</h4><p>The class is designed to create a layer with an inner shadow effect, which can be a visually appealing way to add depth to UI elements.</p><pre>final class InnerShadowLayer: CAShapeLayer {<br><br>    override init() {<br>        super.init()<br>        <br>        masksToBounds = true<br>        shadowRadius = 3<br>        shadowColor = UIColor.black.cgColor<br>        shadowOffset = CGSize(width: 0.0, height: 1.0)<br>        shadowOpacity = 0.5<br>    }<br>    <br>    required init?(coder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br>    <br>    override func layoutSublayers() {<br>        super.layoutSublayers()<br>        <br>        // Creates a path for the shadow that extends slightly outside the bounds of the layer.<br>        let shadowPath = UIBezierPath(roundedRect: bounds.insetBy(dx: -5, dy: -5), cornerRadius: cornerRadius)<br>        // Creates a cutout path that is the inverse of the layer&#39;s bounds.<br>        let cutoutPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).reversing()<br><br>        shadowPath.append(cutoutPath)<br><br>        self.shadowPath = shadowPath.cgPath<br>    }<br>}</pre><p>We need a CAShapeLayer because we have to set shadowPath. For that, we need to create a path for the shadow that extends slightly outside the bounds of the layer using insetBy from bounds. And then another UIBezierPath — a cutout path that is the inverse of the layer’s bounds.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EQrWjINu2nf95IfnWuFoDA.png" /><figcaption>Inner shadow</figcaption></figure><p>In the illustration, it’s already almost there. But we need the last layer which will bring us back to MacOS X Leopard.</p><h4>Border Gradient Layer</h4><p>It’s a subclass of CAGradientLayer designed to create a gradient border around a layer. It uses a CAShapeLayer as a mask to achieve the border effect. The shapeLayer is configured with a stroke color (initially black) and no fill color and is used as the mask for the BorderGradientLayer. This setup allows the gradient to be visible only where the shapeLayer has a stroke, effectively creating a gradient border.</p><pre>final class BorderGradientLayer: CAGradientLayer {<br>    <br>    let shapeLayer: CAShapeLayer = {<br>        let layer = CAShapeLayer()<br><br>        layer.strokeColor = UIColor.black.cgColor // Temporary color<br>        layer.fillColor = nil<br>        return layer<br>    }()<br>    <br>    init(borderWidth: CGFloat, colors: [UIColor]) {<br>        super.init()<br><br>        self.mask = shapeLayer<br>        self.colors = colors.map { $0.cgColor }<br>        self.setBorderWidth(borderWidth)<br>    }<br>    <br>    required init?(coder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br>    <br>    override func layoutSublayers() {<br>        super.layoutSublayers()<br>        <br>        shapeLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath<br>    }<br>    <br>    func setBorderWidth(_ borderWidth: CGFloat) {<br>        self.shapeLayer.lineWidth = borderWidth<br>    }<br>}</pre><p>The initializer of BorderGradientLayer accepts a borderWidth and an array of UIColors for the gradient. It sets up the gradient colors, applies the border width to the shapeLayer, and uses the shapeLayer as a mask to restrict the gradient to the border area.</p><p>The layoutSublayers method ensures that the path of shapeLayer is updated to match the layer&#39;s bounds and corner radius, making sure the border fits the layer correctly. The setBorderWidth method allows dynamic adjustment of the border&#39;s width after initialization.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WxKdCyLFQDX0mkrBVgigLA.png" /></figure><p>Way better. Ok. let’s stop making visual stuff and let’s work on the logic.</p><h4>All together.</h4><p>Now it’s time to combine all these layers and let them into life.</p><pre>private lazy var backgroundLayer = BackgroundLayer(<br>        borderWidth: borderWidth,<br>        colors: borderColors<br>)<br>    <br>private let glareLayer = ProgressGlareLayer()<br>private let shimmeringLayer = ShimmeringLayer()<br>private let progressMaskLayer = ProgressMaskLayer()<br>private let progressBackgroundLayer = ProgressBackgroundLayer()<br>    <br>public init() {<br>        super.init(frame: .zero)<br>        <br>        layer.backgroundColor = UIColor.white.cgColor<br>        layer.masksToBounds = true<br>        <br>        layer.addSublayer(progressMaskLayer)<br>        layer.addSublayer(progressBackgroundLayer)<br>        layer.insertSublayer(backgroundLayer, at: 0)<br>        <br>        <br>        progressBackgroundLayer.addSublayer(shimmeringLayer)<br>        progressBackgroundLayer.addSublayer(glareLayer)<br>        <br>        progressBackgroundLayer.mask = progressMaskLayer<br>}</pre><p>Let’s take a look at how we going to deal with all layers which should change their width with animation:</p><pre>public func setValueWithAnimation(_ value: Double,<br>                               duration: TimeInterval = 1.0,<br>                               animationType: CAMediaTimingFunctionName = .easeInEaseOut,<br>                               completion: (() -&gt; Void)? = nil) {<br>        <br>        let newWidth = calculateProgressWidth(value)<br>        let animationGroup = DispatchGroup()<br><br>        animationGroup.enter()<br>        shimmeringLayer.animateToWidth(<br>            newWidth - WIDTH_ANIMATABLE_INSET,<br>            duration: duration,<br>            animationType: animationType,<br>            completion: animationGroup.leave<br>        )<br>        <br>        animationGroup.enter()<br>        progressMaskLayer.animateToWidth(<br>            newWidth,<br>            duration: duration,<br>            animationType: animationType,<br>            completion: animationGroup.leave<br>        )<br>        <br>        animationGroup.enter()<br>        glareLayer.animateToWidth(<br>            newWidth - WIDTH_ANIMATABLE_INSET,<br>            duration: duration,<br>            animationType: animationType,<br>            completion: animationGroup.leave<br>        )<br><br>        animationGroup.notify(queue: .main) {<br>            completion?()<br>        }<br>}</pre><p>First, we need to prepare a width from a value. The value should be less than 0 and greater than 1. Plus we need to take into consideration a possible border width.</p><pre>fileprivate func normalizeValue(_ value: Double) -&gt; Double {<br>    max(min(value, 1), 0)<br>}<br><br>private func calculateProgressWidth(_ value: Double) -&gt; CGFloat {<br>        let normalizedValue = normalizeValue(value)<br>        let width = bounds.width * normalizedValue - borderWidth<br>        return width<br>}</pre><p>For the correct 3D effect, glare, and shimmering layers should be with a little padding from the right and left sides:</p><pre>newWidth - WIDTH_ANIMATABLE_INSET</pre><p>And finally, the method uses an animation group (DispatchGroup) to synchronize the animations of three layers: shimmeringLayer, progressMaskLayer, and glareLayer. Each layer&#39;s width is animated to the calculated width (adjusted by WIDTH_ANIMATABLE_INSET for shimmeringLayer and glareLayer to fit the design) over the specified duration and with the specified animation curve.</p><p>The animations are coordinated using the DispatchGroup, ensuring that all animations start simultaneously and the completion closure is called only after all animations have finished. This method provides a visually appealing way to update the progress bar&#39;s value with a smooth, synchronized animation across its various decorative layers.</p><h4>Source Code</h4><p>The latest version exists as a Swift Package and Pod. Btw, contributions are welcome!</p><p><a href="https://github.com/maxkalik/RetroProgressBar">GitHub - maxkalik/RetroProgressBar: A progress bar inspired by old versions of iOS and Mac OS X</a></p><h4>Usage</h4><p>Create an instance of RetroProgressBar. You can add it to your view hierarchy as you would with any UIView.</p><pre>let progressBar = RetroProgressBar()</pre><p>Customization:</p><pre>progressBar.progressColor = UIColor.systemBlue<br>progressBar.cornerRadius = 5.0<br>progressBar.borderWidth = 2.0<br>progressBar.borderColors = [UIColor.white, UIColor.gray]</pre><p>To change value with animation:</p><pre>progressBar.setValueWithAnimation(0.75, duration: 1.0, animationType: .easeInEaseOut) {<br>    print(&quot;Animation Completed&quot;)<br>}</pre><p>Without animation:</p><pre>progressBar.setValue(0.5)</pre><h3>Final Thoughts</h3><p>Recreating this old-style progress bar has been a nostalgic dive into the aesthetics I deeply miss. Working with UIKit andCALayer reminded me of the versatility and power of our tools as developers. This project wasn&#39;t just about technical implementation; it was a journey back to a beloved design era, proving that we can still craft anything, including the timeless charm of older designs.</p><p>This experience has been a poignant reminder that in <a href="https://hackernoon.com/a-guide-to-ui-design-patterns-in-flutter-navigation-and-layout?ref=hackernoon.com">UI design</a>, simplicity and nostalgia can coexist beautifully, bridging past and present.</p><p>Happy coding!</p><pre><strong>Want to Connect?</strong><br>Feel free to follow me on <a href="https://twitter.com/maxkalik">Twitter</a>.<br>To learn more about me, check out my <a href="https://hackernoon.com/meet-the-writer-hackernoons-contributor-max-kalik-ios-software-engineer">interview on Hackernoon</a>.<br>I welcome and appreciate any feedback, suggestions, or comments<br>on my research and articles.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9539baf65fc3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mobile App Development in Small to Mid-Sized Businesses.]]></title>
            <link>https://medium.com/swlh/mobile-app-development-in-small-to-mid-sized-businesses-d237c60bb42?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/d237c60bb42</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mobile-app-marketing]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Tue, 17 Oct 2023 11:52:57 GMT</pubDate>
            <atom:updated>2023-10-17T15:14:21.511Z</atom:updated>
            <content:encoded><![CDATA[<h3>Mobile App Development in Small to Mid-Sized Businesses</h3><p>Recently, I was interviewed about mobile app development. I’ve curated the most intriguing questions and consolidated my insights into this article, which might be useful for those in non-technical roles from small/mid-size businesses. I welcome you to join the discussion in the comments.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-NGlpAlGeiiiT-aA" /><figcaption>Photo by <a href="https://unsplash.com/@artinbakhan?utm_source=medium&amp;utm_medium=referral">Artin Bakhan</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>When it comes to mobile app development, do you recommend hiring an in-house team or partnering with a mobile app development agency? While budget is a significant factor, which option do you believe delivers better quality, and why?</h4><p>It is a super individual and depends on the business’s specific needs, budget, constraints, and targets. If a project is already well funded, and implies the long-term vision of updates and iterations, then, I would say an in-house team would be better. Generally, if a project has a specific budget and well-prepared plan, then it could be better to find a dev agency that would commit service to create everything based on an agreement.</p><p>Let me provide some examples. Consider a mobile application for a dance festival somewhere, where people can see lineups, a map, some information, and so on. Does this app need a specific in-house team to develop? I think, not.</p><p>Another example. Consider a project that potentially in the future could be a huge thing. There is a founder who decides how it would be better to hire, but mostly from my experience, that kind of project is developed by an in-house team. Especially if the projects are already well funded. Founders prefer to invest in developers who will make a product at the next desk in the office.</p><p>The last example. Consider a project in which you have to use a really specific technology, and this technology can be used by one of a kind. Let’s say an agency can provide a service to develop a feature using this specific technology. So, if deadlines are really close, a dev agency could be useful.</p><h4><strong>Gartner sees architecture as the heart of a mobile app. Do you agree? If yes, please state a one-liner reason for it.</strong></h4><p>Agree, because it helps developers communicate with each other by maintaining, scaling, and creating new application components.</p><h4><strong>Can you break down the different types of mobile app architectures? And in your opinion, which is the best fit for today’s small businesses? We’re curious because there’s a common belief that PWAs might be superior to Hybrid and Native apps.</strong></h4><p>Native iOS / Android Apps, Cross-Platform Apps (<a href="https://flutter.dev/">Flutter</a>, <a href="https://reactnative.dev/">React-Native</a>), Hybrid Apps (<a href="https://dotnet.microsoft.com/en-us/apps/xamarin">Xamarin</a>, <a href="https://ionicframework.com/">Ionic</a>), Web Apps (<a href="https://react.dev/">React</a>, <a href="https://vuejs.org/">Vue</a>), <a href="https://en.wikipedia.org/wiki/Progressive_web_app">Progressive Web Apps</a> (PWDs) and Server-Driven UI (<a href="https://github.com/Lona/Lona">Lona</a>). In the case of small businesses, from my perspective, I see the tendency to use cross-platform technologies in start-ups, especially in Europe. I explain this to myself so: Time is money, which means the biggest cost is — developer hours. If a business doesn’t need a specific API, higher performance, and more rich UX then why not save money? In the case of PWAs it’s getting really interesting: No need for app store submission (It’s a huge thing in my opinion). In the case of iOS platforms, iOS 16.4 (released in Spring 2023) already brings push notifications for web apps. An App Can be added to the home screen, in MacOS users can add PWA to the Dock.</p><h4><strong>In the initial brainstorming and discussion phase, what core elements do you focus on when thinking about a new mobile app’s architecture?</strong></h4><p>Initially, I’m trying to grab information about business requirements, target audience, platforms, and expected user experience. From a technical perspective, it’s important to discuss which technology / API can be more useful, and how reusable, and scalable a mobile app project should be.</p><h4><strong>How do you define the primary purpose of an app — Is there a checklist or a set of guidelines you follow?</strong></h4><p>Yes, exactly I have a checklist. Basically, there are a set of questions about analyzing competitors, market trends, target audience, feature set discussion, deadlines, prioritization, and so on.</p><h4><strong>When researching the target audience, what tools or methodologies do you employ to understand their OS preferences and needs?</strong></h4><p>I can only say if a product has already launched. I’m a big fan of two platforms: <a href="https://marketingplatform.google.com/about/analytics/">Google Analytics</a>, <a href="https://amplitude.com/">Amplitude</a></p><h4><strong>Based on your experience, which demographic tends to gravitate more towards iOS, and which one leans towards Android?</strong></h4><p>I think a typical iOS user is from a Western country and can be ready to spend money on app purchases and in-app transactions. iOS user is younger than a typical Android user. In the case of Android users, I see that the cost of a device matters to them. Also, I know a lot of developers who really love to customize their devices. By the way, Apple also tries to attract more audiences by offering more and more customization of iOS.</p><h4><strong>iOS or Android, which app development is better per your experience? This is a frequently asked question.</strong></h4><p>As an iOS Developer, I prefer technologies from Apple. Let’s consider <a href="https://developer.apple.com/xcode/swiftui/">SwiftUI</a> — a declarative framework for building UI in Apple platforms. If to compare <a href="https://developer.apple.com/xcode/swiftui/">SwiftUI</a> with <a href="https://flutter.dev/">Flutter</a>, then it’s easy to see the resemblance. The general tendency in my opinion is to as much as possible to simplify building UI and allow developers to focus on solving business problems.</p><h4><strong>How do you “proofcheck” an app idea? What are the top resources, like GitHub, that you might recommend for checking free source codes or similar ideas?</strong></h4><p>I just google it, and use GitHub or GitLab to check sourcode if it’s possible, cannot say anything new here.</p><h4><strong>When establishing an app development team, what roles such as project manager, UI/UX designer, assistant coder, quality analyst, app developer, marketing, or product manager to check the app for market fit, do you deem essential, and why?</strong></h4><p>The app development team is who directly impacts the project’s success. From my experience, the typical small team is Mobile Developers, Backend Developers, QA / Tester, UI/UX Designer, Legal Advisor, Product Manager, and Data Analyst.</p><h4><strong>How do you prioritize and decide on core features for an app? Can you share an instance where a feature seemed essential but was later dropped based on practicality or user feedback?</strong></h4><p>In small teams, there is not so much time to play a role in a large company where stakeholders discuss core features and prioritize spending expensive time of developer hours. Mostly priorities and core features are born from business initiatives (from founders). Consequently, sometimes even core features are turning out to be not so important and even can be dropped. In my current iOS project, we had a main screen with a scroll without even a bottom tab bar, which means all navigation happened by scrolling this main content. When we launched our beta version we started to get user feedback (via Intercom) where they asked how to find a particular section. So the primary idea of having everything on one single screen was dropped and we made a predicted UI — bottom tab bar.</p><h4><strong>What are the must-have toolkits and software packages for mobile app developers today? Please do mention the purpose of any you mention here.</strong></h4><p>IDE: Xcode (for iOS or cross-platform developers) — it has everything you need to build iOS apps: editor, simulators, debugging tools, instruments, and so on; <a href="https://developer.android.com/">Android Studio</a> (for Android and cross-platform developers) — it has an editor, emulator, and debugger. If a mobile developer is making hybrid mobile apps using <a href="https://dotnet.microsoft.com/en-us/apps/xamarin">Xamaring</a> or <a href="https://unity.com/">Unity</a> then the IDE could be Visual Studio or <a href="https://www.jetbrains.com/rider/">Rider</a> from Jet Brains. These IDEs help to make C# projects and .NET. (In my personal experience, I used almost all these IDEs at once, especially when I needed to make an API between Swift and C# for Unity). When I was a React-Native developer, I used a lot <a href="https://code.visualstudio.com/">Visual Studio Code</a>. I would say VSCode (btw it’s free) can help a lot if your project has a backend, so it could be a useful editor for backend purposes.</p><p>Of course, developers should use source control tools like <a href="https://github.com/">GitHub</a>, <a href="https://about.gitlab.com/">GitLab</a>, or <a href="https://bitbucket.org/">Bitbucket</a>.</p><p>The last thing is packages, and it really depends on the project you are working on.</p><p>The most common technology that helps small/mid-size businesses is <a href="https://firebase.google.com/">Firebase</a> — it scales you from nothing to everywhere, but it depends on how much money costs each user for you. If it’s going to be millions of users, then <a href="https://firebase.google.com/">Firebase</a> is getting really aggressively expensive. The stage of mid-size companies if they still use <a href="https://firebase.google.com/">Firebase</a> they need to forecast the future of user amount and the number of requests from the client.<br><a href="https://aws.amazon.com/amplify/">AWS Amplify </a>— provides scalable and secure cloud services for mobile applications.<br>Network handling and HTTP requests: <a href="https://github.com/Alamofire/Alamofire">Alamofire</a> (iOS), <a href="https://github.com/square/retrofit">Retrofit</a>/<a href="https://google.github.io/volley/">Volley</a> (Android).<br>CI/CD Automation — <a href="https://fastlane.tools/">Fastlane</a>, <a href="https://circleci.com/">CircleCI</a>, or <a href="https://github.com/features/actions">GitHub Actions</a>.<br>UI/UX — <a href="https://www.figma.com/">Figma</a></p><p>All team members can communicate via Slack. Tasks can be organized in traditional <a href="https://www.atlassian.com/software/jira">Jira</a> or more modern Linear.</p><p>Monitoring and analysis can be handled well by Crashlytics, I would say it’s one of the best tools to identify app crashes. In the case of iOS development, XCode provides an organizer where a developer can retrieve detected crashes from <a href="https://appstoreconnect.apple.com/login">App Store Connect</a>.</p><h4><strong>How important are mockups and wireframes in the development process? How do you ensure that the UI/UX meets user expectations? Do you think UI/UX agencies are a great option to partner with, for SMBs?</strong></h4><p>In simple words, at this stage, businesses can save money and avoid mistakes, such as an unsuccessful feature that had to be dropped.</p><p>Through experimenting with UI/UX designs, conducting tests, and aligning with best practices, you’re more likely to meet or even exceed user expectations. Also, the more detailed and well-prepared wireframe — saves the time of mobile developers.</p><h4><strong>With rising concerns about data privacy, how do you ensure an app complies with data protection guidelines, like GDPR?</strong></h4><p>In my experience, we (including mobile developers) constantly test and check apps for data protection guidelines. Written tests, flexible database, transparent communication with users about how their data will be used and stored — all these things make an app data protected.</p><h4><strong>Can you share some best practices for coding and programming? What does the KISS (Keep it short and simple) principle mean to you in the context of app development? Or any other principle that you practice?</strong></h4><p>Code should be well-readable, reusable, well-documented, and tested. A lot of books and articles written about this. The <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS</a> principle is about readability. If a developer can write short and simple code then other developers will spend less time to understand it. Simplifying code makes it easier to read, maintain, and debug. Also, I would highlight more principles: <a href="https://en.wikipedia.org/wiki/SOLID">SOLID</a> (actually five design principles aimed at making software designs more understandable, flexible, and maintainable. They include Single Responsibility Principle, Open/Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle.); <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> (Don’t Repeat Yourself): This principle encourages code reusability. If you find yourself writing the same code more than once, it’s often better to create a function or module that can be reused. Plus, I would add a rule here called The Boy Scouting Rule that states “<em>Always leave the code you are working on a little bit better than you found it</em>”. I took it from <a href="https://en.wikipedia.org/wiki/Robert_C._Martin">Robert C. Martin’s</a> book.</p><h4><strong>Why do you think many apps fail? From a developer’s perspective, what are the common pitfalls to avoid?</strong></h4><p>I think the biggest reason of app fails is a lack of market research and unrealistic expectations. From a technical perspective there are a lot of various pitfalls: ignoring questions of scalability, underestimated server burden, technical dept, bad testing, and eventually bad reviews, all these things dragging apps down.</p><h4><strong>When testing an app, what’s your approach? Does testing include everything from frontend, backend, device compatibility, integration, etc.?</strong></h4><p>My approach is to keep a mobile app as stable as much as possible and always keep it ready for release. Using a unit, UI, and other tests can be of course automated. In mobile development, it’s also important to test performance, memory usage, and device compatibility.</p><h4><strong>Are there any tools or frameworks you swear by for comprehensive testing?</strong></h4><p>As a Mobile Developer, I use <a href="https://developer.apple.com/documentation/xctest">XCTest</a> and XCUITest for iOS, and <a href="https://junit.org/">JUnit</a>, <a href="https://developer.android.com/training/testing/espresso">Espresso</a> / <a href="https://developer.android.com/training/testing/other-components/ui-automator">UI Automator for Android</a>. Also, I like to use <a href="https://firebase.google.com/products/crashlytics">Crashlytics</a> for real-time crash reporting, <a href="https://www.postman.com/">Postman</a> for API testing, and <a href="https://jestjs.io/">Jest</a>, <a href="https://mochajs.org/">Mocha</a>, and <a href="https://www.chaijs.com/">Chai</a> for Backend Java Script project testing.</p><h4><strong>How important is user feedback before the official launch? Can you recommend any professionals or groups that businesses should consider for feedback?</strong></h4><p>Before the official launch, for example in the case of iOS, it’s great to use <a href="https://developer.apple.com/testflight/">Testflight</a> with a group of testers who can provide relevant and really important feedback. Even just friends who can test an app in an unexpected way, their feedback will help a lot. It would be also great to have some industry experts who can give specific comments that could be important before the official launch.</p><h4><strong>For deploying apps, what are your thoughts on server choices? Shared vs. dedicated cloud servers — pros and cons?</strong></h4><p>Choosing between shared and dedicated servers on <a href="https://cloud.google.com/">GCP</a> should be guided by your app’s resource requirements, security needs, and scalability plans. Shared Cloud Servers, like those offered by <a href="https://aws.amazon.com/">AWS</a>’s EC2 or <a href="https://www.digitalocean.com/">DigitalOcean</a>’s Droplets, are budget-friendly and easy to manage, making them a good fit for small businesses or apps just starting out. However, they can suffer from limited resources and potential security risks due to shared infrastructure. On the other hand, Dedicated Cloud Servers, such as <a href="https://aws.amazon.com/">AWS</a>’s EC2 Dedicated Hosts or <a href="https://azure.microsoft.com/en-us/">Azure</a>’s Virtual Machines, provide better performance, scalability, and security but come at a higher cost. These are more suitable for complex, resource-intensive apps that anticipate rapid growth. Your choice should align with your app’s current needs and future scalability.</p><p>I prefer <a href="https://cloud.google.com/">Google Cloud Platform</a> (GCP) also offers both shared and dedicated cloud server options, fitting different needs and budgets.</p><h4><strong>Once an app is live, how do you prioritize updates and patches? Is there a regular schedule or is it based on user feedback and security needs?</strong></h4><p>I think It’s really useful for a mobile app when a team defines a cadence of releases. I prefer 2–3 weeks, for small updates that could include new features, bug fixes, and other small updates. Based on user feedback, and business initiatives the updates are prioritized mostly together with a product manager.</p><h4><strong>What are some of the biggest challenges you’ve faced in mobile app development, and how did you overcome them?</strong></h4><p>My biggest challenge was to create a flexible and scalable API between iOS SDK and <a href="https://unity.com/">Unity</a> using undocumented techniques to bridge these two worlds. Without sufficient information on the internet, I managed to describe a quite detailed iOS API with methods for any kind of use in the Unity project.</p><h4><strong>From your experience, can you highlight a few key benefits of collaborating with a professional mobile app development company?</strong></h4><p>From my experience, it was a collaboration with a company that provided a customized SDK (User identification) solution for our app. We didn’t need to make this feature from scratch. The twice-weekly meetings kept everyone aligned and allowed us to discuss problems in real-time. The specialized Slack channel became a hub for quick questions and clarifications, making the entire process more efficient. Eventually, it saves us months of developer time.</p><h4><strong>To wrap up, what’s your one golden piece of advice for small businesses venturing into mobile app development?</strong></h4><p>Keep a small and focused full-stack team and hire only senior developers in the beginning<br>Use external services whenever possible<br>Use no-code tools (like <a href="https://retool.com/">Retool</a> or <a href="https://webflow.com/">Webflow</a>)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d237c60bb42" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/mobile-app-development-in-small-to-mid-sized-businesses-d237c60bb42">Mobile App Development in Small to Mid-Sized Businesses.</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS 16.4 brings push notifications for web apps — why that’s a big deal?]]></title>
            <link>https://itnext.io/ios-16-4-brings-push-notifications-for-web-apps-why-thats-a-big-deal-aa7ac4675f38?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/aa7ac4675f38</guid>
            <category><![CDATA[notifications]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[iphone]]></category>
            <category><![CDATA[push-notification]]></category>
            <category><![CDATA[webkit]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Sat, 18 Mar 2023 18:20:23 GMT</pubDate>
            <atom:updated>2023-03-18T18:20:23.063Z</atom:updated>
            <content:encoded><![CDATA[<h3>iOS 16.4 brings push notifications for web apps — why that’s a big deal?</h3><h4>In the approximate period of March 21 and 28, we will see a new release of iOS 16.4. About a month a beta version was available for download. I tested this version focusing on the push notification for web apps and I have my personal thoughts about this and I would like to share them with the iOS and Web developers community.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jOLnr17iZX1FrrbHXtxdPw.png" /><figcaption>Images are from the Apple website</figcaption></figure><h3>Why are push notifications important for web apps?</h3><p>First of all, this is a significant improvement for iOS overall. Notifications are equally important for both regular mobile and web applications because they have the same goal — to have contact with the audience. But I think Apple has a plan to spice up the market to make artificial competition with native apps. This diversity will bring a different experience where a user can choose between native and web apps.</p><h3>Are web apps really a good alternative to native apps?</h3><p>In the case of mobile applications, then it is too early to say which is better because we will only see this web app integration into the iOS ecosystem and learn statistics. Of course, for now, native apps look more robust and stable. Using native applications gives a different experience compared with web apps. On the other hand, building a web app is a bit easier than native apps, which means startups can build first their web versions before native implementations. Or large companies like Twitter can get more audience to provide two options — web and native.</p><h3>What would you like to see added to web app capabilities on iOS?</h3><p>Improving access to native device features such as camera, microphone, and contacts. Also, it will be cool to improve access to persistent data like user defaults. If web apps could be used absolutely offline then they could be on the same level as native apps. Push notifications also can be improved, for example by grouping and prioritizing them. Another important thing is performance, improving this field can make web apps more competitive.</p><h3>Conclusion</h3><p>I think all these updates are only a rehearsal — a part of unifying mobile and desktop interfaces — most likely what we will see in the future let’s say in iOS 17 or later. Web apps and native apps — it won’t matter anymore. If to try to predict then it seems we will get a combination between MacOS, iPadOS, and iOS called something like AppleOS or so.</p><h3>Links</h3><ul><li><strong>Release notes:</strong> <a href="https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_4-release-notes">https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_4-release-notes</a></li><li><strong>Web push notifications for web apps</strong>: <a href="https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/">https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/</a></li><li><strong>iOS 16.4 Beta version: </strong><a href="https://developer.apple.com/news/releases/?id=03152023d">https://developer.apple.com/news/releases/?id=03152023d</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aa7ac4675f38" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/ios-16-4-brings-push-notifications-for-web-apps-why-thats-a-big-deal-aa7ac4675f38">iOS 16.4 brings push notifications for web apps — why that’s a big deal?</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building iOS framework with dependencies.]]></title>
            <link>https://itnext.io/building-ios-framework-with-dependencies-e6e141f346ec?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/e6e141f346ec</guid>
            <category><![CDATA[framework]]></category>
            <category><![CDATA[xcframework]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[cocoapods]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Wed, 22 Feb 2023 23:14:21 GMT</pubDate>
            <atom:updated>2023-02-22T23:14:21.752Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BsPJZQeCSBaEw-iqjdg7tw.png" /></figure><p>In my current iOS position, I’m deeply involved in building a framework for iOS games. I wouldn’t write this article if this framework weren’t unusual. What do I mean by that — unusual? Usually, frameworks encapsulate only the source code without dependencies and other libraries, and of course, I don’t mean Foundation and other build frameworks in iOS. I mean the dependencies installed using cocoapods which for some business reason could be included in your framework manually.</p><p>Recently I wrote an interesting detailed article/analysis about how to create an iOS XCFramework with cocoapod dependencies, so if it’s your case check this out: <a href="https://hackernoon.com/cocoapod-as-xcframework-with-dependencies">https://hackernoon.com/cocoapod-as-xcframework-with-dependencies</a></p><p>In this article, we will build only a manual framework with other frameworks inside statically. I admire it’s a pretty rare case but businesses sometimes dictate their terms where we — developers should implement a workable solution quickly.</p><h3>Final product</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hhEKs-xSDdVJ_FYoXN1VcA.png" /><figcaption>The final product — MyFramework.framework</figcaption></figure><p>The task is to create a simple framework only for real devices with dependencies that can be installed via cocoapod. Where we could use this framework? In Unity! You can read my article about this <a href="https://medium.com/better-programming/setting-up-ios-framework-for-unity-9ef4e577db89">here</a>. But let’s move forward and start to create our final product.</p><h3>Podfile</h3><p>Assume, we created a new iOS Framework project. I named it MyFramework and in the terminal run pod init in the root of the project folder, so we have Podfile where we can start. Let’s configure it:</p><pre>platform :ios, &#39;14.0&#39;<br><br>source &#39;https://cdn.cocoapods.org/&#39;<br>source &#39;https://github.com/passbase/cocoapods-specs.git&#39;<br>source &#39;https://github.com/passbase/microblink-cocoapods-specs.git&#39;<br><br>target &#39;YourFramework&#39; do<br>  pod &#39;CropViewController&#39;, &#39;2.6.1&#39;<br>  pod &#39;Kingfisher&#39;, &#39;7.5.0&#39;<br>  pod &#39;Intercom&#39;, &#39;14.0.6&#39;<br>  pod &#39;Frames&#39;, &#39;4.0.4&#39;<br>  pod &#39;Passbase&#39;, &#39;2.7.0&#39;<br><br>end</pre><p>I didn’t use some abstract cocoapods, I took the real ones on purpose to show you the real process which you could repeat by yourself. At the first sight of the list of dependencies, you could say that they are picked randomly. And yes and no, because all these dependencies are different ones, for example:</p><ul><li>Kingfisher — it’s just a piece of source code</li><li>CropViewController — also with source code but with a bundle of the resource</li><li>Intercom is interested to be in this list because it’s an xcframework itself</li><li>Frames dependency is here because it’s a combination of source code and some sub-dependencies inside including xcframeworks</li><li>Passbase is one of the most interesting specimens because to install this guy we need to use specific source paths including an additional source path for Passbase’s dependency called Microblink. All of them are xcframeworks.</li></ul><p>Testing all these various dependencies we will create a framework for manual installation.</p><h3>Resources</h3><p>If a framework uses resources (images, video, or other files), then we need to prepare a separate target for it and this process looks a little bit like a workaround. Let me describe it step-by-step:</p><ol><li>Open File / New / Target. Choose macOS and in the Framework &amp; Library section choose Bundle.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/736/1*c9JrOwC3qwF9TDiMgOHrgA.png" /></figure><p>2. For now, make sure the resource files belong to this target.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cY21tH8J25FIOczG7G0grQ.png" /></figure><p>3. Go to Project target / Build phases / Copy Bundle Resources and remove and exclude everything there except the bundle.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4sC7Yl1CXQOESBlR5s1HIQ.png" /></figure><p>4. The last step is not obvious but necessary. Do you remember we created this bundle for MacOS but we need it for iOS? So, go to Build Settings of the framework bundle and change Architectures / Base SDK to iOS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/938/1*dyE1HBUOg4j_ssz5n_MhNg.png" /></figure><p>5. Also in Framework target Build Phases we need to include our bundle as a target dependency.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_h8HT3QiMfmGAAt39Onmjg.png" /></figure><h3>Build</h3><p>Xcode has a powerful CLI to make an archive using xcodebuild with a set of flags:</p><pre>xcodebuild archive \<br>-workspace MyFramework.xcworkspace \<br>-scheme MyFramework \<br>-configuration Release \<br>-sdk iphoneos \<br>-archivePath archives/ios_devices.xcarchive \<br>BUILD_LIBRARY_FOR_DISTRIBUTION=YES \<br>SKIP_INSTALL=NO \</pre><p>This simple script creates an archive. Open it with the right mouse button using “Show content” and find a fresh-backed framework in the products.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MgsmlyO9vlbmmYQdQwfJDQ.png" /></figure><p>Now the framework is ready to be tested. You can just drag this framework to your test iOS project structure. Don’t forget to Embed and Sign this framework. Choose a real device where we are going to test our app with this framework.</p><p>The result will be not so good. The project will give you an error — Build failed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GfeRjs4z8L6Yjvwd-SAheA.png" /></figure><p>The reason is our dependencies are not found, they weren’t included in our framework. So let’s solve this problem.</p><h3>Including dependencies to a framework</h3><p>Since we install our dependencies using cocoapod we need to update Podfile. By default, the pods are installed with dynamic linking but we need to install them with static linkage.</p><pre>platform :ios, &#39;14.0&#39;<br><br>source &#39;https://cdn.cocoapods.org/&#39;<br>source &#39;https://github.com/passbase/cocoapods-specs.git&#39;<br>source &#39;https://github.com/passbase/microblink-cocoapods-specs.git&#39;<br><br>target &#39;YourFramework&#39; do<br>  use_frameworks! :linkage =&gt; :static<br><br>  pod &#39;CropViewController&#39;, &#39;2.6.1&#39;<br>  pod &#39;Kingfisher&#39;, &#39;7.5.0&#39;<br>  pod &#39;Intercom&#39;, &#39;14.0.6&#39;<br>  pod &#39;Frames&#39;, &#39;4.0.4&#39;<br>  pod &#39;Passbase&#39;, &#39;~&gt; 2.7.0&#39;<br><br>end</pre><p>So the main update of our Podfile is updating this particular line: use_frameworks! to use_frameworks! :linkage =&gt; :static</p><p>The next step is not very obvious (I would say even questionable) because we need to include all dependencies’ frameworks to our framework literally by copying them from the Products folder of Pods into our project.</p><p>If doing this manually, you need to Run the framework project first, then you will get the Products folder with frameworks from each pod including their bundles.</p><p>To get there you can right-click on any framework in the Products folder and press Open in Finder.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*mS1afh8adpYLVK5IUu0RLg.png" /></figure><p>From each dependency folder, you need to find a folder with a name something like Name.framework, and drag and drop it to your Framework project. By the way, we don’t need to include frameworks’ resource bundles in our project because they will be included automatically.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lBwYaCMX8TEbSjtKprjF1w.png" /></figure><p>But, even here we can come across with small nuance. As you remember some of our dependencies are XCFramework, and the folder where supposed to be these frameworks are empty.</p><p>Among all the product folders you can find a specific one called: XCFrameworkIntermediates where you can find all frameworks from XCFrameworks.</p><p>So, your project structure should look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*EQ7vY8QGKtUkDaKKx5wl4A.png" /></figure><p>Another important thing about frameworks from XCFrameworks is — we need to embed and sign them in our Framework.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/997/1*pKsGbN9BzCyW2Z1Kp-XV3Q.png" /></figure><p>After all these manual manipulations, you can build your framework again and get the final product which you can test. If you did everything correctly, then your project with the new framework will run successfully and all methods from the framework will work without failures.</p><h3>Potential problems</h3><p>Traditionally, instead of a conclusion, which nobody cares about, I think it will be better to list some potential problems and how to solve them. Let’s get started.</p><h4>Problem 1: Library not loaded.</h4><p>Basically, it is an error always together with this message:</p><pre>dyld[17871]: Library not loaded.<br>&#39;@rpath/MyFramework.framework/MyFramework&#39;</pre><p><strong>Solution:<br></strong>One of the most often errors. We are all people and we can just forget to sign the framework.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zchvOCGT1QrNU3jS7Y9fLA.png" /></figure><h4>Problem 2: Error: Compiled module was created by a newer version of the compiler.</h4><p>In your build script of xcodebuild, you most likely forgot to add this flag: <strong>BUILD_LIBRARY_FOR_DISTRIBUTION</strong></p><h4>Problem 3: Build failed. A client uses the same dependencies which your framework already uses.</h4><p><strong>Solution:<br>Briefly:</strong> Versions of the dependencies in a framework and in an app should be the same.<br><strong>Detailed:</strong> Let’s say you build your framework with cocoapod Alamofire 5.5.0and a client app will use your framework and Alamofire 6.0.0, so this combination will cause a problem. In other words, you cannot build your app, and you won’t understand what is going on, because the error will be about symbols, or something else.</p><pre>pod &#39;Intercom&#39;, &#39;2.0.0&#39;<br>pod &#39;Alamofire&#39;, &#39;5.5.0&#39;</pre><h4>Problem 4: The path to your product is not found (the product folder in the archive is empty)</h4><p><strong>Solution:</strong><br>Check flag: SKIP_INSTALL=NO in your build script — it will install your framework in the archive, in other words, your product folder won’t be empty.</p><h4>Problem 5: Property is not a member type of class MyFramework.MyFramework</h4><pre>.../MyFramework.swiftinterface:6:34: error: &#39;SomeProperty&#39; is not a member type of class &#39;MyFramework.MyFramework&#39;<br>   public var direction: MyFramework.SomeProperty { get set }</pre><p><strong>Solution:<br></strong>The best way to avoid this is if the names of modules in a framework are different and don’t coincide with the name of the framework itself. So, if you just started your framework from scratch — keep your public module name different. For example, our framework is called MyFramework.xcframework so the name of your general module could be MyFrameworkMethods</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e6e141f346ec" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/building-ios-framework-with-dependencies-e6e141f346ec">Building iOS framework with dependencies.</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Quick start writing Unit tests in Xcode]]></title>
            <link>https://levelup.gitconnected.com/quick-start-writing-unit-tests-in-xcode-4655b644c770?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/4655b644c770</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[unitt-testing]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[xctest]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Thu, 26 Jan 2023 21:06:54 GMT</pubDate>
            <atom:updated>2023-01-26T21:06:54.946Z</atom:updated>
            <content:encoded><![CDATA[<h3>Quick start writing unit tests in Xcode</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ZBlv15bnGa1e3my7" /><figcaption>Photo by <a href="https://unsplash.com/@ffstop?utm_source=medium&amp;utm_medium=referral">Fotis Fotopoulos</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>I think it’s not necessary to start this article with how important to write tests — you know it better than me. Also, I won’t go to dive into the theory of patterns and approaches. My goal is just to show you how easy and quickly you can start writing tests today (or right now if your Xcode project is opened) if you still haven’t started yet.</p><p>In order not to turn this article for the sake of the article itself, I will explain why I decided to prepare this small tutorial. Recently I conducted several tech interviews and noticed one curious thing. More and more engineers (even middle and seniors) neglect writing tests. I mean they were really great candidates, but they didn’t write tests at all. So, let this article be here just for those who need to start writing tests right now. Stay tuned.</p><h3>Prerequisites</h3><ol><li>You use at least Xcode 13</li><li>You have an existing iOS project</li><li>You have what to test of course</li></ol><h3>Create Unit Testing Bundle target</h3><p>In your existing project we need to create a new target — Unit Testing Bundle.</p><ol><li>Go to the project navigator and select the project where you want to add a target.</li><li>In the list of targets press +</li><li>Find Unit Testing Bundle</li><li>Press Next.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jsoC29Bh-Tp9RPrR3djCCQ.png" /><figcaption>Unit Testing Bundle</figcaption></figure><p>Name your product. Target to be tested should be your app or framework.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/753/1*OXrNn2h78GQh03uVGqykng.png" /><figcaption>Target to be Tested</figcaption></figure><p>If you don’t see you target in the list of targets, then we need to add it manually using manage targets option.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/1*UE5cqROt4phJYPzX1xt8RQ.png" /><figcaption>Additional step manually add a target</figcaption></figure><h3>Quick overview of a template</h3><p>When you open a file with test you will see XCTestCase template with a few methods. We need only testExample(). Let’s run it using rectangle on the left side of this method.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vboQ7zUD0hCHNGjTFfc6OQ.png" /><figcaption>Start test example</figcaption></figure><p>You will see a green checkmark. Your console log will be filled with information about when the test started, how a lot it takes, and so on. But let’s open Test navigator using CMD + 6. You will see the hierarchy of tests and the same green checkmarks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/291/1*pRmgWZk8RnH70GlFPTumDQ.png" /></figure><p>All this means — your tests are passed. Let’s make our own.</p><h3>Create your first Unit test</h3><p>You testExample method let’s add a following code and run it using triangle.</p><pre>func testExample() throws {<br>    let expr1 = &quot;expression 1&quot;<br>    let expr2 = expr1<br>    XCTAssertEqual(expr1, expr2, &quot;Expressions are equal&quot;)<br>}</pre><p>In this implementation, we used a specific assertion which is a part of XCTest framework. Let’s try another example. For that, you can create testExample2().</p><pre>func testExample2() throws {<br>    let expr1 = &quot;expression 1&quot;<br>    let expr2 = &quot;expression 2&quot;<br>    XCTAssertNotEqual(expr1, expr2, &quot;Expressions are not equal&quot;)<br>}</pre><p>Since we have several tests you would want to run them all. For that, you can use either Test navigator or the class declaration line.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/290/1*kKAGVOTeeib-6pYtGO93YA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/535/1*wgzh7fHmt8HOnbLbkg8zEg.png" /><figcaption>Start all tests in the case</figcaption></figure><p>As you can see XCTest framework provides a lot of asserts for all your general test cases:</p><pre>// Bool assert TRUE<br>XCTAssertTrue(expression, &quot;description&quot;)<br><br>// Bool assert FALSE<br>XCTAssertFalse(expression, &quot;description&quot;)<br><br>// Assert that expression is nil<br>XCTAssertNil(expression, &quot;description&quot;)<br><br>// Assert that expression is NOT nil<br>XCTAssertNotNil(expression, &quot;description&quot;)<br><br>// Assert that an expression is not nil and returns the unwrapped value<br>XCTUnwrap(expression, &quot;description&quot;)<br><br>// Asserts that two expressons have the same value or not<br>XCTAssertEqual(expr1, expr2, &quot;description&quot;)<br>XCTAssertNotEqual(expr1, expr2, &quot;description&quot;)<br><br>// expr1 &gt; expr2<br>XCTAssertGreaterThat(expr1, expr2, &quot;description&quot;)<br><br>// expr1 &lt; expr2<br>XCTAssertLessThat(expr1, expr2, &quot;description&quot;)<br><br>// expr1 &lt;= expr2<br>XCTAssertLessThanOrEqual(expr1, expr2, &quot;description&quot;)<br><br>// Asserts taht two expressions have the same value within a certain accuracy<br>XCTAssertEqualWithAccuracy(expr1, expr2, accuracy, &quot;description&quot;)<br><br>// Asserts that two floating-point values are equal within a specified accuracy.<br>XCTAssertEqual(49.2827, 49.2826, accuracy: 0.001)</pre><h3>Name your test properly</h3><p>Let’s say you have a model:</p><pre>struct SignUpFormModel {<br>    let firstName: String<br>    let lastName: String<br>    let email: String<br>    let password: String<br>    let repeatPassword: String<br>}</pre><p>Where you would have a method to validate email:</p><pre>extension SignUpFormModel {<br>    func isValidEmailFormat() -&gt; Bool {<br>        return email.contains(&quot;@&quot;) &amp;&amp; email.contains(&quot;.&quot;)<br>    }<br>}</pre><p>Ok, now it’s time to make a more professional test, but before that to make it more professional let’s think about naming. To name your tests properly is very important to get used to this from the very beginning. Let’s consider this name:</p><p><strong>testSignUpFormModel_WhenCreated_EmailShouldHaveValidFormat()</strong></p><p>If to break it you can see several parts:</p><p><strong>test&lt;<em>Subject&gt;</em>_&lt;<em>Condition Or State Change</em>&gt;_&lt;<em>Expected Result</em>&gt;()</strong></p><p>The name starts with <strong>test</strong> — it’s important to start from this word, otherwise, XCTest won’t recognize your methods (you won’t see this triangle icon). Other parts describe your test almost in detail.</p><p>So, if you will make yourself keep using this naming format then your colleagues sooner or later will say thank you in some time.</p><h3>Create Unit test using Arrange, Act, Assert (AAA) pattern</h3><p>Okay, we have an empty test, let’s fill it using a simple pattern called Arrange, Act, Assert.</p><ol><li><strong>Arrange</strong> — in other word we can call this step as <strong>Given</strong>, because you it’s where you arrange your test data.</li><li><strong>Act</strong> can be called <strong>When</strong> means where you will call your unit — testable method.</li><li><strong>Assert</strong> — or <strong>Then</strong>. The place where you will assert the result describing this in detail. By the way — don’t be lazy to describe your test well.</li></ol><pre>func testSignUpFormModel_WhenCreated_EmailShouldHaveValidFormat() {<br> <br>    // Arrange (GIVEN)<br>    let firstName = &quot;Maksim&quot;<br>    let lastName = &quot;Kalik&quot;<br>    let email = &quot;test@test.com&quot;<br>    let password = &quot;qwerty123&quot;<br>    let repeatPassword = &quot;qwerty123&quot;<br><br>    let signUpFormModel = SignUpFormModel(<br>        firstName: firstName,<br>        lastName: lastName,<br>        email: email,<br>        password: password,<br>        repeatPassword: repeatPassword<br>    )<br><br>    // Act (WHEN)<br>    let isEmailFormatValid = signUpFormModel.isValidEmailFormat()<br> <br>    // Assert (THEN)<br>    XCTAssertTrue(isEmailFormatValid, &quot;Provided valid email address does not have a valid format&quot;)<br>}</pre><p>Now you can run your first beautiful test and see how it beautifully fit into the list of other tests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/527/1*BqeBfxjKF_OPsD5_akWE3w.png" /></figure><h3>Wrapping up</h3><p>Your first unit test (at least using this described format) will be a great contribution to the project you are working on. If you want to see your project from another point of purpose to make it more robust then it’s a good time to start writing tests even if you are working in a startup. Just start from the simple and obvious part of your code then slowly take other more complicated units to test. Good luck!</p><h3>Links</h3><ul><li><a href="https://developer.apple.com/documentation/xcode/testing-your-apps-in-xcode">https://developer.apple.com/documentation/xcode/testing-your-apps-in-xcode</a></li><li><a href="https://developer.apple.com/documentation/xctest">https://developer.apple.com/documentation/xctest</a></li></ul><h3>Level Up Coding</h3><p>Thanks for being a part of our community! Before you go:</p><ul><li>👏 Clap for the story and follow the author 👉</li><li>📰 View more content in the <a href="https://levelup.gitconnected.com/?utm_source=pub&amp;utm_medium=post">Level Up Coding publication</a></li><li>🔔 Follow us: <a href="https://twitter.com/gitconnected">Twitter</a> | <a href="https://www.linkedin.com/company/gitconnected">LinkedIn</a> | <a href="https://newsletter.levelup.dev">Newsletter</a></li></ul><p>🚀👉 <a href="https://jobs.levelup.dev/talent/welcome?referral=true"><strong>Join the Level Up talent collective and find an amazing job</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4655b644c770" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/quick-start-writing-unit-tests-in-xcode-4655b644c770">Quick start writing Unit tests in Xcode</a> was originally published in <a href="https://levelup.gitconnected.com">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing address autocomplete using SwiftUI and MapKit]]></title>
            <link>https://levelup.gitconnected.com/implementing-address-autocomplete-using-swiftui-and-mapkit-c094d08cda24?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/c094d08cda24</guid>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mapkit]]></category>
            <category><![CDATA[swift-programming]]></category>
            <category><![CDATA[location]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Fri, 02 Dec 2022 17:49:44 GMT</pubDate>
            <atom:updated>2022-12-02T17:49:44.641Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*edRkUQrlKcOWSBZmnGT0vg.jpeg" /></figure><p>Surprisingly for myself, I realized that I have never implemented address autocompletion using only Apple technologies. Before, I had experience only with <a href="https://developers.google.com/maps/documentation/places/ios-sdk/autocomplete">Google Places SDK</a> and even with the not-so-well-known European <a href="http://geodatahub.eu">GeoDataHub</a>. I found it curious — to know how we can limit ourselves to using only SwiftUI and MapKit to solve a similar problem.</p><h3>TL;DR</h3><p><a href="https://github.com/maxkalik/address-autocomplete-swiftui">GitHub - maxkalik/address-autocomplete-swiftui: SwiftUI solution of Address autocomplete</a></p><h3>MapKit</h3><p>Before starting let’s briefly describe MapKit — a powerful framework invented by Apple. <a href="https://developer.apple.com/documentation/mapkit/">See documentation</a>:</p><blockquote>Display map or satellite imagery within your app, call out points of interest, and determine placemark information for map coordinates.</blockquote><p>The framework includes a bunch of useful things. I will list only what we are going to use: the Map itself (MapView for SwiftUI); MKCoordinateRegion — this model will allow showing a particular region on the map; MKAnnotation — another model with coordinates and other data for showing MapMarker on the map, and MKLocalSearch engine which we are going to use to get a list of completions from searchable address. To implement this we need to useMKLocalSearchCompleterDelegate and MKLocalSearchCompleter where we can get results — possible addresses.</p><h3>Project Overview</h3><p>The completed task should look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/918/1*xPhqFxM3gXkOHtpJzVQo1Q.gif" /><figcaption>iOS Application with address completion and map screens</figcaption></figure><p>We are not going to ask a user to get the current location of a device hence we will not touch plist configuration at all, I tried to simplify the project as much as possible. Let’s start with a couple of models. The goal is to see how many things we need to implement for this simple application.</p><p>So the structure is the following, MVVM with two screens: Content and Map.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/306/1*3UjUAGXJWIqkx920UMeXBA.png" /></figure><h3>Models</h3><p>Let’s start with a couple of models: AddressResult and Annotation. Both should be conformed to Identifiable protocol because we are going to use them in the collections.</p><pre>struct AddressResult: Identifiable {<br>    let id = UUID()<br>    let title: String<br>    let subtitle: String<br>}</pre><pre>struct AnnotationItem: Identifiable {<br>    let id = UUID()<br>    let latitude: Double<br>    let longitude: Double<br>    var coordinate: CLLocationCoordinate2D {<br>        CLLocationCoordinate2D(latitude: latitude, longitude: longitude)<br>    }<br>}</pre><p>AddressResult model will be used in the list of results. AnnotationItem collection we will set right in the Map component. But before dive in into this, let&#39;s set up our view models first.</p><h3>Content View Model</h3><p>Our iOS application is starting from ContentView because we are using SwiftUI, therefore, we need to prepare our first view model: ContentViewModel. The ContentViewModel will be observable because two things need to be published — the value from textfield (searchable text) and results array of AddressResult model.</p><pre>class ContentViewModel: NSObject, ObservableObject {<br>    <br>    @Published private(set) var results: Array&lt;AddressResult&gt; = []<br>    @Published var searchableText = &quot;&quot;<br><br>    private lazy var localSearchCompleter: MKLocalSearchCompleter = {<br>        let completer = MKLocalSearchCompleter()<br>        completer.delegate = self<br>        return completer<br>    }()<br>    <br>    func searchAddress(_ searchableText: String) {<br>        guard searchableText.isEmpty == false else { return }<br>        localSearchCompleter.queryFragment = searchableText<br>    }<br>}</pre><p>Some of you can notice searchableText is not marked as private(set). This is because this value is going to be bound. Also, as you can see, here is an initializedMKLocalSearchCompleter. This completer will help to get results using queryFragment. For that, we need to conform to the MKLocalSearchCompleterDelegate:</p><pre>extension ContentViewModel: MKLocalSearchCompleterDelegate {<br>    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {<br>        Task { @MainActor in<br>            results = completer.results.map {<br>                AddressResult(title: $0.title, subtitle: $0.subtitle)<br>            }<br>        }<br>    }<br>    <br>    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {<br>        print(error)<br>    }<br>}</pre><p>We are not going to handle errors at this time, so let me skip this part, we can just print a message. The methodcompleterDidUpdateResults where results are set from the completer’s results. Since our results are published, so we need to the main thread to avoid this warning:</p><pre>[SwiftUI] Publishing changes from within view updates is not allowed, this will cause undefined behavior</pre><h3>Map View Model</h3><p>The view models in this project are independent. We are going to observe them using @StateObject. The MapViewModel is pretty simple: two published variables and one method:</p><pre>class MapViewModel: ObservableObject {<br><br>    @Published var region = MKCoordinateRegion()<br>    @Published private(set) var annotationItems: [AnnotationItem] = []<br>    <br>    func getPlace(from address: AddressResult) {<br>        let request = MKLocalSearch.Request()<br>        let title = address.title<br>        let subTitle = address.subtitle<br>        <br>        request.naturalLanguageQuery = subTitle.contains(title)<br>        ? subTitle : title + &quot;, &quot; + subTitle<br>        <br>        Task {<br>            let response = try await MKLocalSearch(request: request).start()<br>            await MainActor.run {<br>                self.annotationItems = response.mapItems.map {<br>                    AnnotationItem(<br>                        latitude: $0.placemark.coordinate.latitude,<br>                        longitude: $0.placemark.coordinate.longitude<br>                    )<br>                }<br>                <br>                self.region = response.boundingRegion<br>            }<br>        }<br>    }<br>}</pre><p>Region value as MKCoordinateRegion will be bound with a view and AnnotationItem collection will be used by the Map view itself.</p><p>The most interesting part is this getPlace method with the argument as AddressResult. Briefly, this method converts our row address data into the coordinates using MKLocalSearch request!</p><p>Our address consists of a title and subtitle. In some cases, subtitle can contain the title, so we need to check first this part to prepare naturalLanguageQuery. Next, it is going to be a task with MKLocalSearch utility. From the response we need two things: MapItem collection and MKCoordinateRegion. From the first one, we will prepare AnnotationItem collection. The second one will be a map region that encloses the returned search results.</p><h3>Content View</h3><p>I really enjoy SwiftUI because you don’t need to spend so much time building user interfaces. It means you will spend really about 10 mins just to implement everything — 2 screens including a map.</p><p>Our task is simple, we need to prepare just a NavigationView with TextField for query and a List of results. Let’s take a look:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3uIbnYjBtgSKEBZ-AxKhVA.png" /><figcaption>Address autocompletion screen</figcaption></figure><p>The first screen is when the app is just launched and the textfield is immediately focused on appearing. Also, I couldn’t resist myself to add a clear button to the TextField. So, let’s implement all these views.</p><pre>struct ContentView: View {<br>    <br>    @StateObject var viewModel: ContentViewModel<br>    @FocusState private var isFocusedTextField: Bool<br>    <br>    var body: some View {<br>        NavigationView {<br>            VStack(alignment: .leading, spacing: 0) {<br><br>                TextField(&quot;Type address&quot;, text: $viewModel.searchableText)<br>                    .padding()<br>                    .autocorrectionDisabled()<br>                    .focused($isFocusedTextField)<br>                    .font(.title)<br>                    .onReceive(<br>                        viewModel.$searchableText.debounce(<br>                            for: .seconds(1),<br>                            scheduler: DispatchQueue.main<br>                        )<br>                    ) {<br>                        viewModel.searchAddress($0)<br>                    }<br>                    .background(Color.init(uiColor: .systemBackground))<br>                    .overlay {<br>                        ClearButton(text: $viewModel.searchableText)<br>                            .padding(.trailing)<br>                            .padding(.top, 8)<br>                    }<br>                    .onAppear {<br>                        isFocusedTextField = true<br>                    }<br><br>                List(self.viewModel.results) { address in<br>                    AddressRow(address: address)<br>                        .listRowBackground(backgroundColor)<br>                }<br>                .listStyle(.plain)<br>                .scrollContentBackground(.hidden)<br>            }<br>            .background(backgroundColor)<br>            .edgesIgnoringSafeArea(.bottom)<br>        }<br>    }<br>    <br>    var backgroundColor: Color = Color.init(uiColor: .systemGray6)<br>}</pre><p>I will not go through line by line because, in my opinion, this code is pretty simple. I just wanted to grab your attention on some parts.</p><ul><li>FocusState is used for focusing TextView on appear.</li><li>On receiving value in TextView, it’s needed to wait at least 1 second to get a searchable text except by querying each letter.</li><li>List view. I tested .scrollContentBackground(.hidden) which works only in iOS16 — it helps to hide a content background.</li><li>Using overlay in TextField we can add our ClearButton with bound searchable text (1. for checking if this text is not empty then the clear button will be rendered; 2. for clearing text from the ClearButton component).</li></ul><pre>struct ClearButton: View {<br>    <br>    @Binding var text: String<br>    <br>    var body: some View {<br>        if text.isEmpty == false {<br>            HStack {<br>                Spacer()<br>                Button {<br>                    text = &quot;&quot;<br>                } label: {<br>                    Image(systemName: &quot;multiply.circle.fill&quot;)<br>                        .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))<br>                }<br>                .foregroundColor(.secondary)<br>            }<br>        } else {<br>            EmptyView()<br>        }<br>    }<br>}</pre><p>The Address row is wrapped by NavigationLink with a destination in MapView.</p><pre>struct AddressRow: View {<br>    <br>    let address: AddressResult<br>    <br>    var body: some View {<br>        NavigationLink {<br>            MapView(address: address)<br>        } label: {<br>            VStack(alignment: .leading) {<br>                Text(address.title)<br>                Text(address.subtitle)<br>                    .font(.caption)<br>            }<br>        }<br>        .padding(.bottom, 2)<br>    }<br>}</pre><h3>Map View</h3><p>The second screen is a map itself and this view should show the correct location from the search result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*e11AGkmpwp4nasPGlVa38A.png" /><figcaption>Map View</figcaption></figure><p>It’s going to be only one view: Map View. As you remember getPlace method converts addresses into coordinates, so when the view appears we need to update the coordinates: region and annotation items. Eventually, in annotation content, we can prepare the MapMarker. Map view configured with coordinateRegion, annotationItems, and annotationContent arguments. When all data is ready in the view model, the Map view will be updated. As you can see coordinateRegion argument applies a binding value. annotationItems can be just identifiable with coordinate variable inside (latitude and longitude) — it’s used in annotationContent item in closure.</p><pre>struct MapView: View {<br>    <br>    @StateObject private var viewModel = MapViewModel()<br><br>    private let address: AddressResult<br>    <br>    init(address: AddressResult) {<br>        self.address = address<br>    }<br>    <br>    var body: some View {<br>        Map(<br>            coordinateRegion: $viewModel.region,<br>            annotationItems: viewModel.annotationItems,<br>            annotationContent: { item in<br>                MapMarker(coordinate: item.coordinate)<br>            }<br>        )<br>        .onAppear {<br>            self.viewModel.getPlace(from: address)<br>        }<br>        .edgesIgnoringSafeArea(.bottom)<br>    }<br>}</pre><p>Btw, when you build the project you might see some SwiftUI warnings: <strong>Publishing changes from within view updates is not allowed…— </strong>which means you probably updated coordinates during view rendering. If you think that you did absolutely right, your states are updated from onAppear, so it can be an issue in Map inside. You can check <a href="https://www.donnywals.com/xcode-14-publishing-changes-from-within-view-updates-is-not-allowed-this-will-cause-undefined-behavior/">this article</a> about this issue by Donney Wals.</p><h3>Wrapping up</h3><p>Countless applications use Maps. But still few of them use a combination of SwiftUI and MapKit. I was curious to see what we can build if using only Apple technologies. How many lines of code are needed to implement this useful feature. Each year in WWDC you can notice that Apple engineers update MapKit rigorously adding new features especially if it comes to SwiftUI. I used only SwiftUI building Address with an autocompletion feature with Map because I truly believe that this framework is our closest future and UIKit soon will be secondary technology.</p><p><strong>Want to Connect?</strong><br>Don’t hesitate to follow me on <a href="http://twitter.com/maxkalik">Twitter</a>. I appreciate any suggestions or comments regarding my research and articles.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c094d08cda24" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/implementing-address-autocomplete-using-swiftui-and-mapkit-c094d08cda24">Implementing address autocomplete using SwiftUI and MapKit</a> was originally published in <a href="https://levelup.gitconnected.com">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Integrating SwiftUI into UIKit project]]></title>
            <link>https://medium.com/geekculture/integrating-swiftui-into-uikit-project-a1051e432041?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/a1051e432041</guid>
            <category><![CDATA[swift-programming]]></category>
            <category><![CDATA[uikit]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[swiftui]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Mon, 17 Oct 2022 19:01:37 GMT</pubDate>
            <atom:updated>2022-10-18T13:07:53.072Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5-QlS6HrrJgHkKVd4L9IhA.jpeg" /><figcaption>SwiftUI in UIKit</figcaption></figure><p>I’m working on a project where we started classically — UIKit without storyboards, MVVM + Coordinator design pattern. It can be familiar to you especially if you are working for a start-up. All our development process is going really quickly. Sometimes my typical working day I would compare it with a middle distance running. Business waits for new features all the time. At some moment we figured out that we spend a lot of time building new UI features, but our features are not very custom. We waste our time on constraining views, configuring, and conforming to delegates. You know the difference between List and TableView setting up.</p><p>One of the best choices was trying SwiftUI. Using this framework I personally wanted to solve the main problem — to deliver features faster, moving screens and views from one place to another without prejudicing the existing codebase.</p><p>In the beginning, it was a little bit scary to use SwiftUI for production but eventually, we got a pretty result: we doubled the speed of building our features. This means we free up time for: focusing on logic, unit testing, and UI testing.</p><p>So, based on my experience, I would like to share how quickly and easily you can start building new Scenes (Screens) in the MVVM+C design pattern in your UIKit project.</p><h3>TL;DR</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*gEso0pwq0cnh31lmDLlAFw.gif" /><figcaption>MVVM+C UIKit project with SwiftUI SignIn screen</figcaption></figure><p><a href="https://github.com/maxkalik/uikit-swift-to-swiftui">GitHub - maxkalik/uikit-swift-to-swiftui: MVVM+C UIKit project with SwiftUI</a></p><h3>Exploring the project</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jflYSLRebUIFbgC2DlCjEw.png" /><figcaption>Home Screen (UIKit)</figcaption></figure><p>Given: UIKit application with a screen — let’s call it the Home screen with orange background. If we run an application, it starts some custom activity indicator. When UI is ready to use we only have one button — Sign In.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*noIEtR6UTPJT0hu-saRgRQ.png" /><figcaption>SignIn Screen (SwiftUI)</figcaption></figure><p>Let’s imagine you got a task. You are asked by some Product Manager or your Team Leader to implement a new feature — a new SignIn Screen. And it should work in this way: On tapping on the Sign In on Home Screen we should present the SignIn screen. We have to add some logic there — to check textfield if it is empty we need to present an alert with an error message.</p><p>If all is done by a user correctly— the textfield is filled by name then after some loading process we need to segue to the previous orange Home screen with the updated title of the button and a label above.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uwUmGTOWREIaddT1B2YhXQ.png" /><figcaption>Home Screen (UIKit)</figcaption></figure><p>Ok. It doesn’t seem like a complicated task and you as a developer decided to implement this whole screen using SwiftUI. Good decision. But, let’s figure out how we are going to integrate our SwiftUI staff.</p><h3>Looking from an architectural perspective</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UhMc1NMLTpf1r-Bny_KimA.gif" /><figcaption>App Coordinator</figcaption></figure><p>Firstly, let’s take a look at the project from the coordinator’s perspective. The first start method prepares Home Screen. As you can see for preparing of this module is needed View Model and View Controller. Then this View Controller is just pushed to the stack of the Navigation View Controller. It’s a pretty common preparation process for the modules in iOS Development when using MVVM + Coordinator design pattern.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2bcfdb847b7c4f4771a1df52076c3c9e/href">https://medium.com/media/2bcfdb847b7c4f4771a1df52076c3c9e/href</a></iframe><p><strong>View Model </strong>— HomeViewModel includes a couple of delegates — ViewDelegate and CoordinatorDelegate. ViewDelegate is needed for controlling views on the screen in a particular scene. In this case, it’s a Home Screen, and the view model delegates the responsibility of showing and hiding an activity indicator on the screen. <strong>CoordinatorDelegate</strong> delegates the responsibility of showing our new screen — The SignIn screen. But how are we going to implement this part if we want to use SwiftUI?</p><h3>BaseViewController</h3><p>Ok. If you know the project like the back of your hand you can just start.<br>But let’s assume at this point you don’t know exactly about the project structure. You are working in a team. You respect each of your colleagues and their work and you don’t want to break the existing structure just because you want to integrate your SwiftUI staff.</p><p>So let’s take a look at the view hierarchy. Most likely you will see something like this — The ViewController is inherited by BaseViewControlleror something similar. It’s needed for storing some base view logic (not business logic). In our case, BaseViewController has methods for showing and hiding the Activity indicator.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/deff5b24ff7fdf322bfa8632bd3a3cc2/href">https://medium.com/media/deff5b24ff7fdf322bfa8632bd3a3cc2/href</a></iframe><p>You most likely already know how to use SwiftUI View in UIKit and why we needUIHostingController. Also, I bet you understand that under the hood UIHostingController is as usualUIViewController as well. And you will be right to push this view controller to the UINavigationController because it freely accepts this controller in its stack.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7eBJY2A8CoxzqyApxqlPUw.gif" /><figcaption>UIHostingController in UINavigationController stack</figcaption></figure><p>But we have a requirement — we have to use this exact ProgressView without repeating ourselves. Of course this Progress view we can just reimplement this in SwiftUI but what about reusability? It’s time to remember the <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> principle and all warnings in your head about this. As I said before — we don’t want to break the existing structure.</p><h3>BaseHostingController</h3><p>The decent solution is to prepare the sameBaseHostingController as BaseViewController inherited from UIHostingController and somehow from there to use a couple of methods that start and stop the activity indicator.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vLYyOTzfva6m54npr31CLA.png" /></figure><p>As you know UIHostingController is theUIViewController. This means we can wrap our two base controllers using a protocol.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/207a78acdad39b5ff5402dadb9e655fc/href">https://medium.com/media/207a78acdad39b5ff5402dadb9e655fc/href</a></iframe><p>This BaseController protocol will be withProgressView and the same base methods as in BaseViewController. And here we go. Conforming this protocol to both — BaseViewController and BaseHostingController we get an opportunity of using the same ProgressView from UIKit and SwiftUI modules.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fb2090dbc1cf8008755feab75a6b3c52/href">https://medium.com/media/fb2090dbc1cf8008755feab75a6b3c52/href</a></iframe><p>Having BaseHostingController we can use it in the same way as BaseViewController especially if it comes to View Delegates. We just easily control our loading indicator from this Hosting Controller without breaking the structure of the project.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e5d0684ad3ce36e5abde39ab358bdc8b/href">https://medium.com/media/e5d0684ad3ce36e5abde39ab358bdc8b/href</a></iframe><p>Even the coordinator didn’t seem to be changed. Only one beautiful method appeared: showSignIn()</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/aa6f3bf3161f40a3fcbce0b97a8a8dc3/href">https://medium.com/media/aa6f3bf3161f40a3fcbce0b97a8a8dc3/href</a></iframe><p>Look, how it’s beautifully fitted to the existing MVVM+Coordinator design pattern. AppCoordinators got clean and easy-to-read methods with the same steps for preparing the module to be shown. ViewDelegate from SignInHostingController works in the same way as with BaseViewController.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KrFq-7aB6hIM5_9ta7eF5A.png" /><figcaption>AppCoordinator with SignIn Module</figcaption></figure><h3>Wrapping Up</h3><p>If you are working in a team your code should be predictable and readable for everyone, even though your colleagues don’t know about SwiftUI, but anyway, they will understand how to maintain this structure.</p><p>In my personal experience, I use this approach pretty successfully. Almost 70% of the screens of our project today are SwiftUI with UIHostingController. and I guess it’s going to be 100% but then it will be another story about how completely transit your UIKit app into SwiftUI :)</p><h3>Resources</h3><p>GitHub repository: <a href="https://github.com/maxkalik/uikit-swift-to-swiftui">https://github.com/maxkalik/uikit-swift-to-swiftui</a></p><p>UIHostingController: <a href="https://developer.apple.com/documentation/swiftui/uihostingcontroller">https://developer.apple.com/documentation/swiftui/uihostingcontroller</a></p><pre><strong>Want to Connect?</strong></pre><pre>Don&#39;t hesitate to follow me on <a href="https://twitter.com/maxkalik">Twitter</a>.</pre><pre>I appreciate any suggestions or comments regarding my research and articles.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a1051e432041" width="1" height="1" alt=""><hr><p><a href="https://medium.com/geekculture/integrating-swiftui-into-uikit-project-a1051e432041">Integrating SwiftUI into UIKit project</a> was originally published in <a href="https://medium.com/geekculture">Geek Culture</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting Up iOS Framework for Unity]]></title>
            <link>https://medium.com/better-programming/setting-up-ios-framework-for-unity-9ef4e577db89?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/9ef4e577db89</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[unity]]></category>
            <category><![CDATA[game-development]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Tue, 04 Oct 2022 17:35:57 GMT</pubDate>
            <atom:updated>2022-10-04T18:12:16.814Z</atom:updated>
            <content:encoded><![CDATA[<h4>From Swift to C#</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8SrQztkXGb4PmszPFxHU3A.png" /><figcaption>image by author</figcaption></figure><h3>Part 1. Launch UIViewController from Unity</h3><p>I won’t waste your time on a long introduction about the technologies that I will describe here. You most likely already work as an iOS Engineer or Game Developer, which means you probably have some questions regarding the topic of this article. So, let’s get started.</p><p>This article is split into two parts. In the first part, you will learn how to launch a simple UIViewController from Unity. We will force C# to understand Swift. In the second part, we will try to expand the usage of Swift in Unity and explore the limitations.</p><p>All right, here we go.</p><h4>Prerequisites</h4><ul><li>macOS Monterey</li><li>Xcode 13 (or 14)</li><li>Unity LTS 2021.3</li><li>iOS Device (with at least iOS14 or higher)</li></ul><h4>iOS Framework</h4><p>Open Xcode and create a new Swift project — framework.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/798/1*QI4bBrCqUvFX4CjVXqljhQ.png" /><figcaption>Swift project — framework</figcaption></figure><p>The name of the project is up to you, I named it SwiftCodeKit. It’s going to be a simple framework with one view controller. We need only three files:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/266/1*ToYj1ML9HhHewa8shzYP5w.png" /></figure><p>In SwiftCodeKitViewController, let’s create a simple UIViewController — with a button at the center of the view.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/22e1816ded896c0a5d4e2fa4eb8688d5/href">https://medium.com/media/22e1816ded896c0a5d4e2fa4eb8688d5/href</a></iframe><p>In SwiftCodeKit.swift we will collect all our public methods. Having this public class will allow you to test your framework in iOS projects. The first one will bestart(). This method presents our SwiftCodeKitViewController.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f806e8b48b81241cf3fd3e0c3ccb2067/href">https://medium.com/media/f806e8b48b81241cf3fd3e0c3ccb2067/href</a></iframe><p>The third file SwiftCodeKitBridging.swift will contain a list of public exposed methods with C declaration notation @_cdecl. These methods should be stored only in a non-local scope. First, let’s create a method: startSwiftCodeKitController().</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/00db76fb81cd63949e86ee157055d3f4/href">https://medium.com/media/00db76fb81cd63949e86ee157055d3f4/href</a></iframe><p>The attribute @_cdecl is not documented. For now, you only have to know that this attribute exposes a Swift function to C. Further, we will experiment a little bit with this attribute and will figure out the limitation.</p><h4>Building framework</h4><p>In your Xcode project status of the toolbar choose Any iOS Device (arm64) destination. Yes, we are going to build only for iOS devices, not for Simulators. Now, you need only tap Command + B.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/546/1*OJbRYnk6XqGxolOtl4Zkdg.png" /><figcaption>Any iOS device (arm64) destination</figcaption></figure><p>To get our baked framework we need to go to Derived data and take it from there. You should open the Derived data folder from Xcode: Go to Xcode Preferences (cmd + ,), then open Locations, and tap on the arrow at the end of the Derived data path.</p><p>In the Derived data folder, find your project and use this path:<br>Build / Products / Debug-iphoneos / SwiftCodeKit.framework</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/977/1*XMJyFb54p9IcW0WJuPZuzg.png" /><figcaption>Derived data with SwiftCodeKit project</figcaption></figure><h4>Unity project</h4><p>It’s time to set up our Unity project. Open Unity and create a project; I called it UnityClientProject.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*loQzjOXaEK4KS140cNR9WQ.png" /><figcaption>Creating a Unity project</figcaption></figure><p>In the Unity project, you will find folder assets. Drag your SwiftCodeKit.framework and drop it to the Assets folder.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/388/1*ObbA-hi7Y4vCExsQt3mxEg.png" /><figcaption>SwiftCodeKit.framework in Assets / iOS folder</figcaption></figure><p>Regarding UI in Unity, we are going to make a simple button. This button will summon our SwiftCodeKit View Controller. Right-click on Hierarchy, choose UI, and then Button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7pVDgtJLgPKK0of8SGibCw.png" /><figcaption>Button in Unity project</figcaption></figure><p>Also, in the hierarchy, we have to make another object for our button. I call it ButtonController. In the inspector of the ButtonController, add a component / new script and name it ButtonControllerScript.cs. Open this file.</p><p>Actually, we need only one public method: OnPressButton(). But before that, we have to import System.Runtime.InteropServices. This will help us to recognize our methods from the SwiftCodeKit.framework. If you remember, we have only one public function: startSwiftCodeKitController(), so let’s import it using DllImport.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6ecb5540c2183d05ecc1f8701607a6a6/href">https://medium.com/media/6ecb5540c2183d05ecc1f8701607a6a6/href</a></iframe><h4>Building Unity project</h4><p>We are ready to build our iOS project from Unity. Open File / Build Settings and change the Platform to iOS. Then press Build.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/647/1*CFDFXUszy3HynzTqYtcgBA.png" /><figcaption>Unity build settings</figcaption></figure><p>After pressing build it will automatically run an Xcode project. This project is generated by Unity and there you will see only one Framework called UnityFramework.</p><p>A couple of problems you can stumble upon at this moment: the first one — you have to sign this project and the second one is the bitcode error — this can only happen if you use Xcode 14. It’s not so big deal. Let’s just disable bitcode in this project, and run it again.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*SLNfEcuTtdO4V7t18_xTMg.png" /><figcaption>Xcode Build settings</figcaption></figure><p>On your iPhone, you will see your Unity app. The first blue screen with a button — this is the Unity part and the orange presented screen is your framework!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*RE3qaXVeyditIrOkUsOG_Q.gif" /></figure><h3>Wrapping Up</h3><p>Let’s quickly go through what we did:</p><ol><li>Prepared a Framework with a public method for iOS and for C# using @_cdecl attribute.</li><li>Created a simple Unity project with a button at the center.</li><li>Added our Framework to the assets of the Unity project.</li><li>Prepared the ButtonController object with C# script where we imported our method using System.Runtime.InteropServices</li><li>Built an iOS project from Unity.</li></ol><p>As you can see these are basic steps that give you an opportunity to understand how it works. In part two (see below) we will try to make this project closer to real usage. We will experiment with @_cdecl attribute and explore the limitations.</p><h3>Part 2. Exposing Swift Functions to C# in Unity</h3><p>This article is written in two parts. In the second part, we will experiment with @_cdeclto understand the limitations of interoperability of Swift and C#. Let’s get started.</p><p>If you read the first part of this article you should remember the SwiftCodeKitBridging.swift file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/00db76fb81cd63949e86ee157055d3f4/href">https://medium.com/media/00db76fb81cd63949e86ee157055d3f4/href</a></iframe><p>In this file, we store a list of public methods for exposing, using @_cdecl attribute. As you can see we already know how to call some functions from C#. But obviously, it’s not so usable because in real iOS applications we use functions with parameters, our functions return some values, moreover, we use closures, delegates, and so on. How do expose this code to the Unity project?</p><p>Let’s resume what we are gonna try today:</p><ul><li>Function with parameters</li><li>Function returns value</li><li>Function with closure</li></ul><h4>iOS framework update</h4><p>I prepared a small update of our SwiftCodeKitViewController.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e2aa9ca44a11b3e38c8064061736c7d9/href">https://medium.com/media/e2aa9ca44a11b3e38c8064061736c7d9/href</a></iframe><p>I added two more views: UILabel and UIStepper. Using stepper control we can increment and decrement a value and set it to the label.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5JCI5QxABsJVJmGEqUDeuw.png" /><figcaption>SiwftCodeKit UI</figcaption></figure><p>It will be enough to experiment with real UI. Let’s take a look at our public class:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/78a7251d32b280876795e4c761042d4f/href">https://medium.com/media/78a7251d32b280876795e4c761042d4f/href</a></iframe><p>As you can see this class was extended with methods that we are going to use in C#. The most interesting part is the Delegates — they are represented by closures. Let’s connect all these methods to our UI. In viewDidLoad, add our first delegate method SwiftCodeKit.swiftCodeKitDidStart?().</p><p>Here’s other methods that can be used in extensions:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8f9c4627185521d6d3bcb3ea4e17b339/href">https://medium.com/media/8f9c4627185521d6d3bcb3ea4e17b339/href</a></iframe><h4>iOS framework bridging methods</h4><p>All our public methods should be recognized from C#. I suggest walking through them one by one.</p><p>Here’s the function with parameters:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bfef9ce20307a6b6cfff56e839eda7ae/href">https://medium.com/media/bfef9ce20307a6b6cfff56e839eda7ae/href</a></iframe><p>Function returns a value (Double):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f9c59fad652e237e5e86d4b7f17896d0/href">https://medium.com/media/f9c59fad652e237e5e86d4b7f17896d0/href</a></iframe><p>The function returns a string. Here you can notice a difference. Specifically for this kind of data, we need to allocate memory and pass our string to strdup() — this method will duplicate the string for UnsafePointer().</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f49d641673b22bfb6343ccf421b8a9fa/href">https://medium.com/media/f49d641673b22bfb6343ccf421b8a9fa/href</a></iframe><p>OK, now Delegates. As you can see they are functions with escaping closures. But besides escaping closure has some interesting add-on called@convention(c)</p><p>There is a short description from <a href="https://docs.swift.org">docs.swift.org</a>:</p><blockquote>Apply this attribute to the type of a function to indicate its calling conventions. The <em>c</em> argument indicates a C function reference. The function value carries no context and uses the C calling convention.</blockquote><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1e1244e75b25a81a25f24c29631dcb91/href">https://medium.com/media/1e1244e75b25a81a25f24c29631dcb91/href</a></iframe><p>OK, let’s build our updated framework, get this build, and drop it into the Unity project assets folder (see part 1 for how to do this).</p><h4>Unity ButtonControllerScript update</h4><p>Our C# script file has only one imported function for now: startSwiftCodeKitController, so it’s time to add the rest of them. We will walk through the same way as we did with Swift methods:</p><p>Here’s the function with parameters:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b54ba41a81bb98331d0c685883432d09/href">https://medium.com/media/b54ba41a81bb98331d0c685883432d09/href</a></iframe><p>This function returns a value, as shown below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b54ba41a81bb98331d0c685883432d09/href">https://medium.com/media/b54ba41a81bb98331d0c685883432d09/href</a></iframe><p>And Delegates using AOT:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/93448712e0b7c743f04a7cdaef46b808/href">https://medium.com/media/93448712e0b7c743f04a7cdaef46b808/href</a></iframe><p>The last thing we are going to do is to update OnPressButton() method where we use all methods from our Swift framework.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/36a42d4b58ee1bb2ef94324c350d337e/href">https://medium.com/media/36a42d4b58ee1bb2ef94324c350d337e/href</a></iframe><p>Directly from Unity we configured our iOS Controller and used our three delegates. Now, it’s time to build our project. If you followed me correctly and all your methods were recognizable by Unity, your result will look like the following:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*UFic8NZzSNY_ygXWKKSB-Q.gif" /><figcaption>Unity iOS project launched from Unity</figcaption></figure><p>The console of Xcode should print something like this:</p><pre>SwiftCodeKit did start<br>SwiftCodeKit get value: 10<br>SwiftCodeKit get version: Swift Code Kit 0.0.1<br>SwiftCodeKit value did change. Value: 11<br>SwiftCodeKit value did change. Value: 12<br>SwiftCodeKit value did change. Value: 13<br>SwiftCodeKit did finish</pre><h3>Wrapping Up</h3><p>I always ask the same question every time I finalize a task: How can I make this better? I think this task could be improved endlessly. I’m not an expert with Unity (if you know better, please comment), but I know that we can even build DLL Library with all our functions and eventually just import this build directly to the script.</p><p>Also, as an iOS developer using UnsafePointer keep in mind that this memory should be released.</p><p>Thanks for reading.</p><h3>Source Code</h3><p><a href="https://github.com/maxkalik/swift-in-unity">GitHub - maxkalik/swift-in-unity: iOS Framework for Unity</a></p><pre><strong>Want to Connect?</strong></pre><pre>Don&#39;t hesitate to follow me on <a href="https://twitter.com/maxkalik">Twitter</a>.</pre><pre>I appreciate any suggestions or comments regarding my research and articles.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9ef4e577db89" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/setting-up-ios-framework-for-unity-9ef4e577db89">Setting Up iOS Framework for Unity</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fixing Swift Actors and Delegate Error on Xcode 14]]></title>
            <link>https://medium.com/better-programming/fixing-swift-actors-and-delegate-error-on-xcode-14-87f933c9bbcc?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/87f933c9bbcc</guid>
            <category><![CDATA[xcode-14]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Wed, 14 Sep 2022 03:41:33 GMT</pubDate>
            <atom:updated>2022-09-16T12:12:01.392Z</atom:updated>
            <content:encoded><![CDATA[<h4>If you’ve updated Xcode to version 14 you might get an error about mutating actor-isolated property in your project. Let’s fix it.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KRdKbi0vkBJnkBw62usPgA.jpeg" /></figure><p>I’m not going to tell you again about Actors from scratch, what is it and how it works. Since Apple has introduced Swift 5.5 there are tons of cool articles about them. If you still don’t use actors then it’s time to take a look. I bet it will come a time when you will change some of your classes to actors.</p><p>This particular article is about a specific potential issue while using actors with delegates after updating Xcode with version 14. It sounds a bit trivial but I think it could be useful. Let’s get started.</p><h3>Before Xcode 14</h3><p>Let’s assume we have class SomeClass with a delegate:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/dfc374d40d6f457a6574582f806a5229/href">https://medium.com/media/dfc374d40d6f457a6574582f806a5229/href</a></iframe><p>And SomeViewModel — in there we going to use our SomeClass:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/54315176681afedb829e6fab3086eb0e/href">https://medium.com/media/54315176681afedb829e6fab3086eb0e/href</a></iframe><p>As you can see, we did a primitive manipulation by setting delegate to self and conforming view model with SomeClassDelegate.So, let’s print out some results from it.</p><pre><strong>let someViewModel = SomeViewModel()<br>someViewModel.start()</strong></pre><pre>// Printed: some class did start</pre><p>Ok. Now it’s time to refactor our class with an actor. How it could be (and maybe you have done it already in your project in this similar way):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9d4c87c69e8643b364adef35c26d5f26/href">https://medium.com/media/9d4c87c69e8643b364adef35c26d5f26/href</a></iframe><p>In the same way, let’s use it in our view model. Of course, since we have a deal with an actor we need Tasks.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e293a7a421ec789a083ebf6cbb8400eb/href">https://medium.com/media/e293a7a421ec789a083ebf6cbb8400eb/href</a></iframe><h3>After</h3><p>If you are still using Xcode 13 you probably won’t see any warnings in this implementation. But if you have updated already Xcode with version 14, most likely you will get this error:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/898/1*5_ojZgc4WVi5AnaC-Jb1uw.jpeg" /><figcaption>Actor-isolated property ‘delegate’ can not be mutated from a non-isolated context</figcaption></figure><p>At first sight, it’s confusing, especially: “No async operations occur within await expression” when the delegate is an actor’s property. Also, it could be a question — if we await to change a property from outside, why do we see this message about isolation?</p><p>In reality, the errors both make sense and look weird a little bit. As we know — the base idea of the actors is to isolate properties. It means you cannot just update property from outside. So, probably, these errors teach us how to use actors properly and the updating of the property delegate should happen only inside of the actor SomeActor.</p><p>A reasonable solution is to add an additional method. This method will set the delegate with a new value.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bbccf8d2c0684575d8855d039866c2f7/href">https://medium.com/media/bbccf8d2c0684575d8855d039866c2f7/href</a></iframe><p>Inside of the Task in view model replace this:</p><pre>// await someActor.delegate = self<br><strong>await someActor.setDelegate(self)</strong></pre><p>Expected result:</p><pre><strong>let someViewModel = SomeViewModel()<br>someViewModel.start()</strong></pre><pre>// Printed: some actor did start</pre><h3>Wrapping up</h3><p>I wouldn’t write this article if the error wasn’t so unexpected. I see already some questions appear in Stackoverflow or in Swift <a href="https://forums.swift.org/t/used-to-work-xcode-14b4-now-doesnt-actor-isolated-property-lastloggedtimestamp-can-not-be-mutated-from-a-sendable-closure/59262">discussions</a> and I asked the same. If this actor-isolated property error is only about teaching us how to use actors properly, why can’t it be only a warning?</p><p>Thank you for reading.</p><pre><strong>Want to Connect?</strong></pre><pre>Don&#39;t hesitate to follow me on <a href="https://twitter.com/maxkalik">Twitter</a>.</pre><pre>I appreciate any suggestions or comments regarding my research and articles.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=87f933c9bbcc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/fixing-swift-actors-and-delegate-error-on-xcode-14-87f933c9bbcc">Fixing Swift Actors and Delegate Error on Xcode 14</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Updating SwiftUI Views From Objective-C Using MVVM]]></title>
            <link>https://medium.com/better-programming/updating-swiftui-views-from-objective-c-mvvm-9c4f38d6d9c8?source=rss-58757e2cf638------2</link>
            <guid isPermaLink="false">https://medium.com/p/9c4f38d6d9c8</guid>
            <category><![CDATA[objective-c]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Max Kalik]]></dc:creator>
            <pubDate>Tue, 06 Sep 2022 13:55:15 GMT</pubDate>
            <atom:updated>2022-09-12T17:41:53.988Z</atom:updated>
            <content:encoded><![CDATA[<h4>Exploring a nonstandard situation</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6MIYNYcm-Dq7MzzC0bECGA.jpeg" /></figure><p>On the internet, in the Apple documentation, and here on Medium, there is a ton of information about SwiftUI, how to use it from UIKit, and vice versa. But today, let’s consider a not-so-common case in real life that’s useful to reflect on non-standard situations in iOS development.</p><p>Task: Show SwiftUI view from Objective-C codebase. Moreover, when an Objective-C model should be observable, the respective SwiftUI should be updated.</p><p>Result: When you press the Sign In button from Home, View Controller should be pushed a SwiftUI View with a Title, Text Field, and a button Sign In. On edit TextView and on Press on the Sign In button, the title label should be updated.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*h1kZCuij8faEtLW77QLTTQ.gif" /><figcaption>The result</figcaption></figure><h3>TL;DR</h3><p><a href="https://github.com/maxkalik/uikit-objc-to-swiftui">GitHub - maxkalik/uikit-objc-to-swiftui: Transition from UIKit Objective-C to SwiftUI</a></p><h3>Start-Up MVVM+C Project</h3><p>Clone the project from the link above. The repository has the start folder, which has an initial project that we are going to use.</p><p>This iOS App was developed using MVVM + Coordinator. I agree it’s already a bit complicated to use this architecture to explain how to show SwitUI views and update them from Objective-C, but I would say, it’s more like a real situation where we need to stick to a particular pattern. No worries, we have only one screen at this point.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*itjLKbpiFTxxAcCYNVu8LQ.jpeg" /><figcaption>Home Screen</figcaption></figure><p>The structure of the project consists of several entities: AppCoordinator, Protocols, and MVVM Modules. AppCoordinator has only a general App Coordinator. For the protocols, I decided to store there all protocols we will use across the project. And the Modules group has the screens; for now, it’s only one home screen with a black Sign In button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OobCU9sic7ZwTZMZWFVtXw.jpeg" /><figcaption>Initial project structure</figcaption></figure><p>The Sign In button uses a typical coordinator delegate where from the coordinator we have to prepare our solution of showing SwiftUI Login View. But before implementing SwiftUI View, let’s complicate our task a little bit more. Let’s assume we already have LoginViewModel implemented in Objective-C as well and we are not allowed to refactor it into Swift. Only LoginView should be implemented in SwiftUI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*50YlRBK2wyAhiQpiuqx0IQ.jpeg" /><figcaption>Project structure with LoginViewModel</figcaption></figure><p>Okay. Let’s take a look at LoginViewModel. We have three NSString properties.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/df067b8eda7cff538c7296fc325b87e6/href">https://medium.com/media/df067b8eda7cff538c7296fc325b87e6/href</a></iframe><p>The name property should store from SwiftUI TextView. The title should be changed on TextView edit. The buttonTitle will be “Sign In,” and the method buttonTap should change the title value. Nothing crazy.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bacc34e5189bb63320bd1cfe3cddc64a/href">https://medium.com/media/bacc34e5189bb63320bd1cfe3cddc64a/href</a></iframe><h3>Bridging Swift Files</h3><p>It’s time to start adding Swift files to our Objective-C project.</p><p>Go to File ➝ New ➝ File… ➝ SwiftUI View. This will keep the project structure better when saving this file to the Modules/Login/Views/ folder.</p><p>After this, you will be asked about creating Bridging Header, and yes, we need this. Press create.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/1*G4YWUmf4GG8ktSv3tMXDng.png" /><figcaption>Create Bridging Header</figcaption></figure><p>You will see two files: LoginViewModel.swift and UIKitObjCToSwiftUI-Bridging-Header.h in the Views group.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/804/1*_XF3X7sQObSD-CFjTuzaww.jpeg" /><figcaption>Login module structure</figcaption></figure><p>Initially bridging header after creation will be empty. But we already know that we are going to use LoginViewModel.h. So we need to import this header.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/803/1*2SHmCv3lU5XaD2iIvIlEYg.jpeg" /><figcaption>Bridging Header file</figcaption></figure><p>The last step of bridging is to make our Objective-C files see Swift files. So, we need another import for that, and we will do this in AppCoordinator.m. But a bit later.</p><h3>SwiftUI view and Coordinator Interface</h3><p>At this moment we don’t use any view models. We just need to show a pure SwiftUI view with dummy data.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9f5bdc545c52c3a441cd312d379dc84d/href">https://medium.com/media/9f5bdc545c52c3a441cd312d379dc84d/href</a></iframe><p>To show the SwiftUI view, we need to use UIHostingController which will play the role of UIViewController in Objective-C world, so for that, we need to create an “interface” to wire up AppCoordinator and SwiftUI views.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ba610e953128f78cc614df0b66e17d8/href">https://medium.com/media/7ba610e953128f78cc614df0b66e17d8/href</a></iframe><p>It’s time to bridge to make Objective-C see Swift. Let’s import our Bridge header right in the AppCoordinator.m.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/803/1*zV80jBh95DI6LyixTRUF2Q.jpeg" /><figcaption>Importing UIKitObjCToSwitUI-Swift header</figcaption></figure><p>By the way, don’t be scared. You will notice that autocomplete is not working for this header. The last part of this file should be -Swift.h. If you are not sure about that, check:</p><p>Build Settings ➝ Swift Compiler — General ➝ Objective-C Generated Interface Header Name</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/1*oWwxTlumz8q0NdgOHGs2qw.png" /><figcaption>Objective-C Generated Interface Header Name</figcaption></figure><p>Let’s use all these pieces in the AppCoordinator so the (void)showLogin method will be updated:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f9689efa7e7b99e8fca0ae38a787a32a/href">https://medium.com/media/f9689efa7e7b99e8fca0ae38a787a32a/href</a></iframe><p>Run build. If you have done it all correctly, you will see a SwiftUI view after tapping on the Sign In button from the Home View Controller.</p><h3>Objective-C ViewModel and ViewModel Interface</h3><p>We need to make our view model visible in the SwiftUI view, and the title of the Login View should be updated. An Implementation will be a little bit similar to Coordinator interface but with some additional enhancements.</p><p>To unify our “observable” solution, we need a couple of protocols: ObservableNSObject and ObservableNSObjectDelegate. Here’s the code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6dbd402909dcd24322afc274ec4fec96/href">https://medium.com/media/6dbd402909dcd24322afc274ec4fec96/href</a></iframe><p>The trick is to use these protocols for conformance of all Objective-C modules, which will be used in SwiftUI and the viewModelDidUpdate method. They will do this for each update of properties or methods in the view model.</p><pre>@interface LoginViewModel : NSObject &lt;ObservableNSObject&gt;</pre><p>In LoginViewModel.m, update these methods:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/739573886996f53c403b73d70bf10c8f/href">https://medium.com/media/739573886996f53c403b73d70bf10c8f/href</a></iframe><p>Since we prepared the view model to be updated, let’s create our view model interface, as shown below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f66c46909005d9fff4faac209db7b7f0/href">https://medium.com/media/f66c46909005d9fff4faac209db7b7f0/href</a></iframe><p>The main trick is to update the published viewModel using ObservableNSObjectDelegate. Let’s update our LoginView using this view model interface. I called it observable.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/927f8e4c93cf7ebe06739631f2a82d1a/href">https://medium.com/media/927f8e4c93cf7ebe06739631f2a82d1a/href</a></iframe><p>Of course, we need to update Coordinator interface. As you can see, the SwiftUIViewModelInterface is like middleware between the Objective-C view model and SiwftUI View.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1fa094f849f420d2f51a3e33ade5e723/href">https://medium.com/media/1fa094f849f420d2f51a3e33ade5e723/href</a></iframe><p>The AppCoordinator showLogin also should be updated with the LoginViewModel. Of course, ideally, it will be cool to see the SwiftUI object, but Objective-C sees only the UIKit stuff.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/14935b58a07e875bb7f6167b93f7d65f/href">https://medium.com/media/14935b58a07e875bb7f6167b93f7d65f/href</a></iframe><p>That’s it. Run build, and you can see how it works. You can find the result in final folder of the project repository: <a href="https://github.com/maxkalik/uikit-objc-to-swiftui/tree/master/final">https://github.com/maxkalik/uikit-objc-to-swiftui/tree/master/final</a></p><h3>Wrapping Up</h3><p>In my opinion, the best part of this task is the non-standard situation, and I’m sure you most likely won’t be encountered this. But the question of using SwiftUI in Objective-C exists, and why wouldn’t you try to answer it?</p><p>I call it — an uncomfortable task. It means this kind of task confuses you, and you don’t know exactly how to make this at the beginning. They force you to think out of the box, which will make you more professional.</p><pre><strong>Want to Connect?</strong></pre><pre>Don&#39;t hesitate to follow me on <a href="https://twitter.com/maxkalik">Twitter</a>.</pre><pre>I appreciate any suggestions or comments regarding my research and articles.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9c4f38d6d9c8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/updating-swiftui-views-from-objective-c-mvvm-9c4f38d6d9c8">Updating SwiftUI Views From Objective-C Using MVVM</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>