Tüm Yazılar
KategoriSwift
Okuma Süresi
20 dk okuma
Yayın Tarihi
...
Kelime Sayısı
1.634kelime

Kahveni hazırla - bu içerikli bir makale!

Swift 5.7+ RegexBuilder DSL ile type-safe pattern matching. Regex literal, capture groups, custom parsers ve gercek dunya ornekleri.

Swift Regex Builder ile Modern Pattern Matching

Swift 5.7 ile birlikte gelen RegexBuilder, metin isleme dunyasinda devrim yaratti. Artik kriptik regex pattern'leri yerine type-safe, okunabilir ve compose edilebilir DSL kullanabilirsiniz. Bu rehberde RegexBuilder'in tum gucunu kesfedeceegiz.


Icindekiler


1. Neden RegexBuilder?

Geleneksel regex pattern'leri okunmasi zor, hata ayiklamasi kabus gibi ve type-safety sunmaz. RegexBuilder bu sorunlarin tumunu cozer.

Karsilastirma Tablosu

Ozellik
Geleneksel Regex
RegexBuilder
**Okunabilirlik**
Dusuk
Yuksek
**Type Safety**
Yok
Tam
**Compose Edilebilirlik**
Zor
Kolay
**IDE Destegi**
Minimal
Tam autocomplete
**Hata Mesajlari**
Kriptik
Anlasilir
**Performans**
Iyi
Iyi (ayni engine)
**Ogrenme Egrisi**
Dik
Yavas

2. Temel Regex Literal Kullanimi

Swift 5.7 ile regex literal syntax eklendi:

swift
1// Regex literal — derleyici tarafindan dogrulanir
2let emailRegex = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/
3 
4let email = "[email protected]"
5if let match = email.wholeMatch(of: emailRegex) {
6 print("Gecerli email: \(match.output)")
7}
8 
9// String icinde arama
10let text = "Bana [email protected] adresinden ulasin"
11if let range = text.firstMatch(of: emailRegex) {
12 print("Bulunan: \(text[range.range])")
13}
14 
15// Tum eslesmeler
16let multiText = "[email protected] ve [email protected] adresleri"
17let matches = multiText.matches(of: emailRegex)
18print("Toplam \(matches.count) email bulundu")

Regex Operasyonlari

swift
1let input = "2024-03-15"
2let dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
3 
4if let match = input.wholeMatch(of: dateRegex) {
5 print("Yil: \(match.year)")
6 print("Ay: \(match.month)")
7 print("Gun: \(match.day)")
8}
9 
10// Replace islemi
11let redacted = input.replacing(dateRegex, with: "XXXX-XX-XX")
12print(redacted) // "XXXX-XX-XX"
13 
14// Split islemi
15let csv = "elma,armut,portakal"
16let items = csv.split(separator: /,\s*/)
17print(items) // ["elma", "armut", "portakal"]

3. RegexBuilder DSL

RegexBuilder, Swift'in result builder ozelligini kullanarak deklaratif regex olusturmayi saglar:

swift
1import RegexBuilder
2 
3// E-posta regex'i — RegexBuilder ile
4let emailBuilder = Regex {
5 OneOrMore {
6 CharacterClass(
7 .word,
8 .anyOf("._%+-")
9 )
10 }
11 "@"
12 OneOrMore {
13 CharacterClass(
14 .word,
15 .anyOf(".-")
16 )
17 }
18 "."
19 Repeat(2...) {
20 CharacterClass(.word)
21 }
22}
23 
24// Kullanim ayni
25if "[email protected]".wholeMatch(of: emailBuilder) != nil {
26 print("Gecerli email!")
27}

Temel Builder Bileosenleri

swift
1import RegexBuilder
2 
3// One — tam olarak bir kez
4let singleDigit = Regex {
5 One(.digit)
6}
7 
8// OneOrMore — bir veya daha fazla
9let digits = Regex {
10 OneOrMore(.digit)
11}
12 
13// ZeroOrMore — sifir veya daha fazla
14let optionalSpaces = Regex {
15 ZeroOrMore(.whitespace)
16}
17 
18// Repeat — belirli tekrar sayisi
19let zipCode = Regex {
20 Repeat(count: 5) {
21 One(.digit)
22 }
23}
24 
25// Optionally — opsiyonel kisim
26let phoneNumber = Regex {
27 Optionally { "+90" }
28 Repeat(count: 10) {
29 One(.digit)
30 }
31}
32 
33// ChoiceOf — alternatifler
34let protocol_ = Regex {
35 ChoiceOf {
36 "http"
37 "https"
38 "ftp"
39 }
40 "://"
41}
42 
43// Lookahead ve NegativeLookahead
44let passwordCheck = Regex {
45 Lookahead {
46 ZeroOrMore(.any)
47 One(.digit)
48 }
49 Lookahead {
50 ZeroOrMore(.any)
51 One(.word.subtracting(.digit))
52 }
53 Repeat(8...) {
54 One(.any)
55 }
56}

4. Capture Groups ve Tipli Yakalama

RegexBuilder'in en guclu ozelliklerinden biri tipli capture group'lardir:

swift
1import RegexBuilder
2 
3// Tarih parse etme — tipli capture
4let dateParser = Regex {
5 Capture {
6 Repeat(count: 4) { One(.digit) }
7 } transform: { Int($0)! }
8 "-"
9 Capture {
10 Repeat(count: 2) { One(.digit) }
11 } transform: { Int($0)! }
12 "-"
13 Capture {
14 Repeat(count: 2) { One(.digit) }
15 } transform: { Int($0)! }
16}
17 
18if let match = "2024-03-15".wholeMatch(of: dateParser) {
19 let (_, year, month, day) = match.output
20 // year: Int, month: Int, day: Int — otomatik tip donusumu!
21 print("Yil: \(year), Ay: \(month), Gun: \(day)")
22}

TryCapture ile Guvenli Parse

swift
1import RegexBuilder
2 
3enum Priority: String, CaseIterable {
4 case low, medium, high, critical
5}
6 
7let taskParser = Regex {
8 "["
9 TryCapture {
10 OneOrMore(.word)
11 } transform: {
12 Priority(rawValue: String($0))
13 }
14 "] "
15 Capture {
16 OneOrMore(.any)
17 }
18}
19 
20let task = "[high] Sunucu deploy et"
21if let match = task.wholeMatch(of: taskParser) {
22 let (_, priority, description) = match.output
23 print("Oncelik: \(priority), Gorev: \(description)")
24 // priority: Priority tipi — otomatik enum donusumu!
25}

Reference ile Backreference

swift
1import RegexBuilder
2 
3// HTML tag eslesitirme (acilis ve kapanis tag'i ayni olmali)
4let tagRef = Reference(Substring.self)
5 
6let htmlTag = Regex {
7 "<"
8 Capture(as: tagRef) {
9 OneOrMore(.word)
10 }
11 ">"
12 ZeroOrMore(.reluctant) {
13 One(.any)
14 }
15 "</"
16 tagRef
17 ">"
18}
19 
20let html = "<div>Merhaba Dunya</div>"
21if let match = html.wholeMatch(of: htmlTag) {
22 print("Tag: \(match[tagRef])") // "div"
23}

5. Custom Parsing Stratejileri

CustomConsumingRegexComponent protocol'u ile kendi parser'larinizi yazabilirsiniz:

swift
1import RegexBuilder
2 
3struct CurrencyParser: CustomConsumingRegexComponent {
4 typealias RegexOutput = (amount: Double, currency: String)
5 
6 func consuming(
7 _ input: String,
8 startingAt index: String.Index,
9 in bounds: Range<String.Index>
10 ) throws -> (upperBound: String.Index, output: RegexOutput)? {
11 let remaining = input[index..<bounds.upperBound]
12 
13 // Para birimi sembolu
14 let currencies = ["$": "USD", "€": "EUR", "£": "GBP", "₺": "TRY"]
15 
16 for (symbol, code) in currencies {
17 if remaining.hasPrefix(symbol) {
18 let afterSymbol = input.index(index, offsetBy: symbol.count)
19 let numberStr = input[afterSymbol..<bounds.upperBound]
20 .prefix(while: { $0.isNumber || $0 == "." || $0 == "," })
21 
22 let cleanNumber = numberStr.replacingOccurrences(of: ",", with: "")
23 if let amount = Double(cleanNumber) {
24 let end = input.index(afterSymbol, offsetBy: numberStr.count)
25 return (end, (amount, code))
26 }
27 }
28 }
29 return nil
30 }
31}
32 
33// Kullanim
34let priceRegex = Regex {
35 CurrencyParser()
36}
37 
38let text = "Fiyat: ₺1,299.99 + KDV"
39if let match = text.firstMatch(of: priceRegex) {
40 let (amount, currency) = match.output
41 print("\(amount) \(currency)") // 1299.99 TRY
42}

6. Gercek Dunya Ornekleri

Log Parser

swift
1import RegexBuilder
2 
3struct LogEntry {
4 let timestamp: String
5 let level: String
6 let message: String
7}
8 
9let logParser = Regex {
10 "["
11 Capture { OneOrMore(.any.subtracting(.anyOf("[]"))) }
12 "] "
13 Capture {
14 ChoiceOf { "INFO"; "WARN"; "ERROR"; "DEBUG" }
15 }
16 ": "
17 Capture { OneOrMore(.any) }
18}
19 
20let logs = """
21[2024-03-15 10:30:45] INFO: Uygulama baslatildi
22[2024-03-15 10:30:46] ERROR: Veritabani baglantisi basarisiz
23[2024-03-15 10:30:47] WARN: Cache suresi doldu
24"""
25 
26for line in logs.split(separator: "\n") {
27 if let match = String(line).wholeMatch(of: logParser) {
28 let (_, timestamp, level, message) = match.output
29 let entry = LogEntry(
30 timestamp: String(timestamp),
31 level: String(level),
32 message: String(message)
33 )
34 print("\(entry.level): \(entry.message)")
35 }
36}

URL Parser

swift
1import RegexBuilder
2 
3let urlParser = Regex {
4 Capture {
5 ChoiceOf { "https"; "http" }
6 }
7 "://"
8 Optionally {
9 Capture { OneOrMore(.any.subtracting(.anyOf("@"))) }
10 "@"
11 }
12 Capture { OneOrMore(.any.subtracting(.anyOf(":/"))) }
13 Optionally {
14 ":"
15 Capture { OneOrMore(.digit) } transform: { Int($0)! }
16 }
17 Optionally {
18 Capture { Regex { "/"; ZeroOrMore(.any) } }
19 }
20}
21 
22let url = "https://api.example.com:8080/v2/users?page=1"
23if let match = url.wholeMatch(of: urlParser) {
24 print("Protocol: \(match.1)")
25 print("Host: \(match.3)")
26}

7. Performans Karsilastirmasi

Senaryo
NSRegularExpression
Regex Literal
RegexBuilder
**Basit eslesme**
1.0x
1.2x
1.2x
**Karmasik pattern**
1.0x
1.1x
1.3x
**Cok sayida eslesme**
1.0x
0.9x
1.0x
**Compile time**
Runtime
Compile time
Compile time
**Type safety**
Yok
Kismen
Tam
Not: RegexBuilder ve Regex literal ayni engine'i kullanir. Performans farki ihmal edilebilir duzeydedir.

8. Best Practices

  1. Basit pattern'ler icin regex literal kullanin — tek satirlik eslesmelerde RegexBuilder gereksiz karmasiklik ekler
  2. Karmasik parsing icin RegexBuilder tercih edin — okunabilirlik ve bakim kolayligi saglar
  3. TryCapture ile guvenli donusum yapin — force unwrap yerine optional donusum
  4. CustomConsumingRegexComponent ile domain-specific parser yazin
  5. Regex'i bir kez olusturup yeniden kullanin — her cagirisda yeniden derleme maliyeti

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Tebrikler! Bu yaziyi sonuna kadar okudugun icin sana ozel bir hediyem var:

10. Sonuc

Swift RegexBuilder, metin isleme icin modern, type-safe ve okunabilir bir cozum sunar. Geleneksel regex'in gucunu Swift'in tip sisteminin guvenligi ile birlestirerek daha saglam kod yazmanizi saglar. Ozellikle karmasik parsing senaryolarinda RegexBuilder vazgecilmez bir arac haline gelecektir.


Sonuc ve Oneriler

RegexBuilder, Swift ekosisteminde metin isleme islemlerini koklunden degistiren bir aractir. Geleneksel regex pattern'lerinin okunaksizligi ve hata ayiklama zorlugu artik geride kaldi. Type-safe capture group'lar sayesinde derleme zamaninda hatalari yakalayabilir, CustomConsumingRegexComponent ile domain-specific parser'lar olusturabilirsiniz.

Projelerinizde RegexBuilder'a gecis yaparken asama asama ilerleyin: once basit string matching islemlerini donusturun, ardindan karmasik parsing senaryolarini ele alin. Her parser'i ayri bir struct olarak tanimlayip unit test yazmak, uzun vadede bakim maliyetini onemli olcude dusurur. Regex literal ve RegexBuilder'i birlikte kullanarak her senaryo icin en uygun araci secin.

Son olarak, performans acsindan RegexBuilder ve regex literal ayni engine'i kullandigi icin ikisi arasinda anlamli bir fark yoktur. Asil kazanim okunabilirlik, test edilebilirlik ve tip guvenligidir. Bu uc ozellik, buyuk ekiplerde ve uzun omurlu projelerde kritik oneme sahiptir.

Etiketler

#Swift#Regex#Pattern Matching#RegexBuilder#Parsing#iOS
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz