<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DevFright</title>
	<atom:link href="https://www.devfright.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.devfright.com/</link>
	<description>Learn How to Create iOS Apps</description>
	<lastBuildDate>Thu, 15 May 2025 12:11:41 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Using ScrollViewReader for Programmatic Scrolling in SwiftUI: A Tutorial</title>
		<link>https://www.devfright.com/using-scrollviewreader-for-programmatic-scrolling-in-swiftui-a-tutorial/</link>
					<comments>https://www.devfright.com/using-scrollviewreader-for-programmatic-scrolling-in-swiftui-a-tutorial/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Thu, 15 May 2025 12:11:38 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2420</guid>

					<description><![CDATA[<p>Some apps can benefit from being able to programatically scroll to a specific item in a ScrollView. Apple provides the means to do this with the ScrollViewReader. By calling scrollTo and providing it a position of the item, you can programatically have the items scrolled to the correct location. This is particularly useful for apps [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/using-scrollviewreader-for-programmatic-scrolling-in-swiftui-a-tutorial/">Using ScrollViewReader for Programmatic Scrolling in SwiftUI: A Tutorial</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Some apps can benefit from being able to programatically scroll to a specific item in a ScrollView. Apple provides the means to do this with the ScrollViewReader. By calling scrollTo and providing it a position of the item, you can programatically have the items scrolled to the correct location.</p>



<p>This is particularly useful for apps such as chat messages, emails, or anything that lists items. For chat messages, you may want to scroll back up to the first unread message.</p>



<p>Lets take a look at how this works.</p>



<span id="more-2420"></span>



<p>The example below creates a ScrollView with 50 items within.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @State private var offset = 35
    
    var body: some View {
        ScrollViewReader { proxy in
            VStack {
                Button(&quot;Scroll to Item \(offset)&quot;) {
                    withAnimation {
                        proxy.scrollTo(offset, anchor: .center)
                    }
                }
                ScrollView {
                    VStack(alignment: .leading) {
                        ForEach(0..&lt;50) { i in
                            Text(&quot;Item \(i)&quot;)
                                .padding()
                                .id(i)
                                .background(i == offset ? Color.yellow.opacity(0.3) : Color.clear)
                        }
                    }
                }
            }
        }
        .padding()
    }
}
</pre></div>


<p>Line 2 is used to set the offset of where you want to scroll to. You wouldn&#8217;t set this here normally as you would grab some value from the backend to determine where you want to jump to, but in this case, I just set it at 35 so the scroll view scrolls to the 35th item.</p>



<p>Line 5 we declare the ScrollViewReader and in the closure we are provided a ScrollViewProxy. We call it &#8220;proxy&#8221;.</p>



<p>We create a VStack next that puts a Button view up top and a ScrollView below it. The text of the Button is &#8220;Scroll to Item 35&#8221; or whatever you set the offset to earlier.</p>



<p>On lines 8 to 10 we use the ScrollViewProxy and call .scrollTo on it passing in the offset and setting the anchor to .center. The anchor provides the behaviour of the scroll action. .center scrolls the item to the center of the screen in this example.</p>



<p>The final task is on lines 12 to 21 where we create a ScrollView that contains a VStack (optional in this example and deals mostly with spacing). We have a ForEach that is set to show 50 items numbered 0 to 49 and then on line 17 we set the id to the index. In a real applications, perhaps an email app, you might set the id to a message ID and then rather than use the offset, you get the first unread message and scrollTo that message ID.</p>



<p>The last part is just used to colour the Text view based on if it is the offset.</p>



<p>If you run the app now then tapping the button at the top will scroll to item 35 in the list. Any questions, reach out below.</p>
<p>The post <a href="https://www.devfright.com/using-scrollviewreader-for-programmatic-scrolling-in-swiftui-a-tutorial/">Using ScrollViewReader for Programmatic Scrolling in SwiftUI: A Tutorial</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/using-scrollviewreader-for-programmatic-scrolling-in-swiftui-a-tutorial/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Use Annotation and Marker on a SwiftUI Map</title>
		<link>https://www.devfright.com/how-to-use-annotation-and-marker-on-a-swiftui-map/</link>
					<comments>https://www.devfright.com/how-to-use-annotation-and-marker-on-a-swiftui-map/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Thu, 08 May 2025 13:34:02 +0000</pubDate>
				<category><![CDATA[Map Kit Framework]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Tutorial]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2404</guid>

					<description><![CDATA[<p>I have written about MapKit a few times before such as a tutorial about adding a Map to your SwiftUI app and the MKPointAnnotation tutorial from back in 2013, which uses Swift and UIKit. In this tutorial we&#8217;ll look at using the Annotation and Marker structures that provide the means to getting annotations on your [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/how-to-use-annotation-and-marker-on-a-swiftui-map/">How to Use Annotation and Marker on a SwiftUI Map</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I have written about MapKit a few times before such as a <strong>tutorial about adding a Map to your SwiftUI app</strong> and the <a href="https://www.devfright.com/mkpointannotation-tutorial/">MKPointAnnotation tutorial</a> from back in 2013, which uses Swift and UIKit.</p>



<p>In this tutorial we&#8217;ll look at using the Annotation and Marker structures that provide the means to getting annotations on your map to mark locations.</p>



<p>SwiftUI makes it easy to display locations on a map. For apps that target iOS 16 or later, Annotation and Marker are the way to go. For those that are targetting lower versions of iOS the equivelant to Marker is MapMarker and the equivalent of Annotation is MapAnnotation. Today we&#8217;ll look at Marker and Annotation.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img fetchpriority="high" decoding="async" width="600" height="516" src="https://www.devfright.com/wp-content/uploads/2025/05/Screenshot-2025-05-08-at-13.28.51-1-600x516.png" alt="" class="wp-image-2410" style="width:314px;height:auto" srcset="https://www.devfright.com/wp-content/uploads/2025/05/Screenshot-2025-05-08-at-13.28.51-1-300x258@2x.png 600w, https://www.devfright.com/wp-content/uploads/2025/05/Screenshot-2025-05-08-at-13.28.51-1-300x258.png 300w, https://www.devfright.com/wp-content/uploads/2025/05/Screenshot-2025-05-08-at-13.28.51-1.png 828w" sizes="(max-width: 600px) 100vw, 600px" /></figure>
</div>


<span id="more-2404"></span>



<h2 class="wp-block-heading">A Look at using Marker in a SwiftUI Map</h2>



<p>Out of the two options, Marker is the easiest to work with and can be used if you are not interested in visual customisation. It provides a system-styled pin and allows for a custom image, label, and colour. It can be implemented as follows:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
Marker(&quot;MacDonners&quot;, coordinate: annotation.coordinate)
                                    .tint(.red)
</pre></div>


<p>You put the Marker inside your Map and pass it a name, coordinate, and colour. It creates a pin like seen in the centre of the image posted above. You also have the option of adding a custom system image (see <a href="https://www.devfright.com/customizing-sf-symbols-with-variants-in-swiftui/">how to use SF Symbols</a> for how to find the system image you need)</p>



<p>Lets take a look at how to actually use this. First, we&#8217;ll define a structure to hold the details of the Marker:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct CustomMarker: Identifiable {
    let id = UUID()
    let coordinate: CLLocationCoordinate2D
    let title: String
    let image: String
    let colour: Color
}
</pre></div>


<p>We will be using the CustomMarker in a ForEach in the view. For this reason, we mark it as Identifiable. This struct has a coordinate, a title, an image, and a colour. The image is a String because we specify systemImage with a string which you will see later.</p>



<p>Now lets create an array of CustomMarker:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
var markers: &#x5B;CustomMarker] {
    &#x5B;
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8240, longitude: -1.7360),
            title: &quot;MacDonners&quot;,
            image: &quot;fork.knife.circle.fill&quot;,
            colour: .blue
        ),
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8234, longitude: -1.7348),
            title: &quot;Eccleshill Mechanics Institute Library&quot;,
            image: &quot;books.vertical.fill&quot;,
            colour: .red
        ),
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8238, longitude: -1.7385),
            title: &quot;Eccleshill Swimming&quot;,
            image: &quot;drop.fill&quot;,
            colour: .green
        ),
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8242, longitude: -1.7352),
            title: &quot;Nafees Bakers&quot;,
            image: &quot;birthday.cake.fill&quot;,
            colour: .purple
        ),
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8237, longitude: -1.7362),
            title: &quot;Curryosity Indian Takeaway&quot;,
            image: &quot;takeoutbag.and.cup.and.straw.fill&quot;,
            colour: .brown
        ),
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8250, longitude: -1.7364),
            title: &quot;Eccleshill Working Men&#039;s Club&quot;,
            image: &quot;person.3.fill&quot;,
            colour: .blue
        ),
        CustomMarker(
            coordinate: CLLocationCoordinate2D(latitude: 53.8235, longitude: -1.7375),
            title: &quot;Bargains&quot;,
            image: &quot;cart.fill&quot;,
            colour: .green
        )
    ]
}
</pre></div>


<p>Here we pass in the coordinate of the Marker, the title, image, and the colour.</p>



<p>Put the above array in the ContentView and also add this @State property wrapped variable:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 53.824, longitude: -1.7365),
        span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)
    )
</pre></div>


<p>This is used for when we add our Map to the view. We pass it a region which has a centre and a span and essentially sets the location and zoom level that is put on the view when the app opens.</p>



<p>With that in place and our array of markers just below it, you can now add in the body:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
var body: some View {
    Map(initialPosition: .region(region)) {
        ForEach(markers) { marker in
            Marker(marker.title,
                   systemImage: marker.image,
                   coordinate: marker.coordinate)
                .tint(marker.colour)
        }
    }
    .edgesIgnoringSafeArea(.all)
}
</pre></div>


<p>This is very easy to implement in SwiftUI because the framework does a lot of the lifting work.</p>



<p>Line 2, we add a Map to the view and pass in our region as the initialPosition of hte map. Towards the end we add this modifier: .edgesIgnoringSafeArea(.all).</p>



<p>Within our Map we have a ForEach that loops around the markers we created and for each iteration it adds a Marker to the view by setting its title, systemImage, and coordinate. After the Marker we add a modifier to set the colour.</p>



<p>If you go ahead and preview the Map now you will see Markers in various locations. Although most of the annotations are made up, I set the span so that all of them fit in the view on the Preview.</p>



<p>If you need to interact with the Markers then you are best using the Annotation structure that allows more customisation and specifically adds a .onTapGesture.</p>



<h2 class="wp-block-heading">Using Annotation in a SwiftUI Map</h2>



<p>If you want to just show pins on a map then Marker is the way to go, but if you need customisation then go with Annotation.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
struct CustomAnnotation: Identifiable {
    let id = UUID()
    let coordinate: CLLocationCoordinate2D
    let title: String
    let image: String
    let colour: Color
}
</pre></div>


<p>Rename the struct from earlier. This isn&#8217;t mandatory but makes it easier to remember that we&#8217;re now working with an Annotation and not a Marker.</p>



<p>You can use the same array of markers, just renaming to annotations and renaming each item in the array to CustomAnnotation.</p>



<p>You can also keep the same region as the tutorial earlier, but add in the following @State variable.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
@State private var selectedTitle: String? = nil
</pre></div>


<p>This will be used to update the view when an annotation is tapped.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
var body: some View {
    ZStack(alignment: .top) {
        Map(initialPosition: .region(region)) {
            ForEach(annotations) { annotation in
                Annotation(annotation.title, coordinate: annotation.coordinate) {
                    VStack(spacing: 4) {
                        Image(systemName: annotation.image)
                            .font(.title)
                            .foregroundColor(annotation.colour)
                            .padding(8)
                            .background(Circle().fill(Color.white))
                            .shadow(radius: 3)
                            .onTapGesture {
                                selectedTitle = annotation.title
                            }
                        
                        Text(annotation.title)
                            .font(.caption2)
                            .padding(4)
                            .background(.ultraThinMaterial)
                            .cornerRadius(4)
                    }
                }
            }
        }
        .edgesIgnoringSafeArea(.all)
        
        if let selectedTitle = selectedTitle {
            Text(&quot;You tapped: \(selectedTitle)&quot;)
                .padding()
                .background(.thinMaterial)
                .cornerRadius(10)
                .shadow(radius: 4)
                .padding()
                .transition(.move(edge: .top))
        }
    }
}
</pre></div>


<p>This time we&#8217;re going to use a ZStack so that we can display some text on the map to show which annotation was tapped. Putting it in a ZStack means that the Text we use on line 29 is in front of the map and not behind it.</p>



<p>Initialise the Map on line 3 like done previously and add the ForEach on line 4.</p>



<p>This time we declare an Annotation instead of a Marker and pass in the title and coordinate.</p>



<p>Rather than passing in the image here, we nest a VStack and put an Image view and Text view inside. This section from the end of line 5 to line 23 is used to put our custom view in there. I opted for an Image with the annotation.image being that image, and annotation.title being the Text.</p>



<p>On line 13 we have the .onTapGesture that sets the selectedTitle to the annotationTitle on line 14. selectedTitle uses the @State property wrapper and when it changes, it updates the view and because of the if let on line 28, it will show the text view at the top of the Map just in front of it. Of couse, line 14 in the onTapGesture you could do anything you like here such as change to another view or a dialog to show more information.</p>



<p>Marker and Annotation are great for showing items on the map. Unlike MKPointAnnotation where you need to use dequeueReusableAnnotationView for efficiency, this is all built in on Marker and Annotation and you simply need to provide the title, coordinate, image, colour, and a custom view where needed.</p>



<p>Any questions, please ask below. </p>



<p></p>
<p>The post <a href="https://www.devfright.com/how-to-use-annotation-and-marker-on-a-swiftui-map/">How to Use Annotation and Marker on a SwiftUI Map</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/how-to-use-annotation-and-marker-on-a-swiftui-map/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Use ShareLink in SwiftUI to Share Text, Images, and URLs</title>
		<link>https://www.devfright.com/how-to-use-sharelink-in-swiftui-to-share-text-images-and-urls/</link>
					<comments>https://www.devfright.com/how-to-use-sharelink-in-swiftui-to-share-text-images-and-urls/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Wed, 30 Apr 2025 22:25:47 +0000</pubDate>
				<category><![CDATA[ShareLink]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2391</guid>

					<description><![CDATA[<p>The iPhone, for years, has given users the ability to share items with other users. This capability has been around since iOS 6 in the form of UIActivityViewController that allows you to share items like photos, text, websites, files with other services. Apple created a new way to share items with the introduction of ShareLink. [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/how-to-use-sharelink-in-swiftui-to-share-text-images-and-urls/">How to Use ShareLink in SwiftUI to Share Text, Images, and URLs</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The iPhone, for years, has given users the ability to share items with other users. This capability has been around since iOS 6 in the form of UIActivityViewController that allows you to share items like photos, text, websites, files with other services.</p>



<p>Apple created a new way to share items with the introduction of ShareLink. This was first available in iOS 16 and is part of SwiftUI. Apple has conveniently built this into a View that is simple to use.</p>



<span id="more-2391"></span>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
let shareURL = URL(string: &quot;https://www.devfright.com&quot;)!
.
.
.
ShareLink(item: shareURL) {
    Label(&quot;Share URL&quot;, systemImage: &quot;link&quot;)
}
</pre></div>


<p>If we put this in a View we will have a link with the text &#8220;Share URL&#8221;. If we tap on that link then the share sheet will open with the option of sharing the address of this website with someone, or with the option to open it in other apps. You can replace shareURL with shareText and pass in a String and it can also pass a string along.</p>



<h2 class="wp-block-heading">Sharing an Image with ShareLink</h2>



<p>We just shared a URL, and I mentioned that you can pass in a String as well. Lets take a look at sharing an image.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
let shareImage = Image(systemName: &quot;bolt.fill&quot;)
.
.
.
ShareLink(item: shareImageRenderer(), preview: SharePreview(&quot;Bolt Icon&quot;, image: shareImage)) {
    Label(&quot;Share Image&quot;, systemImage: &quot;photo&quot;)
}
</pre></div>


<p>The code above shows how you can add a SharePreview and an image. This is what appears in the share sheet when toy tap on the ShareLink. Before we run this version we also need to implement shareImageRenderer().</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func shareImageRenderer() -&gt; URL {
    let renderer = ImageRenderer(content: shareImage)
    let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(&quot;bolt.png&quot;)
    
    if let data = renderer.uiImage?.pngData() {
        try? data.write(to: tempURL)
    }
    return tempURL
}
</pre></div>


<p>Line 2 is used to convert an image into a UIImage which is platform-native. ImageRenderer came along in iOS 16.</p>



<p>We then get a temporary directory on line 3. A temporaryDirectory is good to use for throw-away files.</p>



<p>With the rendered and uiImage property, we call the pndData() method and we write the file to the tempURL that was created. We then return this URL.</p>



<p>This gives our ShareLink in the view the directory to the image that is about to be shared.</p>



<p>When you hit the link, you&#8217;ll now see a preview of the image and the png file will be in the temp directory and available to share via text, WhatsApp, or any other app that is available and can accept the image.</p>



<p>For this example we needed to convert the Image in the view into the .png because we cannot share a View with ShareLink.</p>



<h2 class="wp-block-heading">Transferable Protocol</h2>



<p>The ShareLink view requires that anything shared conforms to the Transferable protocol. The Transferable protocol tells the system how to serialise data for sharing. In some cases, such as for URL and String, classes do not conform to Transferable, but still work. Apple handles these behind the scenes.</p>
<p>The post <a href="https://www.devfright.com/how-to-use-sharelink-in-swiftui-to-share-text-images-and-urls/">How to Use ShareLink in SwiftUI to Share Text, Images, and URLs</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/how-to-use-sharelink-in-swiftui-to-share-text-images-and-urls/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to build a Clipboard History App with UIPasteboard, Images, and Strings in SwiftUI</title>
		<link>https://www.devfright.com/how-to-build-a-clipboard-history-app-with-uipasteboard-images-and-strings-in-swiftui/</link>
					<comments>https://www.devfright.com/how-to-build-a-clipboard-history-app-with-uipasteboard-images-and-strings-in-swiftui/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Fri, 25 Apr 2025 23:49:15 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[UIPasteboard]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2373</guid>

					<description><![CDATA[<p>In yesterdays tutorial about how to use the UIPasteboard, I pointed out that UIPasteboard can only store a single item and if you replace a string with a string, the old one gets overwritten, and likewise, if you replace a string with an image, or vice-verse, only the newest is kept. In this tutorial we [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/how-to-build-a-clipboard-history-app-with-uipasteboard-images-and-strings-in-swiftui/">How to build a Clipboard History App with UIPasteboard, Images, and Strings in SwiftUI</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In yesterdays tutorial about <a href="https://www.devfright.com/how-to-add-tap-to-copy-with-uipasteboard-in-swiftui/">how to use the UIPasteboard</a>, I pointed out that UIPasteboard can only store a single item and if you replace a string with a string, the old one gets overwritten, and likewise, if you replace a string with an image, or vice-verse, only the newest is kept.</p>



<p>In this tutorial we are going to create a simple way of storing history of what is in your clipboard/pasteboard.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" width="600" height="537" src="https://www.devfright.com/wp-content/uploads/2025/04/IMG_0829-600x537.jpg" alt="" class="wp-image-2375" srcset="https://www.devfright.com/wp-content/uploads/2025/04/IMG_0829-300x268@2x.jpg 600w, https://www.devfright.com/wp-content/uploads/2025/04/IMG_0829-300x268.jpg 300w, https://www.devfright.com/wp-content/uploads/2025/04/IMG_0829.jpg 1290w, https://www.devfright.com/wp-content/uploads/2025/04/IMG_0829-600x537@2x.jpg 1200w" sizes="(max-width: 600px) 100vw, 600px" /></figure>
</div>


<span id="more-2373"></span>



<p>The screenshot above shows what we will be creating. It has a simple text entry field with a copy button, and a Get New Face button that reaches out to <a href="https://thispersondoesnotexist.com" rel="nofollow">thispersondoesntexist.com</a>.</p>



<h2 class="wp-block-heading">Creating a Custom Pasteboard Item</h2>



<p>We begin by creating an enum called PasteboardItem that represents items as text or image. It provides a unique identifier so that it can be used in a list.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
enum PasteboardItem: Identifiable {
    case text(String)
    case image(UIImage, String)

    var id: String {
        switch self {
        case .text(let str): return &quot;text-\(str)&quot;
        case .image(_, let identifier): return &quot;img-\(identifier)&quot;
        }
    }
}
</pre></div>


<p>We declare the enum as conforming to the Identifiable protocol.</p>



<p>We have two cases in this enum, one is text that takes a String, and the other is an Image that requires a UIImage and String, the string being used for the identifier.</p>



<p>On lines 5 &#8211; 10 we implement the id var which is required to conform to Identifiable. In here we have a switch statement that checks the case being created and if text, it gets the String and returns text-somestring. If an image, then it ignores the image, but uses the description to create an id or img-somestring.</p>



<h2 class="wp-block-heading">ContentView &#8211; Creating the UI</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @State private var pasteboardHistory: &#x5B;PasteboardItem] = &#x5B;]
    @State private var selectedItem: PasteboardItem? = nil
    @State private var isLoadingFace = false
    @State private var customText: String = &quot;&quot;
</pre></div>


<p>We begin by adding some variables using the @State property wrapper. Anytime one of these changes the view will be reloaded.</p>



<p>The first one is an array of PasteboardItem. Next, we have the selectedItem which will be used for if the user taps on something in the pasteboard to show on the view. We then have isLoadingFace that is used to display a spinner, and then a variable to store some custom text.</p>



<p>Before implementing the body of the View, we&#8217;ll implement the methods that will be used to support this app.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func copyCustomText() {
    guard !customText.isEmpty else { return }
    UIPasteboard.general.string = customText
    let item = PasteboardItem.text(customText)
    addToHistory(item)
    saveTextItem(customText)
    customText = &quot;&quot;
}
</pre></div>


<p>There&#8217;s a few things happening in this method. First, we check to make sure we&#8217;re not copying something empty. If we are, we return at this point, although the UI also has checks in place to make sure you can only press Copy if there&#8217;s something in the field.</p>



<p>Next, we add this item to the UIPasteboard. Because this is the latest item it doesn&#8217;t matter that it will overwrite whatever else, if anything, was stored in the UIPasteboard.</p>



<p>On line 4 we create a new item which is a PasteboardItem, and then on the next line we add it to the history. After that we save the text item and then set the customText variable back to an empty string.</p>



<p>Lets take a look at the addToHistory and saveTextItem methods next.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func addToHistory(_ item: PasteboardItem) {
    if let existingIndex = pasteboardHistory.firstIndex(where: { $0.id == item.id }) {
        pasteboardHistory.remove(at: existingIndex)
    }
    pasteboardHistory.insert(item, at: 0)
}

func saveTextItem(_ text: String) {
    let filename = &quot;text_\(Date().timeIntervalSince1970).txt&quot;
    let fileURL = getDocumentsDirectory().appendingPathComponent(filename)
    try? text.write(to: fileURL, atomically: true, encoding: .utf8)
}
</pre></div>


<p>The first method, addToHistory, takes a PasteboardItem. We check in the pasteboardHistory array to see if the item exists on line 1, and if so, we remove the item. We then re-add the item at index 0. The reason for this is that we want the latest copies stored at the beginning of our list. By removing the old, we can then put it back at the start without causing a duplicate to happen.</p>



<p>Our saveToFile method takes a String, which in the case of the customText it is the actual text. It then creates a filename with the text and date and then gets the URL and writes the file to disk.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func fetchAndSaveFace() {
    isLoadingFace = true
    let url = URL(string: &quot;https://thispersondoesnotexist.com&quot;)!
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        DispatchQueue.main.async {
            isLoadingFace = false
            guard let data = data, let image = UIImage(data: data), error == nil else {
                print(&quot;Error fetching image: \(error?.localizedDescription ?? &quot;Unknown error&quot;)&quot;)
                return
            }
            
            let timestamp = Date().timeIntervalSince1970
            let identifier = &quot;face_\(timestamp)&quot;
            let filename = &quot;\(identifier).png&quot;
            let fileURL = getDocumentsDirectory().appendingPathComponent(filename)
            
            if let pngData = image.pngData() {
                try? pngData.write(to: fileURL)
                UIPasteboard.general.image = image
                addToHistory(.image(image, identifier))
            }
        }
    }
    task.resume()
}
</pre></div>


<p>This next method does a few things, which isn&#8217;t ideal, but good enough to show how it works.</p>



<p>First, it sets isLoadingFace to true. This is used to start a spinner going to show that something is happening.</p>



<p>Next, we get a URL from a String which is the website where we grab the image. This particular website is used to generate random faces.</p>



<p>On line 4 we create a task, specifically a dataTask from the URLSession class and .shared property. We pass in the URL which will make a network request. We dispatch this to main. It takes a closure that gets called when the request completes. There are three parameters in here which are data, response, and error.</p>



<p>It is dispatched to main so that when it finishes fetching the image the UI can update, although the actual network request will run on a background thread so that it doesn&#8217;t hold up the View.</p>



<p>The task is started when task.resume() is called (see line 24).</p>



<p>When the completion handler is called we first check to see if the data is valid and if the image was downloaded correctly. If all is OK, then we get a timestamp, an identifier which is face_timestamp, then a filename which is made from the identifier, and the fileURL. We then save the file, copy it to the UIPasteboard, and add it to history.</p>



<h2 class="wp-block-heading">Getting the Documents Directory</h2>



<p>We have a small helper method:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func getDocumentsDirectory() -&gt; URL {
    FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)&#x5B;0]
}
</pre></div>


<p>We use this in a couple of locations, so its good practice to not rewrite this each time.</p>



<h2 class="wp-block-heading">Loading the Items</h2>



<p>When we close the app, the history array will nolonger exist, this is the reason we have been saving the pasteboardItems to the documents folder. Another option could be to store them in UserDefaults. This next method is responsible for getting the array of PasteboardItem and populating the history.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func loadSavedItems() -&gt; &#x5B;PasteboardItem] {
    let docURL = getDocumentsDirectory()
    let files = (try? FileManager.default.contentsOfDirectory(at: docURL, includingPropertiesForKeys: nil)) ?? &#x5B;]
    
    var items: &#x5B;PasteboardItem] = &#x5B;]
    
    for url in files {
        if url.pathExtension == &quot;txt&quot;,
           let text = try? String(contentsOf: url, encoding: .utf8) {
            let item = PasteboardItem.text(text)
            if let existingIndex = items.firstIndex(where: { $0.id == item.id }) {
                items.remove(at: existingIndex)
            }
            items.insert(item, at: 0)
        } else if url.pathExtension == &quot;png&quot;,
                  let image = UIImage(contentsOfFile: url.path) {
            // Image identifier based on file name to ensure consistent ID
            let identifier = url.lastPathComponent
            let item = PasteboardItem.image(image, identifier)
            if let existingIndex = items.firstIndex(where: { $0.id == item.id }) {
                items.remove(at: existingIndex)
            }
            items.insert(item, at: 0)
        }
    }
    
    return items
}
</pre></div>


<p>This method returns an array of PasteboardItem.</p>



<p>We get the documents directory first, and then get the files from that directory.</p>



<p>On line 5 we create an empty array of PasteboardItem and call it items. This will be populated when we iterate over the files we got from the directory and then returned at the end.</p>



<p>Lines 7 &#8211; 25 we loop over the files array. If the path extension is txt then we get the contents of the url and create a new PasteboardItem of type text. We check if the item already exists in the items array, which it shouldn&#8217;t. If it does, then we remove it, and then on line 14 we insert it at index 0.</p>



<p>If the item extension is a png then we create a UIImage with the contents of the file at the path.</p>



<p>We create an identifier, and then create the item with the image and identifier created.</p>



<p>We run the same checks in that if the item exists already then remove it. We then add it at index 0 which puts it at the beginning.</p>



<p>There is some diplication in here, but for tutorial purposes, you get a grasp of what it is doing.</p>



<p>When complete, it returns the items (array of PasteboardItem) to the caller.</p>



<h2 class="wp-block-heading">Creating our View</h2>



<p>Now that everything is in place, its time to create our view.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
var body: some View {
    NavigationView {
        ZStack {
            VStack(spacing: 16) {
                HStack {
                    TextField(&quot;Enter custom text&quot;, text: $customText)
                        .textFieldStyle(.roundedBorder)
                        .onSubmit {
                            copyCustomText()
                        }
                    
                    Button(action: copyCustomText) {
                        Text(&quot;Copy&quot;)
                    }
                    .buttonStyle(.bordered)
                    .disabled(customText.isEmpty)
                }
                .padding(.horizontal)
                
                Button(action: fetchAndSaveFace) {
                    Text(&quot;Get New Face&quot;)
                }
                .buttonStyle(.borderedProminent)
                .disabled(isLoadingFace)
                
                Divider()
                
                ScrollView(.horizontal) {
                    HStack {
                        ForEach(pasteboardHistory) { item in
                            Button(action: {
                                selectedItem = item
                                switch item {
                                case .text(let str):
                                    UIPasteboard.general.string = str
                                case .image(let img, _):
                                    UIPasteboard.general.image = img
                                }
                            }) {
                                VStack {
                                    switch item {
                                    case .text(let str):
                                        Text(str)
                                            .padding(8)
                                            .background(Color.blue.opacity(0.1))
                                            .cornerRadius(8)
                                    case .image(let img, _):
                                        Image(uiImage: img)
                                            .resizable()
                                            .frame(width: 60, height: 60)
                                            .clipShape(RoundedRectangle(cornerRadius: 8))
                                    }
                                }
                            }
                        }
                    }
                    .padding(.horizontal)
                }
                
                Divider()
                
                if let selected = selectedItem {
                    Group {
                        switch selected {
                        case .text(let str):
                            Text(str)
                                .font(.title)
                                .padding()
                        case .image(let img, _):
                            Image(uiImage: img)
                                .resizable()
                                .scaledToFit()
                                .frame(maxWidth: 300, maxHeight: 300)
                        }
                    }
                    .transition(.opacity)
                } else {
                    Text(&quot;Tap an item to preview&quot;)
                        .foregroundStyle(.secondary)
                }
                
                Spacer()
            }
            .padding()
            .navigationTitle(&quot;Pasteboard History&quot;)
            .onAppear {
                pasteboardHistory = loadSavedItems()
            }
            
            if isLoadingFace {
                Color.black.opacity(0.4)
                    .ignoresSafeArea()
                VStack {
                    ProgressView()
                        .progressViewStyle(.circular)
                        .scaleEffect(1.5)
                    Text(&quot;Fetching new face...&quot;)
                        .foregroundColor(.white)
                        .padding(.top, 8)
                }
                .padding()
                .background(.ultraThinMaterial)
                .cornerRadius(12)
            }
        }
    }
}
</pre></div>


<p>I wont go through every line in the view as much of it is self explanatory, but as a brief overview we have a NavigationView that has a title set on line 85 of &#8220;Pasteboard History&#8221;. Within this navigation view we have ZStacks and HStacks as well as a ScrollView.</p>



<p>The TextField on line 6 is the place where you can write a message. It binds to $customText and when onSubmit is called, it calls the copyCustomText method we looked at earlier.</p>



<p>The button also is added and has an action that calls the same method. The button is disabled/enabled based on the customText being empty.</p>



<p>We have another Button on line 20 that has an action that calls the fetchAndSaveFace method, also discussed earlier. This button disables while isLoadingFace is true.</p>



<p>On line 90, ifLoadingFace is true, we show a ProgressView to let the user know that something is happening. This disapears when isLoadingFace toggles back to false.</p>



<p>Onto our pasteboard contents, these are shown in a ScrollView set to .horizontal. Each item copied is listed in here with the newest being at the start of the view. Each item is a Button that has an action that sets the selectedItem (line 32). The view shows either an image or a string of text each of which can be pushed to show the contents below.</p>



<p>Showing the contents is handled on line 62 where if there is a selectedItem, it shows on the view either as Text on line 66 or as an Image on Line 70.</p>



<p>If you try the app now, you&#8217;ll be able to copy text and images to the Pasteboard and retrieve them. Enhancements could include a long-tap gesture that brings up a vertical list of items in the pasteboard that you can tap on to copy into the view where a cursor is.</p>



<p>As mentioned earlier, it would also work in UserDefaults where you might persist the items there.</p>



<p>One thing that might confuse you is why we even need UIPasteboard for this tutorial. The answer is that the storage is handled in documents, but we still use the UIPasteboard to store the latest item. By integrating with the built-in pasteboard, it allows us to tap on an image or text in our history and then go to another device on the same iCloud device and paste that item across. This makes it useful because this is fairly standard behaviour with some enhancements.</p>
<p>The post <a href="https://www.devfright.com/how-to-build-a-clipboard-history-app-with-uipasteboard-images-and-strings-in-swiftui/">How to build a Clipboard History App with UIPasteboard, Images, and Strings in SwiftUI</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/how-to-build-a-clipboard-history-app-with-uipasteboard-images-and-strings-in-swiftui/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Add Tap to Copy with UIPasteboard in SwiftUI</title>
		<link>https://www.devfright.com/how-to-add-tap-to-copy-with-uipasteboard-in-swiftui/</link>
					<comments>https://www.devfright.com/how-to-add-tap-to-copy-with-uipasteboard-in-swiftui/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Thu, 24 Apr 2025 10:06:50 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[UIPasteboard]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2358</guid>

					<description><![CDATA[<p>If your user need a way to quickly copy something to the clipboard, or pasteboard as Apple calls it, in your iPhone app, then UIPasteboard is the class that can be used to accomplish this. UIPasteboard has been available since iOS 3.0. Implementing a Reusable View that uses UIPasteboard We&#8217;ll look at creating a reusable [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/how-to-add-tap-to-copy-with-uipasteboard-in-swiftui/">How to Add Tap to Copy with UIPasteboard in SwiftUI</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>If your user need a way to quickly copy something to the clipboard, or pasteboard as Apple calls it, in your iPhone app, then UIPasteboard is the class that can be used to accomplish this. UIPasteboard has been available since iOS 3.0.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" width="600" height="311" src="https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-24-at-10.38.50-600x311.png" alt="" class="wp-image-2359" srcset="https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-24-at-10.38.50-300x155@2x.png 600w, https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-24-at-10.38.50-300x155.png 300w, https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-24-at-10.38.50.png 854w" sizes="(max-width: 600px) 100vw, 600px" /></figure>
</div>


<span id="more-2358"></span>



<h2 class="wp-block-heading">Implementing a Reusable View that uses UIPasteboard </h2>



<p>We&#8217;ll look at creating a reusable View that uses the UIPasteboard that accepts a string and allows you to tap to copy.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct CopyToPasteboardView: View {
    @State private var showTooltip = false
    let textToCopy: String

    var body: some View {
        VStack(spacing: 10) {
            HStack {
                Text(textToCopy)
                    .font(.title3)
                    .foregroundColor(.primary)

                Image(systemName: &quot;doc.on.clipboard&quot;)
                    .foregroundColor(.gray)
                    .imageScale(.small)
            }
            .padding()
            .background(Color(.secondarySystemBackground))
            .cornerRadius(8)
            .onTapGesture {
                UIPasteboard.general.string = textToCopy
                withAnimation {
                    showTooltip = true
                }

                DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
                    withAnimation {
                        showTooltip = false
                    }
                }
            }

            if showTooltip {
                Text(&quot;Copied!&quot;)
                    .font(.caption)
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .background(Color.green.opacity(0.8))
                    .foregroundColor(.white)
                    .cornerRadius(6)
                    .transition(.opacity.combined(with: .move(edge: .top)))
            }
        }
    }
}
</pre></div>


<p>The view will work by accepting a string, and when that string is tapped, it will copy it to the pasteboard. A tool tip will show to let you know that the copy has happened.</p>



<p>Line 2 has a Bool called showToolTip that uses the @State property wrapper.</p>



<p>Line 3 has our textToCopy parameter that is required when the view is initialised.</p>



<p>We create our view with a VStack that has two Text views. The first is on line 8 that is styled and formats the text. This, and an Image view are wrapped in a HStack. The image used is from SF Symbols, see the <a href="https://www.devfright.com/customizing-sf-symbols-with-variants-in-swiftui/">SF Symbols tutorial</a> here. This provides a prompt to the user to indicate that they can copy this text.</p>



<p>Lines 16 &#8211; 18 add some styling to the HStack.</p>



<p>On line 19 we use the .onTapGesture so that when the HStack is tapped, we store the textToCopy in the UIPasteboard.general.string property.</p>



<p>We add in withAnimation and inside that, on line 22, we set showToolTip to true. Because showToolTip uses @State, that Text view will animate and also push up the text to copy part of the view.</p>



<p>On line 25, on the main thread and after 1.5 seconds, we set showToolTip back to false and wrap that in withAnimation. This means that the tool tip shows for 1.5 seconds (animating in), and then disapears while animating out.</p>



<h2 class="wp-block-heading">Using the CopyToPasteboardView</h2>



<p>The final peice of the puzzle is making use of the CopyToPasteboardView. We use it as follows:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    var body: some View {
        CopyToPasteboardView(textToCopy: &quot;https://www.devfright.com/feed&quot;)
            .padding()
    }
}
</pre></div>


<p>The ContentView is fairly self explanatory here. We make a call to our custom view called CopyToPasteBoardView and pass in the text we want to copy. For your app, that might be an email address, or anything you like.</p>



<p>One thing to note is that how this is setup now, you can copy just a single item. If you were to replace the string with an image, that image would overwrite the string, and vice-versa.</p>



<p>In another tutorial we&#8217;ll look at some more advanced features such as being able to store multiple items as well as different content types.</p>
<p>The post <a href="https://www.devfright.com/how-to-add-tap-to-copy-with-uipasteboard-in-swiftui/">How to Add Tap to Copy with UIPasteboard in SwiftUI</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/how-to-add-tap-to-copy-with-uipasteboard-in-swiftui/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Handling Deep Links in SwiftUI with URL Schemes</title>
		<link>https://www.devfright.com/handling-deep-links-in-swiftui-with-url-schemes/</link>
					<comments>https://www.devfright.com/handling-deep-links-in-swiftui-with-url-schemes/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Wed, 23 Apr 2025 22:20:47 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Tutorial]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2353</guid>

					<description><![CDATA[<p>Swift makes it easy to allow deep links into your app. Deep links are especially helpful when wanting to direct users straight to specific content. One way I use deep links is sending emails to my task manager. When I see the task, I can click on a link and it opens up the Mail [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/handling-deep-links-in-swiftui-with-url-schemes/">Handling Deep Links in SwiftUI with URL Schemes</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Swift makes it easy to allow deep links into your app. Deep links are especially helpful when wanting to direct users straight to specific content. One way I use deep links is sending emails to my task manager. When I see the task, I can click on a link and it opens up the Mail app on my Mac or iPhone directly into the app. Another use is Evernote. I can tap on a link and have the Evernote app open with the content I want to see.</p>



<p>You will see these links as message://link_to_some_page/id or evernote://some_note</p>



<p>Lets take a look at how this works.</p>



<span id="more-2353"></span>



<p>The first step is to add a new entry to your info.plist file. If you add &#8220;URLScheme&#8221;URL Types&#8221;, an info.plist will appear in the sidebar. Right click on it and &#8220;Open As > Source Code&#8221;, and add the following:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
&lt;dict&gt;
	&lt;key&gt;CFBundleURLTypes&lt;/key&gt;
	&lt;array&gt;
		&lt;dict&gt;
			&lt;key&gt;CFBundleURLSchemes&lt;/key&gt;
			&lt;array&gt;
				&lt;string&gt;deeplink&lt;/string&gt;
			&lt;/array&gt;
		&lt;/dict&gt;
	&lt;/array&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</pre></div>


<p>Line 10 is where we set our URL scheme. In this example I called it deeplink, which means our deep links will follow the format: deeplink://some_page.</p>



<h2 class="wp-block-heading">Adding a Route</h2>



<p>We will handle deep links with our own router and have an enun of known, and unknown routes. Lets implement the routes as follows:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
enum AppRoute: Equatable {
    case home
    case profile
    case settings
    case unknown
    
    init(url: URL) {
        switch url.host {
        case &quot;home&quot;:
            self = .home
        case &quot;profile&quot;:
            self = .profile
        case &quot;settings&quot;:
            self = .settings
        default:
            self = .unknown
        }
    }
}
</pre></div>


<p>This enum has an init method that accepts a URL. When we pass it a URL it determines if the URL is a home, profile, settings, or unknown route. We will use this a little later.</p>



<h2 class="wp-block-heading">Router Class</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
class AppRouter: ObservableObject {
    @Published var route: AppRoute = .home
    
    func handle(url: URL) {
        self.route = AppRoute(url: url)
    }
}
</pre></div>


<p>Next, we add a Router class. This conforms to ObservableObject and has one @Published property which is an AppRoute, the struct we just created earlier.</p>



<p>It also has a method called handle that accepts a URL and then sets self.route based on the initialised AppRoute.</p>



<h2 class="wp-block-heading">@Main</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
import SwiftUI

@main
struct DeepLinkApp: App {
    @StateObject private var router = AppRouter()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(router)
                .onOpenURL { url in
                    router.handle(url: url)
                }
        }
    }
}
</pre></div>


<p>We create our AppRouter as an @StateObject. An @StateObject lives for the duration of the app with it being declared here. If declared in a view, it would stay alive for the duration fo the view.</p>



<p>Line 10 injects router to the environment.</p>



<p>Line 11 detects when the app opens from a URL and when it does, it receives that URL, something like deeplink://profile and calls the handle method on the router. This then sets the @Published property in that class to the correct AppRoute enum value, and because its @Published, the view can observer it and action accordingly.</p>



<h2 class="wp-block-heading">ContentView and Displaying the Correct Content</h2>



<p>Our next task is to have our ContentView load up the correct view:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @EnvironmentObject var router: AppRouter
    
    var body: some View {
        NavigationStack {
            switch router.route {
            case .home:
                Text(&quot;Home&quot;)
            case .profile:
                Text(&quot;Profile&quot;)
            case .settings:
                Text(&quot;Settings&quot;)
            case .unknown:
                Text(&quot;Unknown Route&quot;)
            }
        }
    }
}
</pre></div>


<p>On line 2 we have our @EnvironmentObject which we access. This was injected in @main in the ContentView. This means that whatever happened in there when the app detected it was opened by URL, we have access to that same instance.</p>



<p>With that value we can use a switch statement and detect what the router&#8217;s route property is, that being .home, .profile, .settings, or .unknown. We can then display the correct view. In this test I just displayed a Text view for each.</p>



<h2 class="wp-block-heading">In Closing</h2>



<p>You don&#8217;t specifically need a route and router and can handle this anyway you want. In short, you setup the URL Scheme in info.plist. You use .onOpenURL in the Scene, and you action it.</p>



<p>If you were building a note taking app, your route enum might have the following cases:</p>



<p>.noteList<br>.newNote<br>.settings<br>.profile</p>



<p>A URL opening .newNote could immediately open the app to a new item making a great place to quickly make a note of something. It is also useful for Apple Shortcuts integration where you might have a shortcut to do something deep within your app.</p>
<p>The post <a href="https://www.devfright.com/handling-deep-links-in-swiftui-with-url-schemes/">Handling Deep Links in SwiftUI with URL Schemes</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/handling-deep-links-in-swiftui-with-url-schemes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Build a Native QR Code Generator for iPhone with SwiftUI</title>
		<link>https://www.devfright.com/how-to-build-a-native-qr-code-generator-for-iphone-with-swiftui/</link>
					<comments>https://www.devfright.com/how-to-build-a-native-qr-code-generator-for-iphone-with-swiftui/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Tue, 22 Apr 2025 11:13:20 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Tutorial]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2341</guid>

					<description><![CDATA[<p>iPhone has had the ability to generate QR codes since iOS 5 that was released in 2011, yet, it is something that I have never used before in an iPhone app. QR codes are useful for many things from putting a URL on a flyer that someone can scan to visit a web address to [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/how-to-build-a-native-qr-code-generator-for-iphone-with-swiftui/">How to Build a Native QR Code Generator for iPhone with SwiftUI</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>iPhone has had the ability to generate QR codes since iOS 5 that was released in 2011, yet, it is something that I have never used before in an iPhone app. QR codes are useful for many things from putting a URL on a flyer that someone can scan to visit a web address to showing on your phone to connect to a friend. In this tutorial, we&#8217;ll look at how a QR code is generated on the iPhone.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="600" height="630" src="https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-22-at-11.28.51-600x630.png" alt="" class="wp-image-2342" style="width:312px;height:auto" srcset="https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-22-at-11.28.51-300x315@2x.png 600w, https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-22-at-11.28.51-300x315.png 300w, https://www.devfright.com/wp-content/uploads/2025/04/Screenshot-2025-04-22-at-11.28.51.png 918w" sizes="auto, (max-width: 600px) 100vw, 600px" /></figure>
</div>


<span id="more-2341"></span>



<h2 class="wp-block-heading">Adding Properties</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
@State private var inputText = &quot;https://www.devfright.com&quot;
private let context = CIContext()
private let filter = CIFilter.qrCodeGenerator()
</pre></div>


<p>We begin by adding these three properties into our ContentView. The first, inputText, uses the <a href="https://www.devfright.com/state-and-stateobject-property-wrappers/">@State property wrapper</a> so that when the inputText changes, the view is refreshed.</p>



<p>The latter two are constants, one that holds the CIContext and the other that uses the qrCodeGenerator method in the CIFilter class. Details of these will be discussed later on.</p>



<h2 class="wp-block-heading">Generating the QR Code</h2>



<p>The next code is what we use to generate the QR Code.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func generateQRCode(from string: String, size: CGFloat = 512) -&gt; UIImage {
    filter.message = Data(string.utf8)

    if let outputImage = filter.outputImage {
        let scaleX = size / outputImage.extent.size.width
        let scaleY = size / outputImage.extent.size.height
        let transform = CGAffineTransform(scaleX: scaleX, y: scaleY)

        let scaledImage = outputImage.transformed(by: transform)

        if let cgimg = context.createCGImage(scaledImage, from: scaledImage.extent) {
            return UIImage(cgImage: cgimg)
        }
    }

    return UIImage(systemName: &quot;xmark.circle&quot;) ?? UIImage()
}
</pre></div>


<p>We accept a string which is the string that we want creating into a QR code. We also specify a size that we want the image and tell it we want to return a UIImage.</p>



<p>Line 2 we use our filter (the CIFilter.qrCodeGenerator()) and set the message property to the string, but convertated to utf8 Data. This is required because the filter expects Data and not a string.</p>



<p>At this point we now have a QR code that can be accessed at filter.outputImage, but we&#8217;re not quite ready yet. The CIImage that has been created needs to be converted to a UIImage so that it can be seen in the view. As a CIImage is low resolution, in this test it is 27.0 points on the width and height.</p>



<p>Lines 4 to 14 show how we can modify the CIImage. We begin by getting the outputImage from the filter and storing that in outputImage.</p>



<p>Lines 5 and 6 we calculate the x and y scale by divding the outputImage&#8217;s width and height into the size, which is defaulted to 512 in the function params. These are stored in scaleX and scaleY.</p>



<p>Line 7 we use CGAffineTransform to scale the image in both x and y directions by passing in the scaleX/Y created on the lines earlier.</p>



<p>Line 9 we apply the scaling transform to the outputImage by using the transformed(by:) method. This creates a new CIImage with the desired size.</p>



<p>On line 11 we use our correctly sized CIImage and convert it to a CGImage. The context used here is for handling the image rendering.</p>



<p>If all is successful at this point, on line 12 we use our CGImage and convert it to a UIImage to return to the caller, which in our case will be the view.</p>



<h2 class="wp-block-heading">Setting up the View for the QR Code to Display</h2>



<p>Now that we have a QR code in a UIImage format, we can use that in the view. It took a few steps to create it because of starting as a CIImage, then being scaled with a transform, and then created as a CGImage to then be returned as a UIImage. With all of that logic out of the way, all we need to do is make a call to this method with a string of text and then receive a UIImage back.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
var body: some View {
    NavigationView {
        VStack(spacing: 20) {
            TextField(&quot;Enter text or URL&quot;, text: $inputText)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()

            Image(uiImage: generateQRCode(from: inputText))
                .interpolation(.none)
                .resizable()
                .scaledToFit()
                .frame(width: 200, height: 200)
                .padding()

            Spacer()
        }
        .navigationTitle(&quot;QR Code Generator&quot;)
    }
}
</pre></div>


<p>We setup a standard View here that has a NavigationView with a navigationTitle set as &#8220;QR Code Generator&#8221;. We then nest a VStack inside that contains a TextField which is where the user can enter text or a URL.</p>



<p>The TextField uses the text property, which is bound to the @State property via $inputText. This binding ensures that any changes made in the TextField automatically update the inputText state. Since the Image view relies on inputText to generate the QR code, each time the user types, the inputText value changes. As a result, the generateQRCode method is called on every key press, and the QR code is updated in real-time as the user types.</p>



<p>Go ahead and run the app now. It will work in the preview, simulator, or on device.</p>
<p>The post <a href="https://www.devfright.com/how-to-build-a-native-qr-code-generator-for-iphone-with-swiftui/">How to Build a Native QR Code Generator for iPhone with SwiftUI</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/how-to-build-a-native-qr-code-generator-for-iphone-with-swiftui/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Use UndoManager with SwiftData</title>
		<link>https://www.devfright.com/how-to-use-undomanager-with-swiftdata/</link>
					<comments>https://www.devfright.com/how-to-use-undomanager-with-swiftdata/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Fri, 18 Apr 2025 14:55:38 +0000</pubDate>
				<category><![CDATA[SwiftData]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2323</guid>

					<description><![CDATA[<p>SwiftData is a framework that is built on top of Core Data and greatly simplifies working with data in your SwiftUI app. One caveat is that not all functionality of Core Data can be found in SwiftData. In this turorial we&#8217;ll look at the UndoManager that tracks changes and allows you to undo and redo [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/how-to-use-undomanager-with-swiftdata/">How to Use UndoManager with SwiftData</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>SwiftData is a framework that is built on top of Core Data and greatly simplifies working with data in your SwiftUI app. One caveat is that not all functionality of Core Data can be found in SwiftData. In this turorial we&#8217;ll look at the UndoManager that tracks changes and allows you to undo and redo changes you have made. This can be extremely useful for apps that implement SwiftData. SwiftData makes this process extremely simple to configure.</p>



<p>Lets begin with the app created in the <a href="https://www.devfright.com/how-to-create-a-simple-to-do-list-with-swipe-actions-in-swiftui-using-swiftdata/">ToDo List app with SwiftData</a>. You can download this below.</p>



<div class="wp-block-file"><a id="wp-block-file--media-be7c6a31-8b1b-4294-b96c-dc7b21c25c0f" href="https://www.devfright.com/wp-content/uploads/2025/04/ToDo.zip">ToDo</a><a href="https://www.devfright.com/wp-content/uploads/2025/04/ToDo.zip" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-be7c6a31-8b1b-4294-b96c-dc7b21c25c0f">Download</a></div>



<span id="more-2323"></span>



<p>Make any changes needed to the bundle ID and signing in the target as well as the container you want to use for storing the data.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
.modelContainer(for: ToDoItem.self, isUndoEnabled: true)
</pre></div>


<p>In ToDoApp, add <strong>isUndoEnabled: true</strong> to the modelContainer. When not declared it defaults to false.</p>



<p>At this point, thanks to SwiftData and behind the scenes, you now have undo available in your ToDo app. To access it, make a change and then swipe left or right with three fingers to step through the changes. Swiping one way will undo changes, and swiping the other way will redo the changes.</p>



<p>One thing to note is that changes are only tracked on a single device. If you mark a task as complete on the iPad and it syncs across, you can&#8217;t undo that change on an iPhone with the swipe gestures.</p>



<h2 class="wp-block-heading">What Else can Undo Do?</h2>



<p>It seems a little odd to write a turorial, give you one parameter to add and then say we&#8217;re done, so lets take a look at how you can implement more to make it useful for you and your users given that many might not realise a three finger swipe would be needed to undo or redo an action, let alone know if undo is even implemented.</p>



<p>The undoManager @Environment variable has a number of properties that can be queried as well as methods that can be called. Some of these include the likes of canUndo which is a Bool that tells you if there are any actions that can be undone. Likewise, canRedo works the same and lets you know if you can redo a change that you just used undo on.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
@Environment(\.undoManager) private var undoManager
</pre></div>


<p>In ContentView add the undoManager from the @Environment. The undoManager here is kept up to date when you set isUndoEnabled to true earlier.</p>



<p>Next, update the ContentView with the following under the ToolBarItem:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
ToolbarItemGroup(placement: .bottomBar) {
    Spacer()
    Button(action : {
        undoManager?.undo()
    }) {
        Image(systemName: &quot;arrow.uturn.backward&quot;)
    }
    .disabled(!(undoManager?.canUndo ?? false))
    
    Button(action : {
        undoManager?.redo()
    }) {
        Image(systemName: &quot;arrow.uturn.forward&quot;)
    }
    .disabled(!(undoManager?.canRedo ?? false))
}
</pre></div>


<p>This adds buttons to the bottom of the NavigationView with the spacer on line 2 pushing the buttons to the right of the view.</p>



<p>We add in two Button views here that manually call the undo() or redo() methods in the undoManager. This is the equivalent of using the swipe gesture I mentioned earlier.</p>



<p>We add an <a href="https://www.devfright.com/customizing-sf-symbols-with-variants-in-swiftui/">Image from SF Symbols</a> and under each button we check if the undoManaget an undo or redo. If it can, the button is enabled.</p>



<p>If you complete tasks now, you will see the undo arrow become enabled. If you tap it, the redo arrow enables and the undo disables.</p>



<p>Other methods include undoCount which is a method that returns an Int. If your UI accounted for this, you can put the number of times you can invoke the undo method. Likewise, redoCount tells you the number of times you can invoke the redo method.</p>



<p>There are more features available such as making groups of changes, similar to transactions in SQL, where you can group changes together and undo a whole group together.</p>



<p>A full list of methods and properties can be found in the <a href="https://developer.apple.com/documentation/foundation/undomanager" rel="nofollow">UndoManager class documentation</a>.</p>



<p>If you have any questions on any part of this, please do not hesitate to reach out below.</p>
<p>The post <a href="https://www.devfright.com/how-to-use-undomanager-with-swiftdata/">How to Use UndoManager with SwiftData</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/how-to-use-undomanager-with-swiftdata/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Exploring SwiftUI Picker Styles: Date, Selection, Graphical, Inline etc&#8230;</title>
		<link>https://www.devfright.com/exploring-swiftui-picker-styles-date-selection-graphical-inline-etc/</link>
					<comments>https://www.devfright.com/exploring-swiftui-picker-styles-date-selection-graphical-inline-etc/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Thu, 17 Apr 2025 16:00:17 +0000</pubDate>
				<category><![CDATA[DatePicker]]></category>
		<category><![CDATA[Picker]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Tutorial]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2314</guid>

					<description><![CDATA[<p>The Picker has been around since iOS 2 and is known as the UIPickerView in UIKit. It joined SwiftUI in iOS 13 as &#8220;Picker&#8221;. As well as the Picker view, there is also the DatePicker view that you will also be familiar with using in apps. The regular Picker also has a modifier called .pickerStyle [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/exploring-swiftui-picker-styles-date-selection-graphical-inline-etc/">Exploring SwiftUI Picker Styles: Date, Selection, Graphical, Inline etc&#8230;</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The Picker has been around since iOS 2 and is known as the UIPickerView in UIKit. It joined SwiftUI in iOS 13 as &#8220;Picker&#8221;. As well as the Picker view, there is also the DatePicker view that you will also be familiar with using in apps.</p>



<p>The regular Picker also has a modifier called .pickerStyle that you can attach to that view where you can pass in various picker styles that conform to the PickerStyle protocol. In this tutorial we&#8217;ll look over some of these to show you ways how you might provide ways for your users to input the required information.</p>



<span id="more-2314"></span>



<h2 class="wp-block-heading">Setting up Pickers</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @State private var selectedVehicle = &quot;Lamborghini&quot;
    @State private var selectedColor = Color.green
    @State private var selectedDate = Date()
    
    let vehicles = &#x5B;&quot;Lamborghini&quot;, &quot;Ferrari&quot;, &quot;McLaren&quot;, &quot;McMurtry&quot;, &quot;Rimac&quot;]
    let colors: &#x5B;Color] = &#x5B;.red, .green, .blue, .yellow, .purple, .gray]
    
    var body: some View {
        ScrollView {
            VStack(spacing: 30) {
                // Selection Picker with Wheel Style
                Text(&quot;Select Your Favorite Vehicle (Wheel Style):&quot;)
                    .font(.headline)
                Picker(&quot;Select a vehicle&quot;, selection: $selectedVehicle) {
                    ForEach(vehicles, id: \.self) { vehicle in
                        Text(vehicle)
                    }
                }
                .pickerStyle(WheelPickerStyle())
                
                // Selection Picker with Segmented Style
                Text(&quot;Select Your Favorite Vehicle (Segmented Style):&quot;)
                    .font(.headline)
                Picker(&quot;Select a vehicle&quot;, selection: $selectedVehicle) {
                    ForEach(vehicles, id: \.self) { vehicle in
                        Text(vehicle)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
                
                // Selection Picker with Menu Style
                Text(&quot;Select Your Favorite Vehicle (Menu Style):&quot;)
                    .font(.headline)
                Picker(&quot;Select a vehicle&quot;, selection: $selectedVehicle) {
                    ForEach(vehicles, id: \.self) { vehicle in
                        Text(vehicle)
                    }
                }
                .pickerStyle(MenuPickerStyle())
                
                // Inline Picker with Label
                Text(&quot;Select a Color (Inline Style):&quot;)
                    .font(.headline)
                Picker(&quot;Select a color&quot;, selection: $selectedColor) {
                    ForEach(colors, id: \.self) { color in
                        Text(color.description)
                            .foregroundColor(color)
                            .tag(color)
                    }
                }
                .pickerStyle(InlinePickerStyle())
                
                Group {
                    Text(&quot;Pick a Date:&quot;)
                        .font(.headline)
                    
                    DatePicker(&quot;&quot;, selection: $selectedDate, displayedComponents: .date)
                        .datePickerStyle(WheelDatePickerStyle())
                        .padding()
                    
                    Text(&quot;DefaultDatePickerStyle&quot;)
                    DatePicker(&quot;Select a Date&quot;, selection: $selectedDate)
                        .datePickerStyle(DefaultDatePickerStyle())
                    
                    Text(&quot;GraphicalDatePickerStyle&quot;)
                    DatePicker(&quot;&quot;, selection: $selectedDate)
                        .datePickerStyle(GraphicalDatePickerStyle())
                }
                
                Text(&quot;You selected: \(selectedVehicle) - \(selectedColor.description)&quot;)
                    .font(.title2)
                    .foregroundColor(selectedColor)
                
            }
            .padding()
        }
    }
}
</pre></div>


<p>Lines 2 &#8211; 4 contain @State property wrappers for vehicle, colour, and Date. We use @State so that the view can update any time one of the variables changes.</p>



<p>On lines 6 and 7 we create two arrays. The first being an array of String that contains five vehicle manufacturers. The other line contains an array of Color.</p>



<p>Line 10 is inside the body of the view and wraps everything in a ScrollView as we have several pickers to show which are longer than the screen.</p>



<p>The first type of Picker we use is the Wheel Picker. We declare it on line 15 as a Picker, and on line 20 we apply the WheelPickerStyle in the .pickerStyle modifier. This particular picker is just a generic single wheel that you can spin up and down by providing it an array. You specify the Text for each line on line 17. Because there isn&#8217;t much to this Picker, it is recommended that you organise the contents of the array into an order that makes sense (alphabetical for example).</p>



<p>On line 25 we declare our next Picker. It is created the same as the previous one, but we change the style to SegmentedPickerStyle() on line 30. Because we bind to $selectedVehicle for this and the previous Picker, when one changes, so does the other.</p>



<p>The same Picker is used on line 35, but this time with the MenuPickerStyle() added in the view modifier.</p>



<p>The last one in this section is declared on line 45 and is set to the style &#8220;InlinePickerStyle()&#8221;. The only difference here is that we base this Picker on the selectedColor and because the array of colours that we loop over contains Colors and not Strings, we can access the colours description to provide the text, but also pass in the colours on lines 48 and 49 to set the foregroundColor and tag.</p>



<h2 class="wp-block-heading">The DatePickers</h2>



<p>Line 58 is where the the first DatePicker is declared. It uses the .datePickerStyle view modifier and uses WheelDatePickerStyle(). I took the title off of this because it squashes to the left of the picker and goes vertical. We bind to $selectedDate so that any updates here will cause any other reference to be updated.</p>



<p>The next DatePicker is declared on line 63 where we set the style to be a DefaultDatePickerStyle.</p>



<p>The final DatePicker we use is declared on line 67 which uses the GraphicalDatePickerStyle.</p>



<p>All three work similar with the exception of the first where we passed in the displayComponents we wanted to use.</p>



<p>Overall, Pickers and DatePickers provide a simple way for your users to select data either provided from an array, or select a date from the calendar. There are various options to suit your needed. Please do reach out with any questions.</p>
<p>The post <a href="https://www.devfright.com/exploring-swiftui-picker-styles-date-selection-graphical-inline-etc/">Exploring SwiftUI Picker Styles: Date, Selection, Graphical, Inline etc&#8230;</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/exploring-swiftui-picker-styles-date-selection-graphical-inline-etc/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Using .onAppear and .onDisappear in your SwiftUI Views</title>
		<link>https://www.devfright.com/using-onappear-and-ondisappear-in-your-swiftui-views/</link>
					<comments>https://www.devfright.com/using-onappear-and-ondisappear-in-your-swiftui-views/#respond</comments>
		
		<dc:creator><![CDATA[Matthew]]></dc:creator>
		<pubDate>Wed, 16 Apr 2025 11:03:51 +0000</pubDate>
				<category><![CDATA[SwiftUI]]></category>
		<guid isPermaLink="false">https://www.devfright.com/?p=2307</guid>

					<description><![CDATA[<p>If you have come from a background using UIKit, you will have worked with ViewControllers and have seen several methods from the ViewController class that were called at certain times during the view loading. An example is viewDidLoad: We override the method, and call super.viewDidLoad(), and then have time to do our own thing such [&#8230;]</p>
<p>The post <a href="https://www.devfright.com/using-onappear-and-ondisappear-in-your-swiftui-views/">Using .onAppear and .onDisappear in your SwiftUI Views</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>If you have come from a background using UIKit, you will have worked with ViewControllers and have seen several methods from the ViewController class that were called at certain times during the view loading. An example is viewDidLoad:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
override func viewDidLoad() {
    super.viewDidLoad()
    // Custom setup code
    print(&quot;View has loaded.&quot;)
}
</pre></div>


<span id="more-2307"></span>



<p>We override the method, and call super.viewDidLoad(), and then have time to do our own thing such as bringing in <a href="https://www.devfright.com/category/core-location-framework/">CoreLocation</a> and setting the delegate, or whatever else you needed. There were several of these methods:</p>



<p>viewDidLoad() &#8211; Called when the view controller is loaded into memory<br>viewWillAppear() &#8211; Called when the view is about to appear on the screen<br>viewDidAppear() &#8211; Called right after the view appears on screen<br>viewWillDisappear() &#8211; Called just prior to the view being removed from the screen<br>viewDidDisappear() &#8211; Called when the view has disappeared from the screen</p>



<p>Depending on what setup or cleanup was needed, these were the places to perform that work.</p>



<p>SwiftUI has simplified this and now has <strong>.onAppear</strong> and <strong>.onDisappear</strong> that can be used. Because of <a href="https://www.devfright.com/state-and-stateobject-property-wrappers/">@State</a>, @Binding, @ObservedObject, and so on, we now have state-driven rendering that handle views updating as needed. When changes are detected, the view will load what it needs.</p>



<h2 class="wp-block-heading">.onAppear</h2>



<p>This relaces the need for viewWillAppear and viewDidAppear. You can add this to any view as follows:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    var body: some View {
        Text(&quot;Some text&quot;)
            .onAppear {
                print(&quot;onAppear&quot;)
                // do something!!!
            }
    }
}
</pre></div>


<p>The Text view in this case has the .onAppear method applied. It gets called and completes before the first rendered frame appears. This highlights an issue that if anything takes time to process, it will hold up the entire view and not just the Text sub view. An example would be something like this:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
import SwiftUI

struct ContentView: View {
    @State private var message = &quot;Waiting...&quot;

    var body: some View {
        VStack(spacing: 20) {
            Text(&quot;Hello, World!&quot;)

            Text(message)
                .onAppear {
                    // Simulate blocking CPU work (100 million iterations)
                    var total = 0
                    for i in 0..&lt;100_000_000 {
                        total += i
                    }
                    message = &quot;Finished heavy task&quot;
                }
        }
        .padding()
    }
}
</pre></div>


<p>Nothing would appear on the screen until 100 million iterations have completed. You wouldn&#8217;t see the &#8220;Waiting&#8230;&#8221; message while it performs the loop because it completes before the first rendered frame.</p>



<p>In short, be careful what you do in .onAppear. If something takes time then its best wrapping it in a Task and using <a href="https://www.devfright.com/using-async-await-and-task-in-swiftui/">async/await</a> so that the rest of the view loads.</p>



<p>This can be done the following way:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @State private var message = &quot;Waiting...&quot;

    var body: some View {
        VStack(spacing: 20) {
            Text(&quot;Hello, World!&quot;)

            Text(message)
                .onAppear {
                    Task {
                        // Simulate blocking CPU work (100 million iterations)
                        var total = 0
                        for i in 0..&lt;100_000_000 {
                            total += i
                        }
                        message = &quot;Finished heavy task&quot;
                    }
                }
        }
        .padding()
    }
}
</pre></div>


<p>By wrapping it in Task, the view is able to load, and when the task completes, the message @State is updated and the view is refreshed. Alternatively, you can use .task { &#8230; }.</p>



<h2 class="wp-block-heading">.onDisappear</h2>



<p>The SwiftUI .onDisappear method replaces the need for viewWillDisappear() and viewDidDisappear() from UIKit. Just like heavy work can block loading of the entire view when done in .onAppear, you can also block removal of the view when using .onDisappear, so make sure you use Task when needed. Here is an example of .onDisappear delaying the view from being removed:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
struct ContentView: View {
    @State private var showingDetail = true
    @State private var status = &quot;Waiting...&quot;

    var body: some View {
        VStack(spacing: 20) {
            Text(&quot;Parent View&quot;)
            Text(&quot;Status: \(status)&quot;)

            if showingDetail {
                BlockingDetailView(onFinish: {
                    status = &quot;Heavy task finished&quot;
                })
            }

            Button(showingDetail ? &quot;Hide Detail&quot; : &quot;Show Detail&quot;) {
                showingDetail.toggle()
            }
        }
        .padding()
    }
}

struct BlockingDetailView: View {
    let onFinish: () -&gt; Void

    var body: some View {
        VStack {
            Text(&quot;Detail View&quot;)
        }
        .onDisappear {
            print(&quot;Start blocking task&quot;)

            performHeavyTask()

            print(&quot;Finished blocking task&quot;)
            onFinish()
        }
    }

    func performHeavyTask() {
        var total = 0
        for i in 0..&lt;100_000_000 {
            total += i
        }
    }
}
</pre></div>


<p>The performHeavyTask causes the view to not disappear until the heavy task completes. To correct this, we can make the following changes:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
.onDisappear {
    Task {
        await runHeavyTaskAsync()
        onFinish()
    }
}
</pre></div>


<p>Await the heavy task method.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
func runHeavyTaskAsync() async {
    await withCheckedContinuation { continuation in
        DispatchQueue.global(qos: .userInitiated).async {
            // Simulate blocking task
            var total = 0
            for i in 0..&lt;100_000_000 {
                total += i
            }
            continuation.resume()
        }
    }
}
</pre></div>


<p>Make sure the heavy task method runs asynchronously.</p>



<p>When running this new version of the code, the view will immediately dismiss while the Task does its thing. Because onFinish is also included in the task, this is called on completion and updates the parent view:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: swift; title: ; notranslate">
BlockingDetailView(onFinish: {
    status = &quot;Heavy task finished&quot;
})
</pre></div>


<p>.onAppear and .onDisappear are extremely helpful when needing to do something when a view is getting ready to appear or getting ready to disappear. Just be mindful of longer running tasks and use Task and async/await where needed.</p>
<p>The post <a href="https://www.devfright.com/using-onappear-and-ondisappear-in-your-swiftui-views/">Using .onAppear and .onDisappear in your SwiftUI Views</a> appeared first on <a href="https://www.devfright.com">DevFright</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.devfright.com/using-onappear-and-ondisappear-in-your-swiftui-views/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Object Caching 32/36 objects using Redis
Page Caching using Redis (Page is feed) 
Database Caching 1/3 queries in 0.005 seconds using Redis

Served from: www.devfright.com @ 2026-04-28 13:35:27 by W3 Total Cache
-->