[iOS - SwiftUI] Text에서 link 감지하고 인앱브라우저로 열기 prefersInApp

2025. 11. 8.



SwiftUI 3.0, iOS15부터는 Text(.init(보여줄_텍스트)) 라고만 쳐도
알아서 텍스트 내 link를 감지하고 클릭가능한 링크로 포맷팅해준다.

Text(.init("링크를 넣으면 www.google.com 클릭 가능해요. 여러개도 가능해요 https://naver.com"))
    .padding(.horizontal, 14)
    .padding(.vertical, 10)
    .cornerRadius(12)
    //...


 
근데 문제는 알아서 일을 너무 잘 한 나머지 바로 외부 브라우저로 url이 열린다.
사용자가 앱에서 이탈하는 것을 막기 위해서 inapp 브라우저로 띄우려고 또 한참 찾아본 결과!
iOS26부터 지원 되는 기능이 있다는 것을 알게 됐다..!! 굉장한 신규 기능!

요걸 해보려고 하니 맥북부터 업데이트를 해야해서리..ㅋㅋ 업데이트 눌러놓고 블로그 적기 시작함


뭐 막 UIKit이랑 연동하고 어쩌고 저쩌고 하는 방법도 있는 거 같지만
새 기능으로 훨씬 간단하게 되는 거 같아서 일단 26+ 이후로는 이걸로 구현 + 이전 버전은 그냥 외부 브라우저로 두기로 했다.

 


 

(결론이 궁금하면 아래로..!!)

 

환경변수에서 openURL 키값으로 설정된 OpenURLAction 인스턴스를 가져와서

함수처럼 실행하는 게 일단 기본이다.

 

Button과 사용 시 - iOS26 이전

아래 첫번째 예시(OpenURLExample)처럼 

Button 을 눌러서 url을 열게 할 때 사용하면 된다.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension EnvironmentValues {

    /// An action that opens a URL.
    ///
    /// Read this environment value to get an ``OpenURLAction``
    /// instance for a given ``Environment``. Call the
    /// instance to open a URL. You call the instance directly because it
    /// defines a ``OpenURLAction/callAsFunction(_:)`` method that Swift
    /// calls when you call the instance.
    ///
    /// For example, you can open a web site when the user taps a button:

     struct OpenURLExample: View {
         @Environment(\.openURL) private var openURL //여기서 환경변수 데려와서

         var body: some View {
             Button {
                 if let url = URL(string: "https://www.example.com") {
                     openURL(url) //여기서 써먹으면 됨
                 }
             } label: {
                 Label("Get Help", systemImage: "person.fill.questionmark")
             }
         }
     }

    /// If you want to know whether the action succeeds, add a completion
    /// handler that takes a Boolean value. In this case, Swift implicitly
    /// calls the ``OpenURLAction/callAsFunction(_:completion:)`` method
    /// instead. That method calls your completion handler after it determines
    /// whether it can open the URL, but possibly before it finishes opening
    /// the URL. You can add a handler to the example above so that
    /// it prints the outcome to the console:
    ///
    ///     openURL(url) { accepted in
    ///         print(accepted ? "Success" : "Failure")
    ///     }
    ///
    /// The system provides a default open URL action with behavior
    /// that depends on the contents of the URL. For example, the default
    /// action opens a Universal Link in the associated app if possible,
    /// or in the user’s default web browser if not.

 

Text와 사용 시 - iOS26 이전

Text로 쓸 때에는 이렇게 .environment로 직접 OpenURLAction을 변경해줄 수 있다.

내가 원하는대로 처리하고, return .handled 요렇게 반환해주면 된다.

    /// You can also set a custom action using the ``View/environment(_:_:)``
    /// view modifier. Any views that read the action from the environment,
    /// including the built-in ``Link`` view and ``Text`` views with markdown
    /// links, or links in attributed strings, use your action. Initialize an
    /// action by calling the ``OpenURLAction/init(handler:)`` initializer with
    /// a handler that takes a URL and returns an ``OpenURLAction/Result``:

     Text("Visit [Example Company](https://www.example.com) for details.")
         .environment(\.openURL, OpenURLAction { url in
             handleURL(url) // Define this method to take appropriate action.
             return .handled
         })

    /// SwiftUI translates the value that your custom action's handler
    /// returns into an appropriate Boolean result for the action call.
    /// For example, a view that uses the action declared above
    /// receives `true` when calling the action, because the
    /// handler always returns ``OpenURLAction/Result/handled``.
    @MainActor @preconcurrency public var openURL: OpenURLAction
}

 

이때 return 값으로 handled, discarded, systemAction, systemAction(_ url: URL) 이 가능했다. (iOS15+ 까지는!)

systemAction으로 반환하면 기본 시스템 처리 액션이 진행돼서 외부 브라우저에서 url이 열리게 되는 구조다.

 

 

결론: iOS26+ 

Text와 사용 시 - iOS26 +

방금 말한 return 값에 이번에 요게 하나 더 추가됐다!

@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *)
        public static func systemAction(_ url: URL? = nil, prefersInApp: Bool) -> OpenURLAction.Result

 

그래서 아래와 같이 쓰면 인앱브라우저가 우선으로 열리게 된다!

Text(.init(message.text))
    .environment(\.openURL, OpenURLAction { _ in
		.systemAction(prefersInApp: true) //요렇게
    })

 

근데 꼭 이렇게 하지 않아도, onOpenURL 함수를 이용해도 된다. 참고

Text(.init(message.text))
	.onOpenURL(prefersInApp: true)

 

이렇게 하면 인앱브라우저에서 우선 열기 완성!

 

 

Button과 사용 시 - iOS26 +

버튼 예시로 돌아와서,

버튼에서도 마찬가지로 사용가능하다!

https://youtu.be/S1lGyjiExq4 에 나온 예시

struct ContentView: View {
	@Environment(\.openURL) var openURL
    
    var body: some View {
    	Button {
        	openURL(URL(string: "https://www.apple.com")!, prefersInapp: true) //이 부분
        } label: {
        	Label("Go to Apple", systemImage: "applelogo")
        }
    }
}

 

 

 


참고:
https://www.reddit.com/r/SwiftUI/comments/1e6onsg/how_to_autodetect_links_in_a_string/
https://stackoverflow.com/questions/66813822/how-to-highlight-and-clickable-if-text-is-url-swiftui
https://fatbobman.com/en/posts/open_url_in_swiftui/
https://youtu.be/S1lGyjiExq4
https://developer.apple.com/documentation/swiftui/view/onopenurl(prefersinapp:)/

 

 

 

 

댓글