
كان الاستيفاء في السلسلة في Swift من الإصدارات السابقة ، ولكن في Swift 5.0 تم توسيع هذه الوظيفة وأصبحت أسرع وأكثر قوة بشكل ملحوظ.
في هذه المقالة ، سنتعرف على الاحتمالات الجديدة لاستكمال السلسلة ونفكر في كيفية تطبيق ذلك في الكود الخاص بنا. يمكنك أيضًا تنزيل مصادر هذه المقالة
هنا.الأساسيات
نحن نستخدم الاستيفاء السلسلة الأساسية مثل هذا:
let age = 38 print("You are \(age)")
نحن نعتبر هذا أمرا مفروغا منه ، ولكن في وقت من الأوقات كان الأمر بمثابة ارتياح كبير مقارنة بما كان علينا التعامل معه من قبل:
[NSString stringWithFormat:@"%ld", (long)unreadCount];
هناك أيضًا مكاسب كبيرة في الأداء ، حيث كان البديل:
let all = s1 + s2 + s3 + s4
نعم ، ستكون النتيجة النهائية هي نفسها ، ولكن سيتعين على سويفت إضافة s1 إلى s2 للحصول على s5 ، إضافة s5 إلى s3 للحصول على s6 ، وإضافة s6 إلى s4 للحصول على s7 ، قبل تعيين الكل.
التغيير العملي في السلسلة لم يتغير عملياً مع Swift 1.0 ، وجاء التغيير الهام الوحيد مع Swift 2.1 ، حيث حصلنا على فرصة لاستخدام
القيم الحرفية للسلسلة في الاستيفاء:
print("Hi, \(user ?? "Anonymous")")
كما تعلم ، فإن سويفت تتطور إلى حد كبير بفضل اقتراحات المجتمع. تتم مناقشة الأفكار وتطويرها وقبولها أو رفضها.
لذلك ، بعد خمس سنوات ، وصل تطوير سويفت إلى الاستيفاء. قدم Swift 5.0 ميزات جديدة فائقة تمنحك القدرة على التحكم في عملية الاستيفاء في السلسلة.
للمحاولة ، خذ بعين الاعتبار السيناريو التالي. إذا قمنا بتعيين متغير عدد صحيح جديد مثل هذا:
let age = 38
من الواضح تمامًا أنه يمكننا استخدام الاستيفاء في السلسلة كما يلي:
print("Hi, I'm \(age).")
ولكن ماذا لو أردنا تنسيق النتيجة بطريقة مختلفة؟
باستخدام نظام الاستيفاء الجديد للسلسلة في Swift 5.0 ، يمكننا كتابة ملحق String.StringInterpolation لإضافة طريقة الاستيفاء الخاصة بنا:
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) } } }
الآن سوف يخرج الرمز المتغير بالكامل كنص: "مرحبًا ، أنا في الثامنة والثلاثين".
يمكننا استخدام تقنية مشابهة لإصلاح تنسيق التاريخ ، حيث أن عرض التاريخ الافتراضي كسلسلة ليس جذابًا للغاية:
print("Today's date is \(Date()).")
سترى أن سويفت يعرض التاريخ الحالي في شكل شيء مثل: "2019-02-21 23:30:21 +0000". يمكننا أن نجعلها أكثر جمالا باستخدام تنسيق التاريخ الخاص بنا:
mutating func appendInterpolation(_ value: Date) { let formatter = DateFormatter() formatter.dateStyle = .full let dateString = formatter.string(from: value) appendLiteral(dateString) }
الآن تبدو النتيجة أفضل بكثير ، مثل: "21 فبراير 2019 23:30:21".
ملاحظة: لتجنب حدوث ارتباك محتمل عند العمل معًا في فريق ، ربما لا ينبغي لك تجاوز أساليب Swift الافتراضية. لذلك ، أعط المعلمات الأسماء التي تختارها لتجنب الالتباس:
mutating func appendInterpolation(format value: Int) {
الآن سوف نسمي هذه الطريقة مع المعلمة المسماة:
print("Hi, I'm \(format: age).")
الآن سيكون من الواضح أننا نستخدم تطبيقنا للأسلوب.
الاستيفاء مع المعلمات
يوضح هذا التغيير أن لدينا الآن سيطرة كاملة على كيفية حدوث الاستيفاء في السلسلة.
على سبيل المثال ، يمكننا إعادة كتابة الكود لمعالجة رسائل Twitter:
mutating func appendInterpolation(twitter: String) { appendLiteral("<a href=\"https://twitter.com/\(twitter)\">@\(twitter)</a>") }
الآن يمكننا أن نكتب مثل هذا:
print("You should follow me on Twitter: \(twitter: "twostraws").")
ولكن لماذا يجب أن نقتصر على معلمة واحدة؟ بالنسبة إلى مثال تنسيق الأرقام لدينا ، ليس من المنطقي إجبار المستخدمين على استخدام معلمة تحويل واحدة (.spellOut) - لذلك سنغير الطريقة بإضافة معلمة ثانية:
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) } }
واستخدامها مثل هذا:
print("Hi, I'm \(format: age, using: .spellOut).")
يمكن أن يكون لديك العديد من المعلمات التي تريدها ، من أي نوع. مثال باستخدام
@ autoclosure للقيمة الافتراضية:
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").")
باستخدام سمة
autoclosure يعني أنه بالنسبة للقيمة الافتراضية ، يمكننا استخدام قيم بسيطة أو استدعاء وظائف معقدة. في الطريقة ، سوف تصبح إغلاق.
الآن ، ربما تفكر في أنه يمكننا إعادة كتابة الكود دون استخدام وظيفة الاستيفاء ، شيء مثل هذا:
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")).")
لكن الآن قمنا بتعقيد الدعوة - لأننا نحاول بوضوح تنسيق شيء ما ، هذه هي نقطة الاستيفاء. تذكر قاعدة سويفت - تجنب الكلمات غير الضرورية.
قدمت Erica Sadun مثالًا قصيرًا وجميلًا على كيفية تبسيط الكود:
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 ? "(*)" : "")")
مضيفا سلسلة الاستيفاء لأنواع مخصصة
يمكننا استخدام سلسلة الاستيفاء لأنواعنا:
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)")
تعد سلسلة الاستيفاء مفيدة لأننا لا نلمس معلومات تصحيح الأخطاء الخاصة بالكائن. إذا نظرنا إليه في المصحح أو عرضناه ، فسنرى بيانات لم تمسها:
print(hater)
يمكننا الجمع بين نوع مخصص مع العديد من المعلمات:
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)")
بالطبع ، يمكنك استخدام جميع ميزات Swift لإنشاء التنسيق الخاص بك. على سبيل المثال ، يمكننا كتابة تطبيق يقبل أي
Encodable وطباعته في 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) } }
إذا جعلنا
الشخص متوافقًا مع بروتوكول
التشفير ، فيمكننا القيام بذلك:
print("Here's some data: \(debug: faker)")
يمكنك استخدام ميزات مثل عدد متغير من المعلمات ، حتى يمكنك وضع علامة على تنفيذ الاستيفاء الخاص بك كقذف. على سبيل المثال ، لا يستجيب نظام التنسيق JSON الخاص بنا في حالة حدوث خطأ في الترميز ، ولكن يمكننا إصلاح ذلك لتحليل الخطأ في المستقبل:
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)")
كل ما بحثناه حتى الآن مجرد تعديلات على أساليب الاستيفاء في السلسلة.
إنشاء أنواع مخصصة باستخدام الاستيفاء
كما رأيت ، كان السؤال عن كيفية تنسيق البيانات في التطبيق الخاص بك بطريقة مريحة حقا ، ولكن يمكننا أيضا إنشاء أنواع خاصة بنا باستخدام الاستيفاء في السلسلة.
لإثبات ذلك ، سنقوم بإنشاء نوع جديد تتم تهيئته من السلسلة باستخدام الاستيفاء في السلسلة.
struct ColoredString: ExpressibleByStringInterpolation {
في الواقع ، يوجد سكر نحوي واحد تحت الغطاء. يمكننا كتابة الجزء الأخير يدويًا:
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)
استنتاج
كما رأينا ، يسمح لنا الاستيفاء بالسلسلة المخصصة بوضع التنسيق في مكان واحد ، بحيث تصبح مكالمات الطريقة أكثر بساطة ووضوحًا. كما يوفر لنا مرونة كبيرة لإنشاء الأنواع المطلوبة بشكل طبيعي قدر الإمكان.
تذكر أن هذه ليست سوى واحدة من الاحتمالات - وليست الوحيدة. هذا يعني أننا في بعض الأحيان نستخدم الاستيفاء ، وأحيانًا الوظائف أو أي شيء آخر. مثل الكثير في التنمية ، تحتاج دائمًا إلى اختيار أفضل طريقة لحل المشكلة.