Swift 4.0 กับ Swift 3.0 - ความแตกต่างและคุณสมบัติใหม่

เผยแพร่แล้ว: 2021-10-05

วันที่ Apple เปิดตัวภาษา Swift ที่รู้จักกันดีในเวอร์ชันใหม่ได้มาถึงเราในที่สุด เราควรคาดหวังอะไรจากมัน? ในขณะที่ความคาดหวังสั่นสะท้าน เราได้ตัดสินใจที่จะนำเสนอภาพรวมเล็กๆ ของการอัปเดตใหม่ที่จะปรากฏในเวอร์ชัน Swift 4

3 รากฐานที่สำคัญของ Swift

ในฐานะที่เป็นภาษาที่ยอดเยี่ยมในการเขียนโค้ด Swift มีประโยชน์ในตัวเอง และอ้างว่า "อยู่ได้นานกว่า" ภาษา Objective-C

คุณสามารถอ่านเกี่ยวกับความแตกต่างที่สำคัญระหว่าง Swift และ Objective-C

Swift นั้น รวดเร็ว ปลอดภัยต่อการพิมพ์ และสื่อความ หมายได้ ดีมาก มันสามารถใช้ในการเขียนซอฟต์แวร์บนโทรศัพท์และแท็บเล็ต เดสก์ท็อปและเซิร์ฟเวอร์ - เห็นได้ชัดในทุกสิ่งที่รันโค้ด ยินดีต้อนรับคุณที่จะเล่นกับมัน - ด้วยแอพ Swift Playgrounds การเรียนรู้วิธีเขียนโค้ดของ Apple หรือใช้ Playgrounds ใน Xcode คุณจะเห็นผลงานของคุณทันที ไม่จำเป็นต้องจดจ่ออยู่กับการพัฒนาและใช้งานแอพที่ ที่แรก. ทุกเวอร์ชันเสริมใหม่จะดีขึ้นและเร็วขึ้น และนั่นคือกรณีของเวอร์ชัน Swift 4
พร้อมมั่นคง?

อีกหนึ่งฟีเจอร์ที่ยอดเยี่ยมที่ Xcode 9 มีให้สำหรับ Swift 4 - คุณไม่จำเป็นต้องกังวลเกี่ยวกับการโยกย้ายที่จะเกิดขึ้น และคุณจะเข้าใจว่าทำไมในขณะที่อ่านบทความนี้

เราจะมาสำรวจกันสั้น ๆ ว่าฟีเจอร์ใหม่ของ bonbons และ Swift 4 ในฤดูใบไม้ร่วงนี้นำเสนออะไรให้เราบ้าง

เริ่มต้น

ภาษานั้นไม่ค่อยมีประโยชน์นักหากไม่มี IDE ที่ใช้งานสะดวก ซึ่งก็คือ Xcode ในโลกของนักพัฒนา  คุณสามารถดาวน์โหลด Xcode 9 เวอร์ชันล่าสุดได้จาก Mac App Store หรือที่หน้าดาวน์โหลดที่ไซต์ Apple Developer แต่ตรวจสอบให้แน่ใจว่าคุณมีบัญชีนักพัฒนาซอฟต์แวร์ที่ใช้งานอยู่ก่อน มันค่อนข้างเสถียร ดังนั้นคุณสามารถแทนที่ Xcode 8 ด้วยมันสำหรับกิจวัตรการเข้ารหัสประจำวันของคุณ

คุณยังสามารถติดตั้ง Xcode ได้หลายเวอร์ชันโดยใช้ xcversion

หากคุณกำลังเริ่มต้นโครงการใหม่ - คุณพร้อมแล้ว แต่ถ้าคุณมีโปรเจ็กต์ที่เขียนด้วย Swift 3.x อยู่แล้ว คุณต้องผ่านกระบวนการย้ายข้อมูล

เราแนะนำให้ลองใช้ Playground ก่อน - เพื่อทำความคุ้นเคยกับการใช้คุณสมบัติใหม่

คุณจะสังเกตเห็นลิงก์ไปยังข้อเสนอ Swift Evolution ในรูปแบบ 'SE-____' ขณะที่คุณกำลังอ่านบทความนี้

กำลังย้ายไปยัง Swift 4

การย้ายจาก Swift เวอร์ชันหลักหนึ่งไปยังเวอร์ชันถัดไปนั้นค่อนข้างเข้มข้น โดยเฉพาะอย่างยิ่งจาก Swift 2.x เป็น 3.0 โดยปกติแล้วจะใช้เวลาประมาณ 1-2 วันต่อโปรเจ็กต์ แต่การโยกย้ายไปยัง Swift 4 นั้นง่ายกว่าเล็กน้อย และสามารถผ่านไปได้เร็วกว่ามาก

การเตรียมการก่อนการย้ายถิ่นฐาน

Xcode 9 ไม่เพียงรองรับ Swift 4 เท่านั้น แต่ยังรองรับเวอร์ชัน 3.2 อีกด้วย ดังนั้นโปรเจ็กต์ของคุณควรคอมไพล์โดยไม่มีปัญหารุนแรงใดๆ สิ่งนี้เป็นไปได้เพราะคอมไพเลอร์ Swift 4 และเครื่องมือการโยกย้ายรองรับภาษาทั้งสองเวอร์ชัน คุณสามารถระบุเวอร์ชัน Swift ต่างๆ ต่อเป้าหมายได้ ซึ่งจะมีประโยชน์มากหากไลบรารีของบุคคลที่สามบางตัวยังไม่ได้อัปเดต หรือหากคุณมีเป้าหมายหลายรายการในโปรเจ็กต์ของคุณ อย่างไรก็ตาม ไม่ใช่แค่ภาษาเท่านั้น แต่ SDK ก็มีการเปลี่ยนแปลงเช่นกัน ดังนั้นจึงเป็นไปได้มากที่จะต้องมีการอัปเดตบางอย่างกับโค้ดของคุณ เนื่องจาก Apple ยังคงพัฒนา SDK API ต่อไป...

เครื่องมือย้ายข้อมูลอย่างรวดเร็ว

และเช่นเคย Apple มีเครื่องมือการโยกย้าย Swift ที่รวมอยู่ใน Xcode ซึ่งสามารถช่วยโยกย้ายจาก Swift เวอร์ชันก่อนหน้าได้ คุณสามารถเปิดใช้งานได้ใน Xcode โดยไปที่ Edit -> Convert -> To Current Swift Syntax… และเลือกเป้าหมายที่คุณต้องการแปลง

คุณจะถูกถามว่าคุณต้องการใช้การตั้งค่าการอนุมาน Objective-C ใด:

เนื่องจากการเปลี่ยนแปลงเพิ่มเติมใน Swift 4 เครื่องมือการโยกย้าย Swift จะจัดการการเปลี่ยนแปลงส่วนใหญ่ให้คุณ

CocoaPods

นักพัฒนา  ส่วนใหญ่ใช้ตัวจัดการการพึ่งพา CocoaPods สำหรับโปรเจ็กต์ของพวกเขา เนื่องจาก Swift Package Manager นั้นไม่ได้เติบโตเต็มที่อย่างที่ควรจะเป็น แม้ว่าจะมีการปรับปรุงอย่างรวดเร็วมาก ดังที่กล่าวไว้ข้างต้น ไลบรารีของบุคคลที่สามบางตัวยังไม่ได้รับการอัปเดตเป็น Swift 4 ดังนั้นคุณจึงสามารถเห็นข้อผิดพลาดขณะรวบรวมบางส่วนได้ ทางออกหนึ่งที่เป็นไปได้ในการแก้ไขปัญหานี้คือการระบุ Swift เวอร์ชัน 3.2 สำหรับ post_install เหล่านั้นที่ยังไม่ได้อัปเดตโดยการเพิ่มสคริปต์ post_install ให้กับคุณ Podfile :

 old_swift_3_pods = [ 'PodName1', 'PodName2', ] post_install do |installer| installer.pods_project.targets.each do |target| if old_swift_3_pods.include? target.name target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '3.2' end end end end

แล้ววิ่ง

 pod install

ตอนนี้คุณสามารถคอมไพล์พ็อดได้โดยไม่มีข้อผิดพลาด

มาตรวจสอบเกี่ยวกับการเปลี่ยนแปลงและเพิ่มเติมของ Swift 4 API

การเปลี่ยนแปลงและการเพิ่ม API

เครื่องสาย

ขณะนี้ String สอดคล้องกับโปรโตคอลการ Collection ด้วยข้อเสนอ SE-0163 จำ Swift 1.x ได้ไหม

ไม่จำเป็นต้องใช้คุณสมบัติอาร์เรย์ characters ในขณะนี้ เนื่องจากคุณสามารถวนซ้ำผ่าน String ได้โดยตรง:

 let string = "Hello, Mind Studios!" for character in string { print(character) }

นอกจากนี้ยังหมายความว่าคุณสามารถใช้วิธีการ Collection และคุณสมบัติใน String เช่น count , isEmpty , map() , filter() , index(of:) และอื่นๆ อีกมากมาย:

 string.count // No more `string.characters.count` string.isEmpty // false let index = string.index(of: " ") // 6 let reversedCollection = "abc".reversed() let reversedString = String(reversedCollection) // "cba" // String filtering let string = "ni123n456iniASijasod! 78a9-kasd aosd0" let numbersString = string.filter { Int(String($0)) != nil } // "1234567890"

ประเภท Substring ใหม่

Swift 4 นำเสนอประเภท Substring ใหม่ซึ่งแสดงถึงลำดับย่อยของ String (ตามที่อธิบายไว้ใน SE-0163 ที่กล่าวถึงข้างต้น)

 // Split string into substrings let string = "Hello, Mind Studios!" let parts = string.split(separator: " ") // ["Hello,", "Mind", "Studios!"] type(of: parts.first!) // Substring.Type

ทั้ง String และ Substring ในขณะนี้สนับสนุนใหม่ StringProtocol ซึ่งทำให้พวกเขาเกือบจะเหมือนกันและทำงานร่วมกัน:

 var hello = parts.first! // Concatenate a String onto a Substring hello += " !" // "Hello, !" // Create a String from a Substring let helloDog = String(hello) // "Hello, !"

โน๊ตสำคัญ

SE-0163 มีบันทึกที่สำคัญมาก:

 Long-term storage of `Substring` instances is discouraged. A substring holds a reference to the entire storage of a larger string, not just to the portion it presents, even after the original string's lifetime ends. Long-term storage of a substring may therefore prolong the lifetime of elements that are no longer otherwise accessible, which can appear to be memory leakage.

ซึ่งหมายความว่า Substring มีวัตถุประสงค์เพื่อใช้เป็นที่เก็บข้อมูลชั่วคราวสำหรับลำดับย่อยของ String หากคุณต้องการส่งต่อไปยังวิธีการบางอย่างหรือคลาสอื่น - ให้แปลงเป็น String ก่อน:

 let substring: Substring = ... // Substring let string = String(substring) // String someMethod(string)

อย่างไรก็ตาม ระบบประเภทของ Swift จะช่วยให้คุณไม่ส่ง Substring ไปที่ใดที่คาดว่า String (สมมติว่าคุณไม่ได้ใช้ StringProtocol ใหม่เป็นประเภทพารามิเตอร์)

ตัวอักษรสตริงหลายบรรทัด

SE-0168 แนะนำไวยากรณ์อย่างง่ายสำหรับตัวอักษรสตริงหลายบรรทัดโดยใช้เครื่องหมายอัญประกาศคู่สามตัว """ ซึ่งหมายความว่าสามารถวางรูปแบบข้อความส่วนใหญ่ (เช่น JSON หรือ HTML) หรือข้อความขนาดยาวบางส่วนได้โดยไม่ต้องมีการหลบหนี:

 let multilineString = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mattis lorem et leo laoreet fermentum. Mauris pretium enim ac mi tempor viverra et fermentum nisl. Sed diam nibh, posuere non lectus at, ornare bibendum erat. Fusce mattis sem ac feugiat vulputate. Morbi at nunc maximus, vestibulum orci et, dictum neque. Vestibulum vulputate augue ac libero vulputate vestibulum. Nullam blandit et sapien non fermentum. Proin mollis nisl at vulputate euismod. """

การขึ้นบรรทัดใหม่ในสตริงตามตัวอักษร

SE-0182 เพิ่มความสามารถในการยกเว้นการขึ้นบรรทัดใหม่ในตัวอักษรสตริงแบบหลายบรรทัดด้วยแบ็กสแลชที่ท้ายบรรทัด

 let escapedNewline = """ Line 1, Line 2 \ next part of line 2, Line 3 """ print(escapedNewline)
 Line 1, Line 2 next part of line 2, Line 3

ปรับปรุงการรองรับ Unicode

Swift 4 รองรับ Unicode 9 ซึ่งหมายความว่าปัญหาในการนับอักขระ Unicode หมดไป:

 "".count // 1, in Swift 3: 2 "".count // 1, in Swift 3: 2 "".count // 1, in Swift 3: 2 - person + skin tone "".count // 1, in Swift 3: 4 "".count // 3, in Swift 3: 1

การเปลี่ยนแปลงทั้งหมดและข้อเสนอที่ดำเนินการตามที่ระบุไว้ข้างต้น (เช่นเดียวกับอื่นๆ อีกมากมาย) กำลังเริ่มต้นจากชุดคุณลักษณะที่อธิบายอย่างกว้างๆ ที่เรียกว่า String Manifesto

การควบคุมการเข้าถึง

Swift 3 นำองค์ประกอบที่ขัดแย้งกันมากมาสู่การควบคุมการเข้าใช้ - fileprivate access modifier ซึ่งอาจทำให้สับสนได้
ก่อนหน้านี้ ตัวแก้ไขระดับการเข้าถึง private ถูกใช้เพื่อซ่อนสมาชิกประเภทจากประเภทอื่น ๆ และสมาชิกส่วนตัวสามารถเข้าถึงได้โดยวิธีการและคุณสมบัติที่กำหนดไว้ในการกำหนดประเภทเท่านั้น โดยปล่อยให้ส่วนขยายประเภทเดียวกันนั้นไม่สามารถเข้าถึงสมาชิกเหล่านั้นได้
fileprivate สามารถใช้เพื่อแชร์การเข้าถึงสำหรับสมาชิกประเภท เช่น คุณสมบัติและเมธอด ภายในไฟล์เดียวกัน
อันที่จริงการใช้ private ทำให้เกิดปัญหาเมื่อส่วนขยายบางประเภทไม่สามารถเข้าถึงสมาชิกประเภทนั้นได้ ดังนั้นการใช้ fileprivate ในสถานการณ์เช่นนี้จึงเป็นวิธีแก้ปัญหาทั่วไป ซึ่งนำไปสู่ปัญหาอื่น: ประเภทอื่นๆ ใน ไฟล์เดียวกันสามารถเข้าถึงสมาชิกเหล่านั้นได้เช่นกัน

Swift 4 จัดระเบียบสิ่งต่าง ๆ โดยอนุญาตให้ส่วนขยายของประเภทเข้าถึงสมาชิก private ของประเภทนั้นในไฟล์เดียวกันตามที่อธิบายไว้ใน SE-0169:

 struct User { private let firstName: String private let lastName: String } extension User: CustomStringConvertible { var description: String { return "User: \(firstName) \(lastName)" } }

พจนานุกรมและชุด

การเริ่มต้น Dictionary ด้วย Sequence

Dictionary สามารถเริ่มต้นด้วย Sequence ขณะนี้ แต่ไม่สามารถส่งลำดับทั้งหมดได้ใน initializer นี้ เฉพาะที่มี tuples (Key, Value) โดยที่ Key เป็นประเภทคีย์ของ Dictionary และ Value แสดงถึงประเภทค่าของ Dictionary

 let stocksIdentifiers = ["AAPL", "GOOGL", "NKE"] let stocksValues = [158.28, 940.13, 53.73] let pairs = zip(stocksIdentifiers, stocksValues) let stocksValuesDict = Dictionary(uniqueKeysWithValues: pairs) // ["GOOGL": 940.13, "NKE": 53.73, "AAPL": 158.28]

ฟังก์ชัน zip ที่นี่สร้างคู่ ( Tuple s) จาก 2 ลำดับ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับฟังก์ชันนี้ในเอกสารประกอบ Swift Standard Library

พจนานุกรมผสาน

คุณสามารถระบุวิธีจัดการคีย์ที่ซ้ำกันเมื่อสร้างพจนานุกรมจากลำดับของ Tuple โดยส่งการปิดไปยังพารามิเตอร์ uniquingKeysWith ซึ่งใช้เพื่อรวมค่าจาก 2 คีย์ที่เหมือนกัน

ตัวอย่างที่ 1:

 let duplicates = [("a", 1), ("b", 5), ("a", 3), ("b", 3)] let dictionary = Dictionary(duplicates, uniquingKeysWith: { (first, _) in return first }) // ["b": 5, "a": 1]

ที่นี่เราปล่อยให้ค่าแรกไม่สนใจค่าถัดไปทั้งหมดด้วยคีย์เดียวกัน

ตัวอย่างที่ 2:

การนับจำนวนครั้งที่อักขระแต่ละตัวปรากฏในสตริง

 let string = "Hello!" let pairs = Array(zip(string, repeatElement(1, count: string.count))) let counts = Dictionary(pairs, uniquingKeysWith: +) // ["H": 1, "e": 1, "o": 1, "l": 2, "!": 1]

ตัวอย่างที่ 3:

ใช้วิธีการ merge :

 let values = ["a": 1, "b": 5] var additionalValues = ["b": 3, "c": 2, "a": 3] additionalValues.merge(values, uniquingKeysWith: +) // ["b": 8, "c": 2, "a": 4]

ตัวห้อยที่มีค่าเริ่มต้น

ก่อนหน้านี้ แนวทางปฏิบัติทั่วไปคือการใช้ตัวดำเนินการการรวมศูนย์เพื่อให้ค่าดีฟอลต์ในกรณีที่ค่าเป็นศูนย์

สวิฟท์ 3:

 let dict = ["a": 1, "b": 5] dict["c"] ?? 0 // 0

Swift 4 แนะนำ default ใหม่ให้กับตัวห้อย (ส่วนหนึ่งของ SE-0165):

 let dict = ["a": 1, "b": 5] dict["c", default: 0] // 0, equals to `dict["c"] ?? 0` in Swift 3

คุณยังสามารถเปลี่ยนพจนานุกรมในขณะที่สมัครสมาชิกด้วยค่าเริ่มต้น:

 let string = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mattis lorem et leo laoreet fermentum. Mauris pretium enim ac mi tempor viverra et fermentum nisl. Sed diam nibh, posuere non lectus at, ornare bibendum erat. Fusce mattis sem ac feugiat vulputate. Morbi at nunc maximus, vestibulum orci et, dictum neque. Vestibulum vulputate augue ac libero vulputate vestibulum. Nullam blandit et sapien non fermentum. Proin mollis nisl at vulputate euismod. """ var wordsCountByLine = [Int: Int]() let lines = string.split(separator: "\n") for (index, line) in lines.enumerated() { let lineWordsCount = line.split(separator: " ").count wordsCountByLine[index, default: 0] += lineWordsCount } print(wordsCountByLine) // [2: 10, 4: 15, 5: 7, 6: 6, 7: 6, 0: 8, 1: 7, 3: 10]

แผนที่และตัวกรองเฉพาะพจนานุกรม

การจัดกลุ่มองค์ประกอบลำดับ

ข้อจำกัดประเภทที่เกี่ยวข้องในโปรโตคอล

ข้อเสนอ SE-0142 แนะนำการเพิ่มส่วนคำสั่งแบบมีเงื่อนไขให้กับการประกาศประเภทที่เกี่ยวข้อง

 extension Sequence where Element: Numeric { var sum: Element { var result: Element = 0 for element in self { result += element } return result } } [1,2,3,4].sum

การเก็บถาวรและการทำให้เป็นอันดับ (การเข้ารหัส / ถอดรหัส)

ก่อนหน้านี้ ในการเรียงลำดับประเภทที่กำหนดเองบางประเภท คุณจะต้องใช้โปรโตคอล NSCoding เก่าและเป็นที่รู้จัก ปัญหาคือประเภทที่ไม่ใช่คลาส เช่น struct และ enum ไม่สอดคล้องกับโปรโตคอลนี้ ดังนั้นนักพัฒนาจึงไม่ต้องทำอะไรมากไปกว่าการใช้แฮ็ก เช่น การเพิ่มเลเยอร์ของความเข้ากันได้เพิ่มเติมโดยการสร้างคลาสที่ซ้อนกันซึ่งสอดคล้องกับ NSCoding

Swift 4 มีวิธีแก้ปัญหาที่สะดวกมากสำหรับปัญหานี้ด้วย SE-0166 - การแนะนำโปรโตคอล Codable :

 struct Employee: Codable { let name: String let age: Int let role: Role enum Role: String, Codable { case manager case developer case admin } } struct Company { let name: String let officeLocation: Location? let employees: [Employee] } struct Location : Codable { let latitude: Double let longitude: Double }

ในกรณีง่ายๆ เช่นนี้ สิ่งที่คุณต้องมีคือการเพิ่ม Codable protocol ที่สอดคล้องกับประเภทที่คุณกำหนดเองทั้งหมด คอมไพเลอร์จะทำสิ่งมหัศจรรย์ทั้งหมดให้คุณ แค่นั้นแหละ!

Codable เป็น typealias สำหรับองค์ประกอบของ Decodable และ Encodable โปรโตคอลเพื่อให้คุณสามารถประกาศตัวอย่างเช่นเพียง Decodable สอดคล้องโปรโตคอลถ้าคุณต้องการที่จะถอดรหัสเช่นประเภทของคุณจากข้อมูล JSON

การเข้ารหัส

หากคุณต้องการทำให้เป็นอนุกรมหรือดีซีเรียล Codable ค่า Codable - คุณต้องใช้และตัวเข้ารหัสหรือตัวถอดรหัส Swift 4 มาพร้อมกับชุดตัวเข้ารหัส/ตัวถอดรหัสสำหรับ JSON และรายการคุณสมบัติแล้ว รวมถึง CocoaError ใหม่สำหรับข้อผิดพลาดประเภทต่างๆ ที่อาจเกิดขึ้นระหว่างการเข้ารหัส/ถอดรหัส NSKeyedArchiver & NSKeyedUnarchiver ยังรองรับประเภท Codable

 let employee = Employee(name: "Peter", age: 27, role: .manager) let company = Company(name: "Awesome Company", officeLocation: nil, employees: [employee]) let encoder = JSONEncoder() let companyData = try encoder.encode(company) let string = String(data: companyData, encoding: .utf8)! print(string) >>> { "name" : "Awesome Company", "employees" : [ { "name" : "Peter", "age" : 27, "role" : "manager" } ] }

เค้กชิ้นหนึ่งใช่มั้ย?

ถอดรหัส

ตัวถอดรหัสใช้เพื่อ Codable ประเภท Codable แบบกำหนดเองจาก Data ไม่ทราบว่าจะถอดรหัสประเภทใดจากข้อมูล ดังนั้นคุณควรระบุประเภทที่จะถอดรหัส เช่น Employee หรือ [Employee] :

 let decoder = JSONDecoder() let jsonData = """ [ { "name" : "Peter", "age" : 27, "role" : "manager" }, { "name" : "Alex", "age" : 26, "role" : "developer" }, { "name" : "Eugene", "age" : 30, "role" : "admin" } ] """.data(using: .utf8)! let employees = try decoder.decode([Employee].self, from: jsonData)
 If one of `Codable` type instances fails to decode, then whole collection will fail to decode.

ชื่อคีย์ที่กำหนดเอง

ในกรณีส่วนใหญ่ ชื่อที่เราใช้ในประเภท Swift แบบกำหนดเองไม่ตรงกับคีย์ในข้อมูล JSON ที่แสดงถึงประเภทนี้ ในการสร้างการแมประหว่างชื่อคุณสมบัติประเภทที่กำหนดเองและคีย์ JSON คุณสามารถสร้าง Enum ที่ซ้อนกันชื่อ CodingKeys ซึ่งควรสอดคล้องกับโปรโตคอล CodingKey :

 struct Country: Decodable { let id: String let name: String let phoneCode: String private enum CodingKeys: String, CodingKey { case id = "alpha3" case name case phoneCode = "phone_code" } }

ถอดรหัสแบบกำหนดเอง

หากคุณมีกรณีที่ซับซ้อน คุณสามารถใช้ตัวเริ่มต้นแบบกำหนดเองจากโปรโตคอลที่ Decodable :

 struct Transaction { let id: Int let action: String let source: String let amount: Int let state: TransactionState let createdAt: Date let authorName: String enum TransactionState: String, Decodable { case done case canceled case processed } } extension Transaction: Decodable { private enum CodingKeys: String, CodingKey { case id case action = "action_name" case source = "source_name" case amount case state case createdAt = "created_at" case author } private enum AuthorKeys: String, CodingKey { case fullName = "full_name" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) actionName = try container.decode(String.self, forKey: .action) sourceName = try container.decode(String.self, forKey: .source) let createdAtValue = try container.decode(Double.self, forKey: .createdAt) createdAt = Date(timeIntervalSince1970: createdAtValue) state = try container.decode(TransactionState.self, forKey: .state) amount = try container.decodeIfPresent(Int.self, forKey: .amount) ?? 0 do { let authorContainer = try container.nestedContainer(keyedBy: AuthorKeys.self, forKey: .author) authorName = try authorContainer.decode(String.self, forKey: .fullName) } catch { authorName = "" } } }

การเข้ารหัสค่าคีย์

หนึ่งในคุณสมบัติที่มีประโยชน์ที่ Swift 4 นำมาคือ Smart KeyPaths ที่อธิบายไว้ใน SE-0161 ไม่เหมือนกับ Swift 3 #keyPath() ซึ่งไม่ได้ถูกพิมพ์อย่างเข้มงวดและใช้ได้เฉพาะกับสมาชิก Objective-C เท่านั้น Swift 4 KeyPath เป็นคลาสทั่วไป ซึ่งหมายความว่าตอนนี้มีการพิมพ์พาธที่สำคัญอย่างเข้มงวด มาดูตัวอย่างกัน:

 struct User { var username: String }

รูปแบบทั่วไปของคีย์พาธ \<Type>.<path> โดยที่ <Type> คือชื่อประเภท และ <path> คือสายโซ่ของคุณสมบัติตั้งแต่หนึ่งรายการขึ้นไป เช่น \User.username :

 let user = User(username: "max") let username = user[keyPath: \User.username] // "max"

คุณยังสามารถเขียนค่าใหม่โดยใช้พาธคีย์นี้ได้ หากค่านั้นเปลี่ยนแปลงได้:

 var user = User(username: "max") user[keyPath: \User.username] = "alex" // "alex"

เส้นทางหลักไม่จำกัดลำดับชั้นหนึ่งระดับ:

 struct Comment { let content: String var author: User } let max = User(username: "max") let comment = Comment(content: "Nice post!", author: max) let authorUsername = comment[keyPath: \Comment.author.username] // "max"

เส้นทางคีย์สามารถเก็บไว้ในตัวแปร:

 let authorKeyPath = \Comment.author let usernameKeyPath = authorKeyPath.appending(path: \.username) let authorUsername = comment[keyPath: usernameKeyPath] // "max"

คุณยังสามารถใช้เส้นทางคีย์สำหรับคุณสมบัติทางเลือกและคุณสมบัติที่คำนวณได้:

 struct Post { let title: String var comments: [Comment] var topComment: Comment? { return comments.first } } let max = User(username: "max") let alex = User(username: "alex") var post = Post(title: "What's new in Swift 4", comments: []) let topCommentAuthorUsernameKeyPath = \Post.topComment?.author.username post[keyPath: topCommentAuthorUsernameKeyPath] // nil let comment = Comment(content: "", author: alex) let anotherComment = Comment(content: "Nice post!", author: max) post.comments = [comment, anotherComment] post[keyPath: topCommentAuthorUsernameKeyPath] // "alex"

แม้ว่า SE-0161 จะเน้นการสนับสนุนตัวห้อยในเส้นทางหลัก แต่ยังไม่ได้ใช้งาน:

 post.comments[keyPath: \.[0].content] // error: key path support for subscript components is not implemented let firstCommentAuthorKeyPath = \Post.comments[0].author // error: key path support for subscript components is not implemented

KVO

นอกจากเส้นทางคีย์ใหม่แล้ว API การสังเกตคีย์-ค่ายังได้รับการอัปเดตใน Swift 4 ด้วย

 New KVO APIs depend on Objective-C runtime and works for `NSObject` subclasses only, so it can't be used for Swift structs and classes which don't inherit `NSObject`. In order to observe property it should be marked as `@objc dynamic var`.
 class User: NSObject { @objc dynamic var name: String var username: String init(name: String, username: String) { self.name = name self.userName = userName super.init() } } let user = User(name: "Max", username: "max") let nameObservation = user.observe(\.name, options: [.new, .old]) { user, change in // NSKeyValueObservation if let oldValue = change.oldValue, let newValue = change.newValue { print("fullName has changed from \(oldValue) to \(newValue)") } else { print("fullName is now \(user.name)") } } user.name = "Alex" // name has changed from Max to Alex

เรียก invalidate() เมธอด invalidate() หากคุณต้องการหยุดการสังเกต

 nameObservation.invalidate() user.name = "Elina" // observer isn't get called

มันยังหยุดทำงานเมื่อถูกกำหนด ดังนั้นโปรดเก็บไว้ในทรัพย์สินหรือที่อื่นหากคุณต้องการเก็บรักษาไว้

ช่วงด้านเดียว

SE-0172 แนะนำช่วง "ด้านเดียว" ซึ่งสร้างขึ้นผ่านเวอร์ชัน prefix/postfix ของโอเปอเรเตอร์ช่วงที่มีอยู่ และโปรโตคอล RangeExpression ใหม่เพื่อลดความซับซ้อนในการสร้างเมธอดที่ใช้ช่วงประเภทต่างๆ

ลำดับอนันต์

คุณสามารถใช้ช่วงด้านเดียวเพื่อสร้างลำดับอนันต์:

 let letters = ["a", "b", "c", "d"] let numberedLetters = Array(zip(1..., letters)) // [(1, "a"), (2, "b"), (3, "c"), (4, "d")]
 let string = "Hello, Mind Studios!" let index = string.index(of: ",")! string[..<index] // "Hello" string[...index] // "Hello,"

การใช้ช่วงด้านเดียวในการจับคู่รูปแบบ

 let value = 5 switch value { case 1...: print("greater than zero") case 0: print("zero") case ..<0: print("less than zero") default: break }

การสมัครรับข้อมูลทั่วไป

SE-0148 ตัวห้อยสามารถมีอาร์กิวเมนต์ทั่วไปและประเภทการส่งคืนได้

 struct JSON { let data: [String: Any] subscript<T>(key: String) -> T? { return data[key] as? T } } let jsonDictionary: [String: Any] = [ "name": "Ukraine", "flag": "", "population": 42_500_000 ] let json = JSON(data: jsonDictionary) let population: Int? = json["population"] // 42500600
 extension Dictionary where Value == String { subscript<T: RawRepresentable>(key: Key) -> T? where T.RawValue == Value { guard let string = self[key] else { return nil } return T(rawValue: string) } } enum Color: String { case red case green case blue } let dictionary = [1: "red"] let color: Color? = dictionary[1] // red

การจำกัดวัตถุประสงค์-C การอนุมาน

Swift 4 ย่อ @objc การอนุมานโดยจำกัดให้เหลือเฉพาะกรณีเหล่านั้นเมื่อต้องมีการประกาศสำหรับ Objective-C (SE-0160)
การทำเช่นนี้จะลดขนาดไบนารีของแอปของคุณโดยไม่รวบรวมโค้ด Objective-C ที่ซ้ำซ้อน หากคุณไม่ได้ใช้ และให้การควบคุมมากขึ้นว่าเมื่อใดที่ @objc จะถูกอนุมาน คลาสที่ได้รับ NSObject จะไม่อนุมาน @objc อีกต่อไป

แต่มีบางสถานการณ์ที่โค้ด Swift จะยังคงมีการอนุมานโดยปริยาย:

  • ประกาศที่มีแอตทริบิวต์ @objc

  • คำประกาศที่เป็นไปตามข้อกำหนดของโปรโตคอล @objc

  • ประกาศที่มี @IBAction, @IBInspectable, @IBOutlet, @NSManaged, @GKInspectable แอตทริบิวต์

ในการเปิดใช้งานการอนุมาน @objc สำหรับทั้งคลาส คุณสามารถใช้แอตทริบิวต์ @objcmembers ใหม่ได้
หากต้องการปิดใช้งานการอนุมาน @objc สำหรับส่วนขยายหรือฟังก์ชันเฉพาะ - เพิ่มแอตทริบิวต์ @nonobjc ใหม่

การเขียนคลาสและโปรโตคอล

ใน Swift 4 เราสามารถเขียนโปรโตคอลร่วมกับ Swift ประเภทอื่นๆ ได้แล้ว:

 User & Codable & CustomStringConvertible typealias MyType = User & Codable & CustomStringConvertible

ประโยชน์ของ Swift 4

ข้อดีของ Swift 4 นั้นใหญ่มาก เนื่องจากมักเกิดขึ้นเมื่อ Apple เปิดตัวเวอร์ชันภาษาใหม่ นอกจากประสิทธิภาพภาษาที่ดีขึ้นแล้ว ยังทำให้กระบวนการย้ายข้อมูลมีเสถียรภาพมากขึ้นอีกด้วย ย้อนกลับไปที่กระบวนการย้ายข้อมูล Swift 2.2 เป็น 3.0 เราระลึกถึงกระบวนการที่สับสนในการถ่ายโอนการพึ่งพาทั้งหมด การเปลี่ยนแปลง Swift 4.0 ช่วยให้เราออกจากไลบรารีของบุคคลที่สามโดยไม่ต้อง "ย้าย" ไลบรารีเหล่านั้น - คุณเพียงแค่ต้องอัปเดต Swift เอง

นอกจากนี้ ในส่วนของการปรับปรุง Swift 4.0 กับ 3.0 ขนาดไฟล์ไบนารีที่คอมไพล์ก็เปลี่ยนไป ซึ่งทำให้ขนาดของแอปลดลง เช่น แอปพลิเคชันมือถือที่เคยมีน้ำหนัก 20 MB และในเวอร์ชัน Swift ใหม่ล่าสุดจะใช้เวลาประมาณ 17 MB และมีความแตกต่างพื้นฐานระหว่าง Swift 4 และ Swift 3 - การแก้ไขข้อผิดพลาดได้เกิดขึ้นแล้ว และภาษาก็เร็วขึ้นเล็กน้อย

ผ่านไปหลายปีแล้วตั้งแต่มีการใช้งาน Swift และยังคงพัฒนาต่อไปในทุกการอัพเดทที่จะมาถึง ด้วยภาษาใหม่แต่ละภาษาช่วยเปลี่ยนมุมมองการพัฒนาใหม่ๆ ที่ไม่เคยเกิดขึ้นมาก่อน และเราตั้งตารอที่จะสำรวจขอบเขตอันไกลโพ้นของ iOS

อย่าพลาดบทความของเราเกี่ยวกับ MVP เทียบกับ MVC เทียบกับ MVVM เทียบกับ VIPER สำหรับการพัฒนา iOS

เขียนโดย Max Mashkov และ Elina Bessarabova