SchemeRoute
Swift 매크로를 이용해 URL 스킴 기반 라우팅 코드를 자동으로 생성하는 패키지입니다.
This package uses Swift macros to generate URL scheme based routing code automatically.
enum 선언과 패턴만으로 문자열/URL ↔ 라우트 변환을 위한 SchemeMapper 를 생성할 수 있습니다.
With just an enum declaration and patterns, you can build a SchemeMapper for string/URL ↔ route conversions.
Requirements
- Swift 5.10 이상 (매크로 기능 활용)
Swift 5.10 or later (requires macro support) - iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6 / macCatalyst 13 이상 타깃
Targets iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6 / macCatalyst 13 or newer
Installation (Swift Package Manager)
Package.swift 의 dependencies 배열에 SchemeRoute 를 최신 버전으로 추가합니다 (현재 0.2.0).
Add SchemeRoute to the dependencies array in Package.swift with the latest version (currently 0.2.0).
.package(url: "https://github.com/ShapeKim98/SchemeRoute.git", from: "0.2.0")사용할 타깃의 dependencies 에 SchemeRoute 를 명시합니다.
Declare SchemeRoute in the target's dependencies where it will be used.
.target(
name: "YourApp",
dependencies: [
.product(name: "SchemeRoute", package: "SchemeRoute")
]
)Xcode에서는 File > Add Packages... 메뉴에서 같은 URL을 입력하면 됩니다.
In Xcode, use File > Add Packages... and enter the same URL.
Quick Start
import SchemeRoute
@SchemeRoutable
enum AppRoute: Equatable {
static var scheme: String { "myapp" }
static var host: String { "app" }
@SchemePattern("")
case home
@SchemePattern("user/${id}/profile")
case userProfile(id: String)
@SchemePattern("article/${slug}?ref=${ref}")
case article(slug: String, ref: String)
}
// 문자열 → 라우트 (호스트는 기본값으로 분리)
let route = AppRoute(rawValue: "user/42/profile")
// URL → 라우트
let fromURL = AppRoute(url: URL(string: "myapp://app/article/swift?ref=newsletter"))
// 라우트 → URL
let url = AppRoute.article(slug: "swift", ref: "newsletter").url()기본 스킴/호스트를 지정하지 않으면 패턴 문자열 안에 호스트(필요하다면 스킴까지)를 직접 포함할 수 있습니다. 기존 iOS/웹 URL을 그대로 다뤄야 할 때 유용합니다.
@SchemeRoutable
enum InlineRoute: Equatable {
@SchemePattern("kakaolink?categoryId=${categoryId}")
case kakaolink(categoryId: String)
@SchemePattern("inline.app/user/${id}/profile")
case inlineProfile(id: String)
}
let inline = InlineRoute(rawValue: "inline.app/user/42/profile")
let kakao = InlineRoute(url: URL(string: "kakaoapp://kakaolink?categoryId=424"))
let deepURL = InlineRoute.inlineProfile(id: "42").url(scheme: "myapp")패턴에 포함된 호스트는 그대로 유지되며, url(scheme:) 을 통해 런타임에서 필요한 스킴을 주입할 수 있습니다.
@SchemeRoutable 매크로는 enum 내 모든 케이스를 스캔하여 SchemeMapper<AppRoute> 를 생성합니다.
The @SchemeRoutable macro scans every case in the enum and generates a SchemeMapper<AppRoute>.
init?(url:) 은 옵셔널 URL을 그대로 받아 nil 이면 초기화에 실패합니다.
init?(url:) accepts an optional URL and returns nil when the argument is nil.
SchemeRoute 프로토콜의 기본 구현(rawValue, init?(rawValue:), init?(url:))도 자동으로 동작합니다.
The default SchemeRoute implementations (rawValue, init?(rawValue:), init?(url:)) then work automatically.
Pattern Rules
- 패턴 문자열은 기본적으로
path?query형태입니다.SchemeRoute.host나SchemeRoute.scheme가 비어 있으면 직접 호스트(및 스킴)을 포함시킬 수 있습니다.
Pattern strings arepath?queryby default. WhenSchemeRoute.hostorSchemeRoute.schemeare empty, you may inline the host (and scheme) manually. - 경로와 쿼리에서 값이 되는 부분은
${name}플레이스홀더로 표기합니다.
Use${name}placeholders wherever the path or query should inject values.- 경로 예:
user/${id}/profile
Path example:user/${id}/profile - 쿼리 예:
pay/complete?order_id=${orderId}
Query example:pay/complete?order_id=${orderId}
- 경로 예:
- 플레이스홀더 이름은
case의 연관값 라벨과 1:1 로 매칭되어야 하며, 모든 연관값은LosslessStringConvertible프로토콜을 따르는 타입이어야 합니다 (예:String,Int,Double,Bool등).
Placeholder names must match associated value labels 1:1, and every associated value must conform toLosslessStringConvertibleprotocol (e.g.,String,Int,Double,Bool, etc.). - 같은 연관값을 두 번 이상 사용할 수 없고 사용하지 않은 연관값이 있으면 오류가 발생합니다.
The same associated value cannot be used more than once, and unused associated values trigger an error. - 외부 라벨이 붙은 연관값(
case article(slug slug: String))은 지원하지 않습니다.
Associated values with external labels (e.g.case article(slug slug: String)) are not supported. SchemeRoute.scheme/host가 지정되어 있다면url()호출 시 기본값으로 사용됩니다. 필요하면url(scheme:host:)에서 값을 덮어쓸 수 있습니다.
WhenSchemeRoute.scheme/hostare set,url()uses them automatically; override them by passing arguments tourl(scheme:host:)when needed.
Automatic Type Conversion
LosslessStringConvertible 프로토콜을 따르는 모든 타입을 연관값으로 사용할 수 있습니다. URL 문자열과 타입 간 자동 변환이 이루어집니다.
Any type conforming to LosslessStringConvertible can be used as associated values. Automatic conversion between URL strings and types is performed.
@SchemeRoutable
enum AppRoute: Equatable {
static var scheme: String { "myapp" }
static var host: String { "app" }
// Int, Bool 등의 타입 자동 변환
@SchemePattern("user/${id}/posts?page=${page}&premium=${premium}")
case userPosts(id: Int, page: Int, premium: Bool)
// 옵셔널 타입도 지원
@SchemePattern("product/${productId}?discount=${discount}&quantity=${quantity}")
case product(productId: String, discount: Double?, quantity: Int?)
}
// URL → 라우트 변환
AppRoute(url: URL(string: "myapp://app/user/123/posts?page=2&premium=true"))
// => userPosts(id: 123, page: 2, premium: true)
AppRoute(url: URL(string: "myapp://app/product/ABC?discount=0.25&quantity=5"))
// => product(productId: "ABC", discount: 0.25, quantity: 5)
AppRoute(url: URL(string: "myapp://app/product/ABC?discount=0.25"))
// => product(productId: "ABC", discount: 0.25, quantity: nil)
// 라우트 → URL 변환
AppRoute.userPosts(id: 999, page: 5, premium: true).url()
// => myapp://app/user/999/posts?page=5&premium=true
AppRoute.product(productId: "TEST", discount: nil, quantity: nil).url()
// => myapp://app/product/TEST지원 타입:
Supported Types:
String,Int,Double,Bool,UInt등 기본 타입
Built-in types likeString,Int,Double,Bool,UInt, etc.- 위 타입들의 옵셔널 버전 (
Int?,Double?등)
Optional versions of the above types (Int?,Double?, etc.) LosslessStringConvertible을 따르는 커스텀 타입
Custom types conforming toLosslessStringConvertible
동작 방식:
Behavior:
- 변환 실패 시 (예:
"abc"→Int) 라우트 매칭이 실패합니다.
Conversion failures (e.g.,"abc"→Int) result in route matching failure. - 옵셔널 타입: 파라미터가 없거나 빈 값이면
nil로 처리됩니다.
Optional types: Missing or empty parameters are treated asnil. - 비옵셔널 타입: 파라미터가 없거나 빈 값이면 매칭이 실패합니다 (String 제외).
Non-optional types: Missing or empty parameters cause matching to fail (except String). - URL 생성 시
nil값을 가진 파라미터는 URL에서 제외됩니다.
When generating URLs, parameters withnilvalues are excluded from the URL.
Manual Router Configuration
매크로 대신 직접 매퍼를 구성하려면 SchemeMapper 의 Builder 를 사용할 수 있습니다.
If you prefer manual control, build the mapper by hand with SchemeMapper's Builder.
let router = SchemeMapper<AppRoute> { builder in
builder.register("user/${id}/profile", queryKeys: []) { params in
guard let id = params["id"] else { return nil }
return .userProfile(id: id)
} render: { route in
guard case let .userProfile(id) = route else { return nil }
return ["id": id]
}
}대부분의 경우 매크로를 사용하는 편이 선언적이며 오류를 줄일 수 있습니다.
In most cases, macros remain more declarative and help prevent mistakes.
Example Run
리포지토리에는 간단한 실행 예제가 포함되어 있습니다.
A simple runnable example ships with the repository.
swift run SchemeRouteClient출력 로그를 통해 문자열/URL 매칭과 URL 생성을 확인할 수 있습니다.
Check the output log to see string/URL matching and URL generation in action.
License
이 프로젝트는 MIT License 하에 배포됩니다.
This project is distributed under the MIT License.