
How I Handle External Links in My iOS App with a Built-In Copy Option
Have you ever tapped an email link and had it open in the wrong app and wished you could've simply copied it instead? I did. And so for my apps I created a simple reusable SwiftUI
view that lets the person tapping the link within my iOS
and macOS
apps have the power to decide whether to open or copy any external link, whether it's a website or a mailto URL
.

Introduction
When I first started building Simply Customize It I wanted to make sure that anytime a user encountered an external link, whether it was for a website or for a support-request email, they could easily choose to skip opening it and instead copy it.
I’ve run into issues in the past where tapping a mail link would open the default Mail
app instead of my preferred primary email like Gmail
or Spark
. I'd then have to copy and paste the address manually.... sometimes even having to clean it up first. Through this I realized that people using my app might want to preview the link first or just open it in a completely different context.
As such I built a simple SwiftUI
view with a built-in copy button next to the link. And recently, when I saw a Reddit
thread asking how to handle this more flexibly I figured it was time to share my version and how it works.
This solution works for email, websites, or any other URL
while offering a clean, flexible experience across both iOS
and macOS
.
What It Looks Like in My App
I use this setup in several of my apps (Simply Customize It, Simply Match It, and Simply Uncover It) mostly in the About and/or Support sections. It’s a reusable, streamlined view that displays:
- An overwritable caption (for example "Email Me" or "Visit Site")
- A right arrow to indicate it’s tappable
- A copy icon to the left that lets you grab the
URL
without opening the link
The copy button is helpful as it allows:
- Use of an alternate app (for example
Gmail
instead ofMail
) - Preview ability of where the link leads
- Copying the address to paste later or share in another way
Here’s how it appears in context:


The Code
I’ve included below my reusable SwiftUI view, DisplayLinkAndCopy
, which combines a tappable Link(destination:)
with a handy copy button for grabbing the link (or any custom string) to your clipboard. The code in its entirety is available on GitHub here but I'll break it down per view below.
The code in its entirety is available on GitHub here.


Main Wrapper View
This view wraps both the copy button and the display link. You can use it as-is or rename it to fit your own codebase. You can call DisplayLinkAndCopy
with just the URL
while also allowing flexibility with the:
copyString
letting you define something else that's copied to the clipboard allowing you to leave off themailto:
in email URLscaptionString
overwrite theURL
string with a more readable caption
import SwiftUI
public struct DisplayLinkAndCopy: View {
public var url: URL
// Below values optional as they both default to url.absoluteString
public var copyString: String?
public var captionString: String?
public var body: some View {
HStack {
CopyStringButton(myString: copyString ?? url.absoluteString)
.padding(.horizontal, 10)
Spacer()
DisplayLink(URLsource: url, captionText: captionString ?? url.absoluteString)
}.padding(.horizontal, 20)
}
}
The Copy Button
This view handles the actual clipboard logic using UIPasteboard
on iOS
and NSPasteboard
on macOS
. By default, it displays a simple copy icon doc.on.doc
but you can easily swap this out. I’ve included a commented-out alternative using a Label
which you can uncomment or adapt if you’d prefer a visible text label alongside the icon. You could even pass in a custom string if you’d like to make the button more descriptive or accessible.
import SwiftUI
public struct CopyStringButton: View {
public var myString: String
public init(myString: String) {
self.myString = myString
}
public var body: some View {
Button(action: {
#if os(macOS)
let pasteboard = NSPasteboard.general
pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil)
pasteboard.setString(myString, forType: NSPasteboard.PasteboardType.string)
#else
UIPasteboard.general.string = myString
#endif
}, label: {
Image(systemName: "doc.on.doc")
// Above is just the icon. If you prefer you can use a label with/without the label hidden:
// Label("Copy", systemImage: "doc.on.doc").labelsHidden()
})
}
}
The Display Link
This view displays the tappable caption using Link(destination:)
and includes a chevron icon to visually indicate it’s clickable by giving it a similar look to what you’d expect in a NavigationLink
. If for some reason, like bypassing the wrapper view, a URL
isn't passed in a message is gracefully shown rather than crashing the app.
import SwiftUI
public struct DisplayLink: View {
public var URLsource:URL?
public var captionText:String
public init(URLsource: URL? = nil,
captionText: String? = nil) {
self.URLsource = URLsource
self.captionText = captionText ?? "External Link"
}
public var body: some View {
if URLsource != nil {
Link(destination: URLsource!) {
HStack {
Spacer()
Text(captionText)
.multilineTextAlignment(.center)
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.secondary)
Spacer()
}
}
} else {
HStack {
Spacer()
Text("No URL set for \"\(captionText)\"")
.multilineTextAlignment(.center)
Spacer()
}
}
}
}
How to Use It
You can drop DisplayLinkAndCopy
into any SwiftUI
view. Here is an example ContentView
with a few examples that work in both iOS
and macOS
:
import SwiftUI
struct ContentView: View {
let websiteString = "https://www.simplykyra.com/blog/how-i-handle-external-links-in-my-ios-app-with-a-built-in-copy-option"
let emailString = "mail@simplykyra.com?subject=Display Link Blog Post"
var body: some View {
VStack(alignment: .center, spacing: 10, content: {
Text("Here are the examples using both a website and an email.")
Divider()
Text("You can set just the url and by default both caption and the copy value will be the same.")
DisplayLinkAndCopy(
url: URL(string: websiteString)!
)
Text("Or overwrite the caption with your own text:")
DisplayLinkAndCopy(
url: URL(string: websiteString)!,
captionString: "Click link for blog post!"
)
Text("Email might be more complicated as the action url includes \"mailto:\" and optional add-ons like the subject so you may want to overwrite the copy text like this:")
DisplayLinkAndCopy(
url: URL(string: "mailto:\(emailString)")!,
copyString: emailString,
captionString: "Mail Me Here!"
)
})
.multilineTextAlignment(.center)
}
}
You can drop this into any SwiftUI
view with no extra setup needed and have it work for both iOS
and macOS
. I love that the customizable captionString
and copyString
values makes this view work for emails, deep links, or complex URLs.
Optional Customizations
Want to tweak it? Here are some quick ideas:
- Change the icon or add a
Label()
for accessibility - Show a confirmation toast or alert after it copies the string
- Add haptic feedback (on iOS)
- Make the copy button optional or revealed on long press
If you do end up customizing this I’d love to see what you come up with so feel free to reach out! You can comment below or send out an email! If you want to follow and comment the links to all of my socials are in this website's footer so check it out below!
Final Thoughts
This small reusable view gives me an easy way to share external links without worrying about where they’ll open and where they're expected to open to. It lets your app's audience choose what works for them while working smoothly across iOS
and macOS
in SwiftUI
.
If you'd rather see the code in one entire file you can grab it from GitHub repo right here. If you do end up using this or building something even better I’d love to hear about it!
Have a variation you want to share? Leave a comment, tag me, or reach out via email.
Hope you’re having a great day and happy building!
And don't forget if you want more posts like this I regularly share new ones to Facebook and Instagram or you can join my email list under the search bar or at the bottom of this post.