← 모든 글

canOpenURL 지원 중단: 대신 무엇을 호출해야 하나

Apple은 iOS 3.0부터 함께해 온 메서드를 세 문장으로 퇴역시켰습니다. “canOpenURL:은 지원이 중단되었습니다. 먼저 검증하지 말고, URL을 열어 본 다음 실패를 처리하십시오. 커스텀 URL 스킴 대신 유니버설 링크를 쓰면 이런 검증 자체가 필요 없어집니다.”1

이 항목에는 레이더 179874781이 붙어 있고, UIKit의 Deprecations 아래, 앱 실행 자체를 막는 씬 생명주기 의무화 항목 바로 밑에 놓여 있습니다.1 이웃한 항목은 어떤 결과가 따르는지 분명히 밝힙니다. canOpenURL 항목은 아무 결과도 밝히지 않습니다.

세 문장은 각각 별개의 지시이고, 개발자가 실제로 품는 질문에 답하는 것은 두 번째 문장뿐입니다. 그래서 대신 무엇을 써야 하는가. Apple의 답은 이름만 바꾸는 것이 아니라 호출의 형태를 바꿉니다.

핵심 요약

  • Apple은 iOS, iPadOS, Mac Catalyst, tvOS, visionOS에서 27.0부터 canOpenURL(_:)의 지원을 중단하면서 “URL을 열어 보고 실패를 처리하는 쪽을 택하십시오”라는 메시지를 남겼습니다.2 제거 버전도, 런타임에서 달라지는 동작도 없습니다.
  • 실제로 위력이 있는 변화는 숫자 하나이고, 그것마저 릴리스 노트가 아니라 지원 중단된 메서드 자신의 설명 안에 들어 있습니다. “iOS 27 이상으로 링크한 앱은 LSApplicationQueriesSchemes 키에 최대 25개 항목까지만 넣을 수 있습니다.” 이전 상한 50개에서 절반입니다.2 발동 조건은 여러분이 링크하는 SDK입니다.
  • 기계적인 치환 대상은 open(_:options:completionHandler:)와 그 Boolean 반환값이며, 허용 목록 항목이 아예 필요 없습니다. “open(_:options:completionHandler:) 메서드는 LSApplicationQueriesSchemes 요구 사항의 제약을 받지 않습니다.”2
  • 대체 수단을 잃는 패턴이 하나 있습니다. canOpenURL은 화면에 무엇을 그리기도 전에 답을 주지만, 열어 보는 방식은 결과로만 답하기 때문입니다. 성공하면 다른 앱이 전면으로 올라옵니다.2 살아남은 것은 universalLinksOnly입니다. iOS 10부터 있던 open 옵션이고 Apple은 이것을 지원 중단하지 않았습니다. 이 옵션은 “URL이 유효한 유니버설 링크이고 그 URL을 열 수 있는 앱이 설치되어 있을 때만 URL을 엽니다.”3
  • SwiftUI에는 이 메서드가 처음부터 없었고, 완료 클로저의 Boolean은 이미 “열었는지”가 아니라 “열 수 있는지”를 뜻합니다.415 제가 운영 중인 앱 7개, 자체 작성 Swift 파일 463개를 통틀어 canOpenURLLSApplicationQueriesSchemes는 각각 0회 등장합니다.6

결과가 없는 지원 중단, 그리고 결과가 있는 숫자

Apple이 쓴 것은 지원 중단이지 제거가 아닙니다. 심볼 페이지는 UIApplication이 존재하는 다섯 플랫폼 모두에서 deprecatedAt 27.0을 달고 있고, 개요와 설명, 반환값 계약, 선언은 그대로 남아 있습니다.2 이 메서드가 언제부터 동작을 멈추는지 명시한 Apple 페이지는 없으며, 가장 가까운 선례는 오히려 반대 방향을 가리킵니다. iOS 10.0에서 지원이 중단된 openURL(_:)은 지금도 문서 페이지가 살아 있고, 그 주석은 이제 “이 메서드를 호출해도 아무 일도 일어나지 않습니다”라고 적혀 있습니다.7 10년의 지원 중단이 만들어 낸 것은 사라진 메서드가 아니라 아무 일도 하지 않는 메서드였습니다. 그것은 일정표가 아니라 선례입니다.

발표의 범위 역시 지원 중단의 범위보다 좁습니다. 이 릴리스 노트는 Apple이 “iOS & iPadOS 27 Beta 4 Release Notes”라고 제목을 붙인 페이지에만 있고 다른 어디에도 없는 반면, 심볼 메타데이터는 tvOS와 visionOS에서도 똑같이 이 메서드를 지원 중단으로 표시합니다. 그리고 2026년 6월 자 Apple UIKit 업데이트 페이지도, Xcode 27 릴리스 노트도 이 변경을 전혀 언급하지 않습니다.1289 여기서 오래 남을 기록은 심볼 페이지 쪽입니다.

그래서 진짜 이야기는 파묻힌 숫자입니다. 지원 중단된 메서드 자신의 설명, 애초에 그 선언을 요구하는 바로 그 참고 문단 안에 이렇게 적혀 있습니다. “iOS 15 이상으로 링크한 앱은 LSApplicationQueriesSchemes 키에 최대 50개 항목까지 넣을 수 있습니다. iOS 27 이상으로 링크한 앱은 LSApplicationQueriesSchemes 키에 최대 25개 항목까지 넣을 수 있습니다.”2

허용 목록은 자신이 뒷받침하던 유일한 메서드의 지원 중단보다 오래 살아남고, 상한은 새 SDK로 링크한 앱에서 절반이 됩니다. Apple은 상한만 말하고 거기서 멈춥니다. 26번째 항목부터 어떻게 되는지는 어디에도 없고, 참고 문단의 두 문장을 겹쳐 읽으면 문서화된 답이 아니라 그럴듯한 답이 나옵니다. 선언되지 않은 스킴은 항상 false를 반환하므로, 스킴 40개를 선언한 앱이 다시 링크하면 그중 15개를 설치되지 않은 것처럼 다루기 시작할 공산이 큽니다. 어느 15개인지, 애초에 잘림이 배열 순서를 따르기는 하는지 Apple은 말한 적이 없습니다. 메커니즘은 추론으로, 위험은 실재하는 것으로 다루십시오. 앱이 없어서 나온 false와 선언이 없어서 나온 false는 겉보기에 똑같기 때문입니다.

같은 설명의 다음 문단에는 두 번째 제한이 나오는데, 둘은 더해지는 것이 아니라 서로 배타적인 갈래입니다. Apple은 이를 여러분이 링크한 SDK 기준으로 가릅니다. “앱을 이전 버전의 iOS로 링크했지만 iOS 9.0 이상에서 실행 중이라면, 이 메서드를 최대 50번까지 호출할 수 있습니다. 그 한도에 도달하면 이후 호출은 항상 false를 반환합니다. 사용자가 앱을 재설치하거나 업그레이드하면 iOS가 한도를 초기화합니다.”2 조건을 보십시오. 이 호출 예산은 iOS 9 이전으로 링크한 앱의 몫이고, 오늘 출시할 수 있는 앱 중에 그런 것은 없습니다. 현재의 모든 앱은 다른 갈래, 곧 선언 상한 아래에 있으며, Apple이 50개에서 25개로 자른 것이 바로 이쪽입니다. 둘 다 소진되면 똑같이 조용한 false로 끝나고, 둘 다 Apple이 방금 지원 중단한 그 메서드 안에만 문서화되어 있습니다.

프라이버시 관점의 해석 역시 추론입니다. canOpenURL은 어떤 사람이 무슨 앱을 설치했는지 알아내는 표준적인 방법이었고, 이는 링크 확인이라기보다 기기 신호에 가깝습니다. 그리고 Apple 자신의 핑거프린팅 정의는 “기기나 사용자를 식별하려고 기기 신호에 접근하는 데 오용될 수 있는” API를 포함합니다.10 그런데 Apple은 둘을 연결한 적이 없습니다. 이 메서드는 필수 사유(required reason) API 목록 어디에도 없고, 릴리스 노트와 지원 중단 메시지, 심볼 페이지 모두 순전히 기계적인 근거만 제시합니다.1210 질의 상한을 절반으로 줄인 조치는 프라이버시 서사와 잘 맞습니다. 다만 Apple은 그렇게 적지 않았습니다.

canOpenURL이 실제로 약속한 것

true는 URL에 대한 설명이 아니라 다음 호출에 대한 보증이었습니다. “이 메서드가 true를 반환하면, 같은 URL로 open(_:options:completionHandler:) 메서드를 이어 호출했을 때 그 URL을 처리할 수 있는 앱이 반드시 실행된다는 것을 iOS가 보증합니다.”2 반면 false는 설계상 모호했습니다. 원인이 둘인데 어느 쪽인지 가려낼 방법이 없었습니다. “기기에 해당 URL의 스킴을 처리하도록 등록된 앱이 설치되어 있지 않거나, Info.plist 파일에 해당 URL의 스킴을 선언하지 않은 경우 false입니다.”2

답해 준 적이 없는데도 답해 준다고 착각하기 쉬운 것이 셋 있습니다. “반환값은 URL의 유효성, 지정한 리소스의 존재 여부, 그리고 유니버설 링크의 경우 그 링크에 응답하도록 등록된 앱이 기기에 설치되어 있는지를 나타내지 않습니다.”2 세 번째 조항은 Apple이 권하는 마이그레이션에서 중요해지며, 아래에서 다시 다룹니다.

이 메서드를 구조적으로 쓸모 있게 만들던 성질은 무엇으로도 대체되지 않습니다. canOpenURL은 선언에 nonisolated 키워드를 달고 있고, Apple은 “이 메서드는 메인 스레드가 아닌 스레드에서도 안전하게 호출할 수 있습니다”라고 분명히 말합니다.2 메인 액터 밖에서 쓸 수 있는 동기 Boolean은 아직 아무것도 그려지기 전에 레이아웃 결정을 가를 수 있습니다.

시도한 뒤 처리하기: 달라지는 호출의 형태

Apple이 내놓은 대체 지침은 한 구절뿐이고, 변경 전후는 사소해 보입니다.

// Before: validate, then open. Requires an Info.plist declaration.
let url = URL(string: "someapp://profile/42")!
if UIApplication.shared.canOpenURL(url) {
    UIApplication.shared.open(url)
} else {
    presentWebFallback()
}
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>someapp</string>
</array>
// After: attempt, then handle. No declaration needed.
let url = URL(string: "someapp://profile/42")!
let opened = await UIApplication.shared.open(url)
if !opened {
    presentWebFallback()
}

Info.plist 항목은 사라집니다. Apple이 직접 그렇게 말합니다. “이 메서드와 달리 open(_:options:completionHandler:) 메서드는 LSApplicationQueriesSchemes 요구 사항의 제약을 받지 않습니다. URL을 처리할 수 있는 앱이 있다면, 스킴을 선언하지 않았더라도 시스템이 그 앱을 실행합니다.”2 완전히 옮겨 간 코드베이스는 이 키를 지우게 되고, 그와 함께 25개 제한 문제도 없어집니다.

다시 쓴 뒤에도 남는 차이가 둘 있고, 둘 다 구조적입니다.

첫째는 격리와 시점입니다. UIApplication의 선언은 @MainActor class UIApplication이므로 open은 메인 액터에서 실행되고, 개요 문장은 그 성격을 있는 그대로 말합니다. “지정한 URL의 리소스를 비동기적으로 열려고 시도합니다.”1112 메인 스레드 밖에서 쓰던 동기 Boolean은 사라졌습니다. 그래서 모델 계층이나 백그라운드 큐, 격리되지 않은 헬퍼에 놓여 있던 “검증 후 열기” 로직은 자리를 옮기거나 async가 되어야 합니다.

둘째는 이제 조용히 물어볼 수 없다는 점입니다. 시도가 성공하면 다른 앱이 전면에 나와 있습니다. “iOS가 그 앱을 실행하고 URL을 전달합니다. (앱을 실행하면 그 앱이 전면으로 올라옵니다.)”12 답이 행동의 부수 효과로 도착하는 것입니다. 실패하는 경우에 대해 Apple은 사용자에게 보이는 무언가를 문서화하지 않고 “완료 핸들러가 success 매개변수 false와 함께 호출됩니다”라고만 적습니다. 그러나 행동에 앞서 답이 필요했던 모든 패턴은 그 답을 잃습니다. 앱이 있을 때만 “다른 앱에서 열기” 행을 보여 주는 것, 설치된 앱에 맞춰 공유 시트 순서를 정하는 것, 경쟁하는 대상 중에서 기본값을 고르는 것이 그렇습니다.12

Apple 자신의 문서도 아직 따라오지 못했습니다. open(_:options:completionHandler:) 페이지는 여전히 이렇게 안내합니다. “URL을 처리할 수 있는 앱이 설치되어 있는지 확인하려면, 이 메서드를 호출하기 전에 canOpenURL(_:) 메서드를 호출하십시오.”12 대체 메서드의 문서가 지원 중단된 메서드를 권하고 있는 셈입니다.

살아남은 설치 여부 확인

문서화된 경로 하나는 직접 열어 보는 방식으로 canOpenURL의 질문에 답하면서도, 답이 “아니오”일 때 사용자에게 아무것도 보여 주지 않습니다. UIApplication.OpenExternalURLOptionsKey.universalLinksOnly는 iOS 10부터 존재했고, Apple은 이를 지원 중단하지 않았으며, 그 동작은 정확히 설치 여부 확인입니다. “이 메서드는 URL이 유효한 유니버설 링크이고 그 URL을 열 수 있는 앱이 설치되어 있을 때만 URL을 엽니다.”3

// Presence check with no side effect when the app is absent.
let url = URL(string: "https://myphotoapp.example.com/albums?albumname=vacation")!
let installed = await UIApplication.shared.open(
    url,
    options: [.universalLinksOnly: true]
)
if !installed {
    presentWebFallback()   // nothing opened, nothing switched
}

값진 쪽은 false 분기입니다. 브라우저도 뜨지 않았고, 앞으로 나선 앱도 없으며, 호출자는 예전에 canOpenURL이 알려 주던 사실을 그대로 얻습니다. 대가는 옵션 이름 안에 숨어 있습니다. 이 옵션이 없으면 https 시도는 브라우저가 받아 줄 수 있는 한 언제나 성공합니다. “유니버설 링크를 처리할 앱이 없으면 iOS는 그 링크를 사용자의 기본 브라우저로 넘겨, 연결된 웹사이트가 응답하도록 합니다.”2 이 옵션은 유니버설 링크를 기분 좋게 만들어 주는 대체 경로를 억누르는 대가로 의미 있는 Boolean을 얻는 셈입니다. Apple은 이 값의 타입을 “Boolean 값을 담은 NSNumber 객체”로 규정하며, Swift의 true는 Objective-C 브리징을 통해 그 조건을 만족합니다.3

대가는 구조적이고, 그것이 바로 Apple의 세 번째 문장입니다. 유니버설 링크에는 양방향 연결이 필요합니다. “사용자가 앱을 설치하면, 시스템은 웹 서버에 저장된 파일을 확인해 여러분의 웹사이트가 앱을 대신해 URL을 열도록 허용하는지 검증합니다. 이 파일은 오직 여러분만 서버에 올릴 수 있으며, 이것이 웹사이트와 앱의 연결을 안전하게 만듭니다.”13 여러분이 통제하는 서버 파일은 커스텀 스킴이 한 번도 요구하지 않던 바로 그것입니다. 그래서 이 마이그레이션은 자기 앱 패밀리 안에서는 통하지만, theirapp://만 공개하는 제3자에 대해서는 아무것도 해 주지 못합니다. 문서화된 의외의 동작 둘이 함께 따라옵니다. 자기 앱이 자기 유니버설 링크를 열면 그 링크는 자기 앱으로 들어오지 않고, Safari에서 자기 사이트를 보다가 같은 도메인 링크를 누르면 Safari에 그대로 머무릅니다.13

앞서 미뤄 둔 세 번째 조항이 바로 여기에 떨어집니다. canOpenURL은 유니버설 링크에 대해서도 설치 여부 질문에 답한 적이 없습니다.2 그러니 마이그레이션은 설치 여부 감지를 옮겨 오는 일이 아니라 없애는 일입니다. 평범한 https 시도는 앱이 응답했든 웹사이트만 응답했든 어차피 성공하기 때문입니다. 커스텀 스킴이 설치 상태를 흘려보낸 덕분에만 성립하던 패턴은 스킴과 함께 떠나고, Apple의 릴리스 노트는 바로 그 상실을 마이그레이션의 이유로 내세웁니다.

SwiftUI에는 애초에 이 메서드가 없었습니다

SwiftUI 쪽 이야기는 짧고, 소식은 좋습니다. EnvironmentValues.openURLOpenURLAction, Link는 모두 iOS 14.0부터 제공되며 지원 중단 표시가 없고, 셋 중 어느 것도 유효성 검사를 노출하지 않습니다.4514

SwiftUI가 대신 내놓는 것은 Apple이 이제 모든 곳에서 바라는 그 의미론을 담은 “시도하고 처리하기”이며, 완료 매개변수 문서는 이 Boolean이 예전 질문에 답한다고 말합니다. “URL을 열 수 있는지 판단한 뒤, 다만 URL을 완전히 열기 전에 호출될 수도 있는 클로저입니다. 이 클로저는 메서드가 URL을 열 수 있는지를 나타내는 Boolean 값을 받습니다.”15 열었는지가 아니라 열 수 있는지입니다. Apple 자신의 예제도 그것을 출력합니다.

openURL(url) { accepted in
    print(accepted ? "Success" : "Failure")
}

Link는 Boolean을 전혀 노출하지 않고 환경 값에 위임하며, 그 기본 동작은 이미 유니버설 링크 이야기를 그대로 구현하고 있습니다. “기본 동작은 가능하면 연결된 앱에서 유니버설 링크를 열고, 그렇지 않으면 사용자의 기본 웹 브라우저에서 엽니다.”5 .handled, .discarded, .systemAction을 반환하는 커스텀 OpenURLAction은 모든 Link와, 그 환경 값을 읽는 Text 안의 모든 마크다운 링크를 가로챕니다.5

SwiftUI만으로 마이그레이션을 계획하기 전에 짚어야 할 빈틈이 하나 있습니다. OpenURLAction에는 옵션 딕셔너리가 없습니다. 호출 시그니처는 callAsFunction(_:), callAsFunction(_:completion:), 그리고 iOS 26에서 추가된 callAsFunction(_:prefersInApp:) 셋뿐이고, 어느 것도 universalLinksOnly를 받지 않습니다.1518 부수 효과 없는 설치 여부 확인은 여전히 UIApplication.shared.open 호출을 뜻합니다.

앱 7개, 파일 463개, 호출 0건

남의 코드를 이야기하기 전에 제 포트폴리오부터 감사했고, 마이그레이션 목록이 나올 줄 알았습니다. 옮길 것이 하나도 없었습니다.6

Swift 파일 canOpenURL LSApplicationQueriesSchemes URL 열기 호출 지점
Reps 77 0 0 5
Return 57 0 0 2
Banana List 55 0 0 2
Ace Citizenship 26 0 0 2
Water 34 0 0 0
ResumeGeni 71 0 0 12
Yawara 143 0 0 0
합계 463 0 0 23

이 0은 범위를 세 번 넓히는 동안에도 살아남았습니다. 벤더링된 모든 Swift 패키지, 모든 저장소의 모든 파일 형식까지 확장해도 마찬가지였습니다.6 192개의 .plist, .pbxproj, .entitlements, .xcconfig 파일 어디에도 질의 스킴 선언이 없는데, 이는 당연한 귀결입니다. canOpenURL 호출이 없는 앱은 스킴을 선언할 이유가 없습니다.

이유는 시시하고, 아마 흔합니다. 호출 지점 23곳 가운데 9곳은 법적 문서를 열고, 4곳은 앱 자체의 웹 제품으로 넘기며, 2곳은 사용자가 자기 지역 대표를 찾을 수 있도록 경고창에서 census.gov를 엽니다. 2곳은 API에서 받아 온 채용 공고 URL을 열고, 2곳은 macOS 전용 file: 내보내기이며, 1곳은 “Health Access Required” 경고 뒤에 붙은 UIApplication.openSettingsURLString입니다. 이 20곳 중 다른 앱에 질의하는 것은 하나도 없습니다. 목적지가 전부 Safari가 언제나 응답하는 https이거나 시스템 URL이기 때문입니다. 흥미로운 동작은 나머지 3곳에 몰려 있습니다.

열기 전에 검증하는 유일한 지점은 이번 지원 중단이 겨냥하는 일과 다른 일을 합니다. ResumeGeni의 결제 흐름은 /api/me/portalPOST한 뒤 서버가 돌려준 URL에 대해 url.scheme == "https"를 확인하는데, 침해되었거나 버그가 있는 서버가 file:이나 커스텀 스킴 URL을 건넬 경우에 대비한 것입니다.6 Apple의 노트는 대상 앱이 존재하는지 검증하는 행위를 겨냥할 뿐 원격 입력을 신뢰하는 문제에는 아무 말도 하지 않으므로, 저 가드를 지우는 것은 마이그레이션이 아니라 보안 퇴행입니다.

나머지 둘은 Banana List의 macOS 핸드오프이고, 그중 하나가 포트폴리오 전체에서 유일하게 진짜 설치 여부를 확인하는 곳입니다. 이 앱은 번들된 .mcpb 확장을 Launch Services에 넘기기 전에 NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.anthropic.claudefordesktop") != nil을 묻고, 바로 옆 버튼은 앱이 먼저 필요한 사람을 위해 claude.com/download를 엽니다. 코드 주석이 동기를 밝힙니다. 이 확인이 없으면 macOS가 자체적으로 “이 문서를 열도록 설정된 응용 프로그램이 없습니다” 대화상자를 띄웁니다.6 플랫폼은 다르지만, 이번 감사에서 가장 쓸모 있는 데이터 지점입니다. “시도하고 처리하기”의 실패 양상을 실제 환경에서 붙잡아 보여 주기 때문입니다. 실패한 시도가 언제나 조용한 것은 아니며, 시스템이 대신 말해 버리면 사용자는 여러분이 준비한 대체 경로 대신 시스템 오류를 읽게 됩니다.

어떤 저장소도 applinks:를 선언하지 않으므로 여기 있는 앱 중 유니버설 링크를 지원하는 앱은 없고, Apple이 제시한 구조적 해법의 비용은 제 쪽에서도 아직 치르지 않았습니다.6 커스텀 스킴의 비용은 Info.plist 항목 하나지만, 유니버설 링크의 비용은 웹 서버에 올리는 파일 하나와 권한 설정(entitlement), 그리고 여러분이 소유한 도메인입니다.

여기서 진짜 작업이 어디에 있는지가 드러납니다. 자체 앱 코드가 아니라, 설치된 앱에 따라 순서를 바꾸는 공유 시트, “다른 앱에서 열기” 선택기, 그리고 어트리뷰션 SDK입니다. 저장소가 깨끗하다고 해서 이들이 없다는 뜻은 아닙니다.

감사 자체는 27 사이클의 다른 항목들보다 여기서 더 쉽습니다. 빌드 설정에서 들어와 텍스트 검색을 통째로 무력화하는 런치 스크린 키와 달리, LSApplicationQueriesSchemes에는 Apple 빌드 설정 레퍼런스에 대응하는 INFOPLIST_KEY_ 항목이 없습니다. 그래서 grep이 올바른 첫 수이며, 그 배열에 어떤 스킴이 들어 있는지를 보면 어느 의존성이 그 답을 원하는지 알 수 있습니다.16

자주 묻는 질문

iOS 27에서 canOpenURL이 동작을 멈추나요?

아닙니다. 언제 멈출지도 Apple은 말한 적이 없습니다. 심볼에는 deprecatedAt 27.0이 붙어 있지만, 반환값 계약, 뒤따르는 open 호출에 대한 보증, 선언 상한, 레거시 호출 예산을 포함한 페이지의 나머지 부분은 전부 문서로 남아 있습니다.2 제거 버전은 릴리스 노트에도, 심볼 페이지에도, Xcode 27 릴리스 노트에도 등장하지 않습니다.129 갑작스러운 단절이 아니라 경고와 느린 쇠퇴를 전제로 계획하십시오.

앱 설치 여부를 알아야 한다면 대신 무엇을 써야 하나요?

대상 앱을 여러분이 통제하는지에 달렸습니다. 통제한다면 유니버설 링크를 게시하고 openuniversalLinksOnly를 전달하십시오. Apple은 이 옵션을 두고 URL이 유효한 유니버설 링크이면서 그것을 받을 앱이 설치되어 있을 때만 연다고 문서화했으므로, false는 아무것도 열리지 않았고 아무것도 전환되지 않았다는 뜻입니다.3 비용은 웹 서버에 올리는 양방향 연결 파일입니다.13 제3자가 커스텀 스킴만 공개한다면 대체 수단은 없습니다. canOpenURL은 여전히 답하고, 여전히 Info.plist 선언을 요구하며, iOS 27 이상으로 링크한 앱에서는 그 선언의 상한이 25개로 절반이 됩니다.2 SwiftUI 코드는 이 부분에서 바꿀 것이 없습니다. openURLLink는 애초에 그런 검사를 제공한 적이 없기 때문입니다.414

LSApplicationQueriesSchemes가 여전히 필요한가요?

canOpenURL을 쓸 때만 필요합니다. Apple의 Launch Services 문서는 이 키를 전적으로 지원 중단된 메서드를 기준으로 정의합니다. 이 키는 “UIApplication 클래스의 canOpenURL: 메서드와 함께 앱이 사용할 수 있도록 하려는 URL 스킴을 지정합니다.”17 이 키는 Apple의 현행 Information Property List 레퍼런스에 페이지 자체가 없고, 지원 중단된 메서드의 설명은 이 키에 대해 레거시 아카이브로 링크를 겁니다.217 대체 메서드는 아무것도 요구하지 않습니다. Apple이 open을 선언 요구 사항에서 아예 면제했기 때문입니다.2 마이그레이션을 끝내면 배열은 사라지고, 호출을 하나라도 남기면 배열과 선언 요구 사항, 그리고 줄어든 상한까지 함께 남습니다.

항목이 25개를 넘으면 App Store에서 거부되나요?

그렇게 적어 둔 Apple 페이지는 없습니다. 이 상한은 오직 한 곳, canOpenURL(_:)의 설명에만 등장하며, Apple은 이를 제출 규칙이 아니라 그 키가 담을 수 있는 양의 제한으로 서술합니다.2 iOS 27 릴리스 노트에도, UIKit 업데이트 페이지에도, Xcode 27 릴리스 노트에도 심사상의 결과를 붙인 문구는 없고, 시스템이 선언되지 않은 것으로 취급하는 스킴에 대해 문서화된 증상은 런타임에서 그냥 false가 돌아오는 것뿐입니다.1289 App Store 심사 지침에는 LSApplicationQueriesSchemes도, canOpenURL도, 25개라는 숫자도 등장하지 않는데, 제출 규칙이 존재했다면 바로 거기에 있어야 합니다.19 그러니 대비해야 할 실패는 거부된 빌드가 아니라 여러분 코드가 내놓는 잘못된 답입니다.

핵심 정리

iOS 개발자에게: - if canOpenURL(url) { open(url) }let opened = await open(url)!opened 분기의 대체 경로로 바꾸고, 호출이 하나도 남지 않으면 그에 대응하는 LSApplicationQueriesSchemes 항목을 지우십시오.2 - 서버에서 받아 온 URL에 대해 수행하는 스킴 검사는 그대로 두십시오. Apple의 노트는 앱 존재 여부 검증을 겨냥할 뿐 원격 입력 문제는 다루지 않는데, 이 둘은 grep에서 똑같아 보입니다.

앱 패밀리 딥링크나 SDK를 출시하는 팀에게: - 설치 여부에 대한 답이 필요하다면 canOpenURL 대신 universalLinksOnly를 잡고, 그것이 요구하는 연결 도메인 파일의 비용을 예산에 넣으십시오.313 앱이 없을 때 사용자에게 아무것도 보여 주지 않는, 문서화된 유일한 검사입니다. - iOS 27 SDK로 링크하기 전에 LSApplicationQueriesSchemes 배열의 개수를 세어 보십시오. 25개를 넘는 항목은 문서화된 상한 바깥에 놓이고, 선언되지 않은 스킴에 대해 Apple이 밝힌 증상은 그냥 false인데, 이는 앱이 설치되지 않은 경우와 완전히 똑같이 읽힙니다.2

릴리스 관리자에게: - 이 지원 중단을 릴리스 차단 항목으로 잡지 마십시오. 제거 일정도 없고 런타임에서 달라지는 것도 없으므로, 거부를 부르는 런치 스크린 키, 실행 자체를 막는 씬 의무화, 빌드를 멈추는 @State 매크로보다 뒤에 놓입니다. - 실제로 발동 조건이 있는 항목은 25개 상한 하나입니다. 사용자가 쓰는 OS가 아니라 여러분이 링크하는 SDK를 기준으로 삼기 때문입니다.2


27 사이클은 저마다 다른 무게로 계속 도착하고, 그 무게를 읽어 내는 것이 사이클을 잘 쓰는 방법입니다. 런치 스크린 키는 제출을 막고, 씬 의무화는 앱 실행을 막고, @State 매크로는 빌드를 멈추며, On Demand Resources는 마이그레이션 시계를 켜고, canOpenURL은 경고만 합니다. 경고를 두고 긴급함을 만들어 내면 사이클 하나를 통째로 낭비하게 됩니다. 지켜볼 만한 문장은 릴리스 노트가 아니라 설명 문단 안에 있고, 그것은 숫자입니다. 시리즈 전체는 Apple 생태계 시리즈에서 볼 수 있습니다.

참고 자료


  1. Apple, iOS & iPadOS 27 Release Notes, UIKit 섹션의 Deprecations 항목(레이더 179874781). 확인 시점에 이 페이지는 스스로를 “iOS & iPadOS 27 Beta 4 Release Notes”라고 표기하고 있었고, 이 글에서 인용한 다른 릴리스 노트 페이지도 모두 마찬가지였습니다. 따라서 여기 실린 릴리스 노트 문구는 전부 잠정적인 것으로 다루시고, 더 오래 남을 기록은 심볼 페이지의 가용성 메타데이터라고 보시면 됩니다. 여기 인용한 항목 전문의 출처: “canOpenURL:은 지원이 중단되었습니다. 먼저 검증하지 말고, URL을 열어 본 다음 실패를 처리하십시오. 커스텀 URL 스킴 대신 유니버설 링크를 쓰면 이런 검증 자체가 필요 없어집니다.” 이 항목은 씬 생명주기 항목(레이더 141837548) 바로 뒤에 이어집니다. “최신 SDK로 빌드한 앱은 씬 기반 생명주기를 채택해야 하며, 그렇지 않으면 실행에 실패합니다.” HTML 페이지가 JavaScript를 통해 렌더링되기 때문에, 2026년 7월 26일에 Apple 문서 JSON을 기준으로 확인했습니다. 같은 JSON에는 canOpenURL이 정확히 한 번, 레이더 179874781이 한 번 등장합니다. tvOS 27, visionOS 27, watchOS 27, macOS 27 릴리스 노트도 같은 날 가져왔으며 각각 “tvOS 27 Beta 4”, “visionOS 27 Beta 4”, “watchOS 27 Beta 4”, “macOS 27 Golden Gate Beta 4”라는 제목이었고, 두 문자열 모두 0회 등장했습니다. 

  2. Apple, canOpenURL(_:), UIKit 인스턴스 메서드 레퍼런스. nonisolated func canOpenURL(_ url: URL) -> Bool로 선언되었고 iOS 3.0(Mac Catalyst 13.1, tvOS 9.0, visionOS 1.0)에 도입되었으며, iOS, iPadOS, Mac Catalyst, tvOS, visionOS에서 deprecatedAt 27.0으로 표시되어 있고 각각 “URL을 열어 보고 실패를 처리하는 쪽을 택하십시오”라는 가용성 메시지를 답니다. 페이지의 지원 중단 요약은 릴리스 노트 세 문장 가운데 둘을 그대로 반복합니다. “먼저 검증하지 말고, URL을 열어 본 다음 실패를 처리하십시오. 커스텀 URL 스킴 대신 유니버설 링크를 쓰면 이런 검증 자체가 필요 없어집니다.” 다음 내용의 출처이기도 합니다. 반환값 문서(“기기에 해당 URL의 스킴을 처리하도록 등록된 앱이 설치되어 있지 않거나 Info.plist 파일에 해당 URL의 스킴을 선언하지 않은 경우 false, 그 외에는 true”), 보증(“이 메서드가 true를 반환하면, 같은 URL로 open(_:options:completionHandler:) 메서드를 이어 호출했을 때 그 URL을 처리할 수 있는 앱이 반드시 실행된다는 것을 iOS가 보증합니다. 반환값은 URL의 유효성, 지정한 리소스의 존재 여부, 그리고 유니버설 링크의 경우 그 링크에 응답하도록 등록된 앱이 기기에 설치되어 있는지를 나타내지 않습니다”), 스레드 관련 주석(“메인 스레드가 아닌 스레드에서도 이 메서드를 안전하게 호출할 수 있습니다”), 두 상한을 모두 포함해 여기 인용한 허용 목록 참고 문단(“iOS 15 이상으로 링크한 앱은 LSApplicationQueriesSchemes 키에 최대 50개 항목까지 넣을 수 있습니다. iOS 27 이상으로 링크한 앱은 LSApplicationQueriesSchemes 키에 최대 25개 항목까지 넣을 수 있습니다”), 여기 인용한 런타임 호출 예산(“앱을 이전 버전의 iOS로 링크했지만 iOS 9.0 이상에서 실행 중이라면, 이 메서드를 최대 50번까지 호출할 수 있습니다. 그 한도에 도달하면 이후 호출은 항상 false를 반환합니다. 사용자가 앱을 재설치하거나 업그레이드하면 iOS가 한도를 초기화합니다”), 면제 조항(“이 메서드와 달리 open(_:options:completionHandler:) 메서드는 LSApplicationQueriesSchemes 요구 사항의 제약을 받지 않습니다. URL을 처리할 수 있는 앱이 있다면, 스킴을 선언하지 않았더라도 시스템이 그 앱을 실행합니다”), 그리고 유니버설 링크 폴백(“유니버설 링크를 처리할 앱이 없으면 iOS는 그 링크를 사용자의 기본 브라우저로 넘겨, 연결된 웹사이트가 응답하도록 합니다”). 참고 문단의 한 문장은 게시된 그대로 읽으면 이상합니다. “이 메서드는 기기에 등록된 앱이 설치되어 있지 않더라도 선언되지 않은 스킴에 대해 항상 false를 반환합니다.” 이 글에서 false 값의 모호함에 관한 주장은 그 문장이 아니라 반환값 섹션에 근거합니다. 2026년 7월 26일에 Apple 문서 JSON을 기준으로 확인했습니다. 

  3. Apple, UIApplication.OpenExternalURLOptionsKey.universalLinksOnly, UIKit 타입 프로퍼티 레퍼런스. iOS 10.0(Mac Catalyst 13.1, tvOS 10.0, visionOS 1.0)부터 제공되며, 2026년 7월 26일 기준 지원 중단 메타데이터가 없습니다. 개요(“URL은 반드시 유니버설 링크여야 하며 이를 열도록 구성된 앱이 있어야 합니다”)와 여기 인용한 설명의 출처입니다. “이 키를 open(_:options:completionHandler:) 메서드의 옵션 딕셔너리에 포함하면, 그 메서드는 URL이 유효한 유니버설 링크이고 그 URL을 열 수 있는 앱이 설치되어 있을 때만 URL을 엽니다. 이 키의 값은 Boolean 값을 담은 NSNumber 객체입니다.” 

  4. Apple, EnvironmentValues.openURL, SwiftUI 인스턴스 프로퍼티 레퍼런스. @MainActor @preconcurrency var openURL: OpenURLAction으로 선언되었고 iOS 14.0, iPadOS 14.0, Mac Catalyst 14.0, macOS 11.0, tvOS 14.0, visionOS 1.0, watchOS 7.0부터 제공되며, 2026년 7월 26일 기준 지원 중단 메타데이터가 없습니다. 여기 재수록한 openURL(url) { accepted in ... } 예제와 이 글에서 인용한 기본 동작 설명의 출처입니다. 

  5. Apple, OpenURLAction, SwiftUI 구조체 레퍼런스. @MainActor @preconcurrency struct OpenURLAction으로 선언되었고 iOS 14.0부터 제공되며 지원 중단 메타데이터가 없습니다. “시스템은 URL의 내용에 따라 동작이 달라지는 기본 URL 열기 동작을 제공합니다. 예를 들어 기본 동작은 가능하면 연결된 앱에서 유니버설 링크를 열고, 그렇지 않으면 사용자의 기본 웹 브라우저에서 엽니다”의 출처이며, 커스텀 동작이 “기본 제공 Link 뷰, 마크다운 링크가 있는 Text 뷰, 속성 문자열 안의 링크”에 적용된다는 서술의 출처이기도 합니다. 이 글에서 언급한 Result 멤버는 하위 페이지인 Apple, OpenURLAction.Result에서 가져왔으며, 여기에는 handled, discarded, systemAction, systemAction(_:), 그리고 iOS 26의 타입 메서드 systemAction(_:prefersInApp:)이 열거되어 있습니다. 

  6. 필자가 2026년 7월 26일 macOS 26.5.2에서 출시 중인 Apple 플랫폼 프로젝트 7개(Reps, Return, Banana List, Ace Citizenship, Water, ResumeGeni, Yawara)를 직접 조사한 결과입니다. build/, DerivedData/, .build/, Pods/, Carthage/, .swiftpm/, SourcePackages/, 패키지 checkouts/를 제외한 자체 작성 Swift 코드를 ripgrep으로 훑었습니다. 파일 커버리지는 저장소별로 find 결과와 rg 결과를 대조해(각각 77, 57, 55, 26, 34, 71, 143개 파일) gitignore된 자체 Swift 파일이 누락되지 않았음을 확인했습니다. canOpenURL의 0은 세 가지 방식으로 검증했습니다. 자체 작성 Swift, 빌드 산출물과 벤더링된 모든 패키지 체크아웃을 포함하는 --no-ignore --hidden 옵션의 Swift 검색(ResumeGeni 트리에서 Swift 파일 3,946개, 공용 941Kit 트리에서 15,760개), 그리고 --no-ignore로 모든 파일 형식을 대상으로 한 검색입니다. 제3자 코드와 벤더링된 코드까지 포함해 모든 패스에서 0이었습니다. LSApplicationQueriesSchemesINFOPLIST_KEY_LSApplicationQueriesSchemes는 192개의 .plist, .pbxproj, .entitlements, .xcconfig 파일 전체에서 0을 반환했습니다. 호출 지점 23곳은 SwiftUI Link 뷰 6개, UIApplication.shared.open 호출 3개, openURL(...) 호출 12개(모두 ResumeGeni에 있으며 @Environment(\.openURL) 선언 6개에서 비롯), Banana List의 macOS 코드에 있는 NSWorkspace.shared.open 호출 2개로 구성됩니다. Link( 집계에는 단어 경계가 필요한데, 경계 없는 패턴은 NavigationLink(와 프로젝트 고유의 여러 ...Link( 타입까지 함께 잡히기 때문입니다. 어떤 프로젝트에도 SFSafariViewController 사용은 0건, WKWebView 사용도 0건이며, applinks:를 선언한 저장소도 0개입니다. ResumeGeni의 결제 가드는 Profile/ProfileView.swift:1746이고, Banana List의 설치 여부 확인과 그 설명 주석은 Banana List/SettingsView.swift:321:329이며, 이 글에서 인용한 “이 문서를 열도록 설정된 응용 프로그램이 없습니다”라는 문구는 macOS 대화상자를 그대로 옮긴 것이 아니라 그 소스 주석의 표현입니다. Return의 설정 딥링크는 Return/ContentView.swift:222입니다. Reps의 메인 앱 타깃은 SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"를 선언합니다. 이 글의 어떤 플랫폼 관련 주장도 *_DEPLOYMENT_TARGET 키에서 도출하지 않았고, 어떤 앱의 전체 플랫폼 지원 범위도 SUPPORTED_PLATFORMS에서 추론하지 않았습니다. 이 프로젝트들의 빌드 구성 80개 중 이 값을 설정한 것은 40개뿐입니다. 

  7. Apple, openURL(_:), UIKit 인스턴스 메서드 레퍼런스. func openURL(_ url: URL) -> Bool로 선언되었고 iOS 2.0에 도입되어 iOS 10.0(Mac Catalyst 13.1)에서 지원이 중단되었습니다. 현재의 지원 중단 주석 출처입니다. “이 메서드를 호출해도 아무 일도 일어나지 않습니다. 대신 open(_:options:completionHandler:) 메서드를 사용하십시오.” 2026년 7월 26일 확인했습니다. 

  8. Apple, UIKit updates, Apple Developer Documentation. 2026년 6월 섹션은 네 개의 하위 절(General, App life cycle, Drag and drop, Text views)로 이루어져 있고, App life cycle 아래에 씬 생명주기 의무화가 들어 있습니다. “iOS 27부터 최신 SDK로 빌드한 앱은 씬 기반 생명주기를 사용해야 하며, 그렇지 않으면 실행에 실패합니다.” 2026년 7월 26일 canOpenURLLSApplicationQueriesSchemes를 검색했으나 페이지 어디에도 등장하지 않았습니다. 

  9. Apple, Xcode 27 Release Notes. 2026년 7월 26일 canOpenURL, LSApplicationQueriesSchemes, 레이더 179874781을 검색했으나 어느 것도 등장하지 않았습니다. 

  10. Apple, Describing use of required reason API, Bundle Resources 문서. 여기 인용한 Apple의 핑거프린팅 정의 출처입니다. “앱이 핵심 기능을 제공하기 위해 사용하는 일부 API는… 기기나 사용자를 식별하려고 기기 신호에 접근하는 데 오용될 소지가 있으며, 이를 핑거프린팅이라고 합니다. 사용자가 앱에 추적 권한을 허용했는지와 무관하게 핑거프린팅은 허용되지 않습니다.” 2026년 7월 26일 검색 결과 canOpenURLLSApplicationQueriesSchemes는 이 페이지에 등장하지 않으며, 이것이 Apple이 이 메서드를 핑거프린팅과 연결한 적이 없다는 이 글의 서술의 근거입니다. 이 페이지는 보고 요건을 설명하고 범주 목록은 NSPrivacyAccessedAPIType 문서로 넘깁니다. 이 글에 담긴 지원 중단의 프라이버시 해석은 Apple이 밝힌 근거가 아니라 필자의 추론입니다. 

  11. Apple, UIApplication, UIKit 클래스 레퍼런스. @MainActor class UIApplication으로 선언되었고 iOS 2.0부터 제공되며 지원 중단 메타데이터가 없습니다. open(_:options:completionHandler:)가 물려받고 canOpenURL(_:)nonisolated로 빠져나가는 메인 액터 격리의 출처입니다. 

  12. Apple, open(_:options:completionHandler:), UIKit 인스턴스 메서드 레퍼런스. iOS 10.0부터 제공되며 지원 중단 메타데이터가 없고, 완료 핸들러 형태와 async 형태 양쪽으로 선언되어 있습니다. func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:], completionHandler completion: (@MainActor @Sendable (Bool) -> Void)? = nil)func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:]) async -> Bool입니다. 개요(“지정한 URL의 리소스를 비동기적으로 열려고 시도합니다”), 여기 인용한 실행 동작(“지정한 URL 스킴을 다른 앱이 처리한다면 iOS가 그 앱을 실행하고 URL을 전달합니다. (앱을 실행하면 그 앱이 전면으로 올라옵니다.) 지정한 스킴을 처리할 수 있는 앱이 없으면 완료 핸들러가 success 매개변수 false와 함께 호출됩니다”), 그리고 여전히 지원 중단된 메서드를 권하는 안내의 출처입니다. “URL을 처리할 수 있는 앱이 설치되어 있는지 확인하려면, 이 메서드를 호출하기 전에 canOpenURL(_:) 메서드를 호출하십시오. 사용하려는 스킴 등록에 관한 중요한 참고 사항이 있으니 그 메서드의 설명을 반드시 읽어 보십시오.” 2026년 7월 26일 확인했습니다. 

  13. Apple, Allowing apps and websites to link to your content, Xcode 문서. 서버 측 연결 요건(“사용자가 앱을 설치하면, 시스템은 웹 서버에 저장된 파일을 확인해 여러분의 웹사이트가 앱을 대신해 URL을 열도록 허용하는지 검증합니다. 이 파일은 오직 여러분만 서버에 올릴 수 있으며, 이것이 웹사이트와 앱의 연결을 안전하게 만듭니다”), 브라우저 폴백(“앱을 설치하지 않은 사용자의 경우 시스템이 기본 웹 브라우저에서 URL을 열어 웹사이트가 처리하도록 합니다”), 앱이 자기 유니버설 링크를 열어도 자기 앱으로 라우팅되지 않는다는 주석(“앱이 위의 방법 중 하나로 자기 웹사이트의 유니버설 링크를 열면, 그 링크는 앱에서 열리지 않습니다”), 그리고 여기 설명한 동일 도메인 Safari 동작의 출처입니다. 이 페이지는 유니버설 링크를 라우팅하는 호출로 SwiftUI의 EnvironmentValues.openURL과 UIKit의 open(_:options:completionHandler:)를 나열합니다. 

  14. Apple, Link, SwiftUI 구조체 레퍼런스. @MainActor @preconcurrency struct Link<Label> where Label : View로 선언되었고 iOS 14.0, macOS 11.0, watchOS 7.0부터 제공되며 지원 중단 메타데이터가 없습니다. 여기 인용한 기본 동작의 출처입니다. “사용자가 Link를 탭하거나 클릭하면 기본 동작은 URL의 내용에 따라 달라집니다. 예를 들어 SwiftUI는 가능하면 연결된 앱에서 유니버설 링크를 열고, 그렇지 않으면 사용자의 기본 웹 브라우저에서 엽니다.” 

  15. Apple, OpenURLAction.callAsFunction(_:completion:), SwiftUI 인스턴스 메서드 레퍼런스. @MainActor @preconcurrency func callAsFunction(_ url: URL, completion: @escaping (Bool) -> Void)로 선언되었습니다. 여기 인용한 완료 클로저의 의미론 출처입니다. “URL을 열 수 있는지 판단한 뒤, 다만 URL을 완전히 열기 전에 호출될 수도 있는 클로저입니다. 이 클로저는 메서드가 URL을 열 수 있는지를 나타내는 Boolean 값을 받습니다.” 형제 메서드인 callAsFunction(_:)은 URL 하나만 받습니다. 2026년 7월 26일 확인했습니다. 

  16. Apple, Build settings reference, Xcode 문서. 2026년 7월 26일 INFOPLIST_KEY_LSApplicationQueriesSchemes를 검색한 결과 이 설정은 등장하지 않는 반면, INFOPLIST_KEY_LSApplicationCategoryType, INFOPLIST_KEY_LSBackgroundOnly, INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace, INFOPLIST_KEY_LSUIElement는 등장합니다. 자체 프로퍼티 리스트를 병합하는 빌드 단계가 이 키를 주입할 수는 있으므로, 이 부재는 저장소 검색이 완전한 감사가 된다는 뜻이 아니라 올바른 첫 수라는 뜻입니다. 

  17. Apple, Launch Services Keys, Information Property List Key Reference(Apple 아카이브). 이 키의 정의 출처입니다. “LSApplicationQueriesSchemes(Array - iOS) UIApplication 클래스의 canOpenURL: 메서드와 함께 앱이 사용할 수 있도록 하려는 URL 스킴을 지정합니다. canOpenURL: 메서드와 함께 쓰려는 각 URL 스킴을 이 배열에 문자열로 추가하십시오.” 이 페이지는 해당 키가 “iOS 9.0 이상에서 지원된다”고 밝히며 항목 상한은 언급하지 않습니다. 이 아카이브는 canOpenURL(_:) 자신의 설명 안에 걸린 링크의 목적지입니다. 이 키는 Apple의 현행 Information Property List 레퍼런스에 페이지가 없으며, 2026년 7월 26일 확인 결과 documentation/bundleresources/information-property-list/lsapplicationqueriesschemes.json은 HTTP 404를 반환하는 반면 lsapplicationcategorytype, lsbackgroundonly 같은 형제 LS* 키 페이지는 200을 반환합니다. 이 레퍼런스의 자체 인덱스 JSON 역시 어느 쪽으로도 쓸 만한 검증 수단이 못 됩니다. 최상위 키 그룹 일곱 개만 열거할 뿐 개별 LS* 키를 하나도 명시하지 않으며, 살아 있는 페이지가 있는 lsapplicationcategorytype조차 거기에는 없기 때문입니다. 

  18. Apple, OpenURLAction.callAsFunction(_:prefersInApp:), SwiftUI 인스턴스 메서드 레퍼런스. @MainActor @preconcurrency func callAsFunction(_ url: URL, prefersInApp: Bool)로 선언되었고 iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, macOS 26.0, tvOS 26.0, visionOS 26.0, watchOS 26.0부터 제공됩니다. OpenURLAction 페이지에서 Calling the action이 아니라 Instance Methods 아래에 실려 있습니다. 옵션 딕셔너리가 아니라 Boolean 하나만 받으므로 이것이 세 번째이자 마지막 호출 시그니처이며, 셋 중 어느 것도 universalLinksOnly를 받지 않습니다. 2026년 7월 26일 확인했습니다. 

  19. Apple, App Store Review Guidelines. 2026년 7월 26일 LSApplicationQueriesSchemes, canOpenURL, “25 entries”를 검색한 결과 각각 0회 등장했습니다. 어떤 적극적 주장을 뒷받침하기 위해서가 아니라 제출 규칙이 존재하지 않는다는 사실을 뒷받침하기 위해 인용했습니다. 

관련 게시물

iOS 27 런치 스크린 규칙: 네 개의 키냐, 리젝이냐

iOS 27 SDK로 빌드한 앱은 런치 스크린을 선언해야 하며, 그렇지 않으면 App Store에서 리젝됩니다. 네 개의 키, 그리고 생성된 plist를 쓰는 타깃을 점검하는 방법입니다.

10 분 소요

On Demand Resources 지원 중단: Background Assets가 요구하는 비용

Apple은 13개 단어로 ODR을 지원 중단했습니다. 대체 수단은 세 갈래로 갈리고, iOS 26이라는 하한선을 긋고, 그동안 시스템이 대신 해주던 디스크 관리를 다시 개발자 몫으로 돌려놓습니다.

17 분 소요