الاستيفاء سلسلة متقدمة في Swift 5.0



كان الاستيفاء في السلسلة في 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 { //    -    -    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)" 

في الواقع ، يوجد سكر نحوي واحد تحت الغطاء. يمكننا كتابة الجزء الأخير يدويًا:

 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) 

استنتاج


كما رأينا ، يسمح لنا الاستيفاء بالسلسلة المخصصة بوضع التنسيق في مكان واحد ، بحيث تصبح مكالمات الطريقة أكثر بساطة ووضوحًا. كما يوفر لنا مرونة كبيرة لإنشاء الأنواع المطلوبة بشكل طبيعي قدر الإمكان.

تذكر أن هذه ليست سوى واحدة من الاحتمالات - وليست الوحيدة. هذا يعني أننا في بعض الأحيان نستخدم الاستيفاء ، وأحيانًا الوظائف أو أي شيء آخر. مثل الكثير في التنمية ، تحتاج دائمًا إلى اختيار أفضل طريقة لحل المشكلة.

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


All Articles