Interpolasi string lanjutan di Swift 5.0



Interpolasi string ada di Swift dari versi sebelumnya, tetapi di Swift 5.0 fungsi ini diperluas, menjadi lebih cepat dan lebih kuat.

Pada artikel ini, kita akan membahas kemungkinan interpolasi string baru dan mempertimbangkan bagaimana ini dapat diterapkan dalam kode kita sendiri. Anda juga dapat mengunduh sumber untuk artikel ini di sini.

Dasar-dasarnya


Kami menggunakan interpolasi string dasar seperti ini:

let age = 38 print("You are \(age)") 

Kami menerima hal ini begitu saja, tetapi pada suatu waktu itu merupakan kelegaan yang signifikan dibandingkan dengan apa yang harus kami hadapi sebelumnya:

 [NSString stringWithFormat:@"%ld", (long)unreadCount]; 

Ada juga keuntungan kinerja yang signifikan, karena alternatifnya adalah:

 let all = s1 + s2 + s3 + s4 

Ya, hasil akhirnya akan sama, tetapi Swift harus menambahkan s1 ke s2 untuk mendapatkan s5, menambahkan s5 ke s3 untuk mendapatkan s6, dan menambahkan s6 ke s4 untuk mendapatkan s7, sebelum menetapkan semua.

Interpolasi string praktis tidak berubah dengan Swift 1.0, satu-satunya perubahan signifikan datang dengan Swift 2.1, di mana kami mendapat kesempatan untuk menggunakan string literal dalam interpolasi:

 print("Hi, \(user ?? "Anonymous")") 

Seperti yang Anda tahu, Swift berkembang berkat saran komunitas. Gagasan dibahas, dikembangkan, dan diterima atau ditolak.

Jadi, lima tahun kemudian, pengembangan Swift harus sejalan interpolasi. Swift 5.0 memperkenalkan fitur-fitur super baru yang memberi kami kemampuan untuk mengontrol proses interpolasi string.

Untuk mencoba, pertimbangkan skenario berikut. Jika kita menetapkan variabel integer baru seperti ini:

 let age = 38 

cukup jelas bahwa kita dapat menggunakan interpolasi string sebagai berikut:

 print("Hi, I'm \(age).") 

Tetapi bagaimana jika kita ingin memformat hasilnya dengan cara yang berbeda?

Menggunakan sistem interpolasi string baru di dalam Swift 5.0, kita dapat menulis ekstensi String.StringInterpolation untuk menambahkan metode interpolasi kita sendiri:

 extension String.StringInterpolation { mutating func appendInterpolation(_ value: Int) { let formatter = NumberFormatter() formatter.numberStyle = .spellOut if let result = formatter.string(from: value as NSNumber) { appendLiteral(result) } } } 

Sekarang kode akan menampilkan seluruh variabel sebagai teks: "Hai, saya tiga puluh delapan."

Kita dapat menggunakan teknik serupa untuk memperbaiki format tanggal, karena tampilan tanggal default sebagai string tidak terlalu menarik:

 print("Today's date is \(Date()).") 

Anda akan melihat bahwa Swift menampilkan tanggal saat ini dalam bentuk sesuatu seperti: "2019-02-21 23:30:21 +0000". Kita dapat membuatnya lebih cantik menggunakan format tanggal kita sendiri:

 mutating func appendInterpolation(_ value: Date) { let formatter = DateFormatter() formatter.dateStyle = .full let dateString = formatter.string(from: value) appendLiteral(dateString) } 

Sekarang hasilnya terlihat jauh lebih baik, seperti: "21 Februari 2019 23:30:21".

Catatan: untuk menghindari kemungkinan kebingungan saat bekerja bersama dalam sebuah tim, Anda mungkin tidak boleh mengganti metode Swift default. Karena itu, berikan parameter nama-nama pilihan Anda untuk menghindari kebingungan:

 mutating func appendInterpolation(format value: Int) { 

Sekarang kita akan memanggil metode ini dengan parameter bernama:

 print("Hi, I'm \(format: age).") 

Sekarang akan menjadi jelas bahwa kita menggunakan implementasi metode kita sendiri.

Interpolasi dengan parameter


Perubahan ini menunjukkan bahwa kita sekarang memiliki kontrol penuh atas bagaimana interpolasi string terjadi.

Misalnya, kita dapat menulis ulang kode untuk memproses pesan Twitter:

 mutating func appendInterpolation(twitter: String) { appendLiteral("<a href=\"https://twitter.com/\(twitter)\">@\(twitter)</a>") } 

Sekarang kita bisa menulis seperti ini:

 print("You should follow me on Twitter: \(twitter: "twostraws").") 

Tetapi mengapa kita harus membatasi diri pada satu parameter saja? Untuk contoh format angka kami, tidak masuk akal untuk memaksa pengguna menggunakan satu parameter konversi (.spellOut) - jadi kami akan mengubah metode dengan menambahkan parameter kedua:

 mutating func appendInterpolation(format value: Int, using style: NumberFormatter.Style) { let formatter = NumberFormatter() formatter.numberStyle = style if let result = formatter.string(from: value as NSNumber) { appendLiteral(result) } } 

Dan gunakan seperti ini:

 print("Hi, I'm \(format: age, using: .spellOut).") 

Anda dapat memiliki sebanyak mungkin parameter dari jenis apa pun. Contoh menggunakan @autoclosure untuk nilai default:

 extension String.StringInterpolation { mutating func appendInterpolation(_ values: [String], empty defaultValue: @autoclosure () -> String) { if values.count == 0 { appendLiteral(defaultValue()) } else { appendLiteral(values.joined(separator: ", ")) } } } let names = ["Malcolm", "Jayne", "Kaylee"] print("Crew: \(names, empty: "No one").") 

Menggunakan atribut @autoclosure berarti bahwa untuk nilai default, kita dapat menggunakan nilai sederhana atau memanggil fungsi kompleks. Dalam metode ini, mereka akan menjadi penutup.

Sekarang, Anda mungkin berpikir bahwa kami dapat menulis ulang kode tanpa menggunakan fungsi interpolasi, seperti ini:

 extension Array where Element == String { func formatted(empty defaultValue: @autoclosure () -> String) -> String { if count == 0 { return defaultValue() } else { return self.joined(separator: ", ") } } } print("Crew: \(names.formatted(empty: "No one")).") 

Tapi sekarang kami telah mempersulit panggilan - karena kami jelas mencoba untuk memformat sesuatu, ini adalah titik interpolasi. Ingat aturan Swift - hindari kata-kata yang tidak perlu.

Erica Sadun menawarkan contoh yang sangat singkat dan indah tentang bagaimana menyederhanakan kode:

 extension String.StringInterpolation { mutating func appendInterpolation(if condition: @autoclosure () -> Bool, _ literal: StringLiteralType) { guard condition() else { return } appendLiteral(literal) } } let doesSwiftRock = true print("Swift rocks: \(if: doesSwiftRock, "(*)")") print("Swift rocks \(doesSwiftRock ? "(*)" : "")") 

Menambahkan interpolasi string untuk tipe khusus


Kita dapat menggunakan interpolasi string untuk tipe kita sendiri:

 struct Person { var type: String var action: String } extension String.StringInterpolation { mutating func appendInterpolation(_ person: Person) { appendLiteral("I'm a \(person.type) and I'm gonna \(person.action).") } } let hater = Person(type: "hater", action: "hate") print("Status check: \(hater)") 

Interpolasi string berguna karena kita tidak menyentuh informasi debug tentang suatu objek. Jika kita melihatnya di debugger atau menampilkannya, maka kita akan melihat data yang tidak tersentuh:

 print(hater) 

Kami dapat menggabungkan jenis khusus dengan beberapa parameter:

 extension String.StringInterpolation { mutating func appendInterpolation(_ person: Person, count: Int) { let action = String(repeating: "\(person.action) ", count: count) appendLiteral("\n\(person.type.capitalized)s gonna \(action)") } } let player = Person(type: "player", action: "play") let heartBreaker = Person(type: "heart-breaker", action: "break") let faker = Person(type: "faker", action: "fake") print("Let's sing: \(player, count: 5) \(hater, count: 5) \(heartBreaker, count: 5) \(faker, count: 5)") 

Tentu saja, Anda dapat menggunakan semua fitur Swift untuk membuat pemformatan Anda sendiri. Sebagai contoh, kita dapat menulis implementasi yang menerima Encodable dan mencetaknya di JSON:

 mutating func appendInterpolation<T: Encodable>(debug value: T) { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted if let result = try? encoder.encode(value) { let str = String(decoding: result, as: UTF8.self) appendLiteral(str) } } 


Jika kita membuat Orang mematuhi protokol Encodable , maka kita dapat melakukan ini:

 print("Here's some data: \(debug: faker)") 

Anda dapat menggunakan fitur seperti sejumlah variabel parameter, bahkan menandai implementasi interpolasi Anda sebagai melempar . Misalnya, sistem pemformatan JSON kami tidak merespons jika terjadi kesalahan penyandian, tetapi kami dapat memperbaikinya untuk menganalisis kesalahan di masa mendatang:

 mutating func appendInterpolation<T: Encodable>(debug value: T) throws { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let result = try encoder.encode(value) let str = String(decoding: result, as: UTF8.self) appendLiteral(str) } print(try "Status check: \(debug: hater)") 

Semua yang telah kita lihat sejauh ini hanyalah modifikasi pada metode interpolasi string.

Membuat Jenis Kustom Menggunakan Interpolasi


Seperti yang Anda lihat, itu adalah pertanyaan tentang bagaimana memformat data dalam aplikasi Anda dengan cara yang sangat nyaman, tetapi kami juga dapat membuat tipe kami sendiri menggunakan interpolasi string.

Untuk mendemonstrasikan ini, kita akan membuat tipe baru yang diinisialisasi dari string menggunakan interpolasi string.

 struct ColoredString: ExpressibleByStringInterpolation { //    -    -    struct StringInterpolation: StringInterpolationProtocol { //   -    var output = NSMutableAttributedString() //     var baseAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Georgia-Italic", size: 64) ?? .systemFont(ofSize: 64), .foregroundColor: UIColor.black] //   ,        init(literalCapacity: Int, interpolationCount: Int) { } // ,      mutating func appendLiteral(_ literal: String) { //   ,       print("Appending \(literal)") //    let attributedString = NSAttributedString(string: literal, attributes: baseAttributes) //     output.append(attributedString) } // ,          mutating func appendInterpolation(message: String, color: UIColor) { //     print("Appending \(message)") //        var coloredAttributes = baseAttributes coloredAttributes[.foregroundColor] = color //    -     let attributedString = NSAttributedString(string: message, attributes: coloredAttributes) output.append(attributedString) } } //    ,     let value: NSAttributedString //      init(stringLiteral value: String) { self.value = NSAttributedString(string: value) } //     init(stringInterpolation: StringInterpolation) { self.value = stringInterpolation.output } } let str: ColoredString = "\(message: "Red", color: .red), \(message: "White", color: .white), \(message: "Blue", color: .blue)" 

Bahkan, ada satu gula sintaksis di bawah tenda. Kita dapat menulis bagian terakhir secara manual:

 var interpolation = ColoredString.StringInterpolation(literalCapacity: 10, interpolationCount: 1) interpolation.appendLiteral("Hello") interpolation.appendInterpolation(message: "Hello", color: .red) interpolation.appendLiteral("Hello") let valentine = ColoredString(stringInterpolation: interpolation) 

Kesimpulan


Seperti yang Anda lihat, interpolasi string khusus memungkinkan kami menempatkan pemformatan di satu tempat, sehingga pemanggilan metode menjadi lebih mudah dan jelas. Ini juga memberi kami fleksibilitas besar untuk membuat jenis yang diperlukan sealami mungkin.

Ingatlah bahwa ini hanyalah salah satu dari kemungkinan - dan bukan satu-satunya. Ini berarti bahwa kadang-kadang kita menggunakan interpolasi, terkadang fungsi atau yang lainnya. Seperti banyak dalam pengembangan, Anda selalu perlu memilih cara terbaik untuk menyelesaikan masalah.

Source: https://habr.com/ru/post/id447586/


All Articles