Default Tamer supports three types of routing rules, each designed for different use cases. Rules are evaluated in order from top to bottom, and the first matching rule determines which browser opens the URL.
// Rule.swift:10-14enum RuleType: String, Codable, CaseIterable { case sourceApp = "Source App" case domain = "Domain" case urlPattern = "URL Pattern"}
Bundle ID Resolution: Default Tamer includes logic to dynamically resolve bundle identifiers for apps that may have multiple versions or change their bundle IDs (see Rule.swift:106-121).
// Rule.swift:16-20enum DomainMatchType: String, Codable { case exact = "Exact" case suffix = "Suffix" case contains = "Contains"}
Exact
Suffix
Contains
Match a specific domain exactly. The www. prefix is automatically normalized for convenience.
// Router.swift:116-120case .exact: // Normalize both by stripping 'www.' for cleaner UX // This way github.com matches both github.com and www.github.com let normalizedHost = host.hasPrefix("www.") ? String(host.dropFirst(4)) : host let normalizedPattern = pattern.hasPrefix("www.") ? String(pattern.dropFirst(4)) : pattern matches = normalizedHost == normalizedPattern
Examples:
Pattern: github.com
✅ Matches: github.com, www.github.com
❌ Doesn’t match: gist.github.com, api.github.com
Match all subdomains of a domain. Useful for matching entire organizations or multi-tenant services.
// Router.swift:121-125case .suffix: // Pattern like ".atlassian.net" or "atlassian.net" let suffixPattern = pattern.hasPrefix(".") ? pattern : "." + pattern matches = host.hasSuffix(suffixPattern) || host == pattern
Normalization: Domain matching is case-insensitive. Both the URL’s host and your pattern are converted to lowercase before comparison (Router.swift:96-111).
// Rule.swift:22-51struct Rule: Identifiable, Codable, Hashable { let id: UUID var type: RuleType var enabled: Bool var targetBrowserId: String var openInPrivateMode: Bool // Match criteria (only one set used based on type) var sourceAppBundleId: String? var sourceAppName: String? var domainPattern: String? var domainMatchType: DomainMatchType? var urlContains: String? var urlRegex: String?}
Strategy: Place specific rules before general ones. More specific rules should appear higher in your list to prevent general rules from capturing URLs unintentionally.