15 tips để trở thành 1 dev Swift giỏi hơn (phần 1)

400
Bài viết chia sẻ kinh nghiệm của công ty Savvy Apps kể từ khi công ty bắt đầu code các projects mới bằng Swift với phiên bản 2.0 ra đời tháng 9/2015. Các kinh nghiệm này sẽ giúp dev code sạch hơn trong khi vẫn hỗ trợ các dev dần thích nghi với Swift tốt hơn dù vẫn còn quen thuộc với Objective-C.
So với Objective-C, Swift được thêm những tính năng ngôn ngữ để giúp code an toàn hơn, nhanh hơn, đáng tin cậy hơn và có thể dễ dàng debug tốt hơn.
Nội dung chia sẻ gồm 3 phần: phần 1 nói về các tips mà các dev có thể không biết đến, phần 2 dành cho các bạn mới bắt đầu với Swift và phần 3 dành cho các dev đã sử dụng Swift.
1. Các tips về Swift bạn có thể không biết nhưng bạn nên biết
Tận dụng khả năng đọc tốt của constants
Một cách khéo léo để tận dụng các structs trong Swift là tạo 1 file cho tất cả các constants trong app. Đây là cách hữu dụng vì Swift cho phép chúng ta lồng các structures như thế:

Việc này sẽ đem đến 1 namespace cho các constants. Ví dụ, chúng ta có thể sử dụng Constants.FoursquareApi.BaseUrl để tiếp cận constant BaseUrl của Foursquare. Chúng giúp mọi thứ đọc được và cung cấp 1 lớp các encapsulation – đóng gói các constants liên quan.

Tránh NSObject và @objc để tăng hiệu suất

Swift cho phép chúng ta mở rộng các classes từ NSObject để sử dụng các tính năng trong Objective-C của đối tượng khi chạy ứng dụng (runtime). Nó cũng cho phép chúng ta đặt các method Swift với @objc để chỉ ra các methods được sử dụng từ Objective-C.
Điều này đồng nghĩa các method đang chuẩn bị sử dụng dynamic dispatch (cơ chế gọi hàm động) thay vì dispatch tĩnh hoặc dispatch vtable (virtual function table – bảng hàm ảo). Kết quả cuối cùng là các methods từ Objective-C có hiệu suất chậm hơn gấp 4 lần khi được gọi. Trên thực tế, hiệu suất có thể không đáng kể nhưng khi trang bị kiến thức này, chúng ta biết rằng thực hiện method trong Swift nhanh hơn 4 lần so với Objective-C.

Sử dụng Method Swizzling trong Swift
Method swizzling là 1 kỹ thuật thay thế việc thực hiện 1 method bằng 1 method khác. Nếu bạn chưa quen với swizzling, hãy xem qua bài viết blog này. Swift đã tối ưu hóa code để gọi các địa chỉ bộ nhớ trực tiếp, thay vì tìm kiếm địa điểm method trong runtime như ở Objective-C. Vì thế, sẽ không thực hiện swizzling mặc định trong class Swift nếu chúng ta không:
  • Vô hiệu hóa hành động tối ưu hóa này với từ khóa dynamic Đây là lựa chọn được ưu tiên hơn và lựa chọn khả dĩ nhất nếu codebase hoàn toàn bằng Swift.
  • Mở rộng NSObject. Thay vì thực hiện cách này cho method swizzling, hãy sử dụngdynamic. Method swizzling có thể thực hiện được trong các classes đã tồn tại, có base class là NSObject nhưng chúng ta nên lựa chọn 1 cách có chọn lọc các method với dynamic .
  • Sử dụng chú thích @objc với method đang được swizzled. Điều này phù hợp nếu method mà chúng ta muốn swizzle cũng cần được thể hiện ra bằng code Objective-C cùng 1 lúc.
Cập nhật: Theo yêu cầu, chúng ta đã thêm 1 ví dụ về swizzle thuần trong Swift. Nó vẫn yêu cầu sử dụng method từ Objective-C, nhưng class của chúng ta không thừa hưởng từ NSObject và methods của chúng ta không đánh dấu là @objc. Tìm hiểu nó trong hành động của 1 playground tại đây.

 

2. Tips Cho Những Người Mới Bắt Đầu
Xóa bỏ code không đồng bộ
Swift có 1 syntax gọn gàng để viết các hàm completion. Chúng ta có các khối completion bằng ObjectiveC, nhưng chúng lại xuất hiện trễ trong giai đoạn phát triển của ngôn ngữ và đã có 1 số syntax khó, như chúng ta thấy ở đây:

 

Với closure syntax mới, Swift giúp điều này trở nên dễ dàng hơn. Bất kì method nào sử dụng 1 closure như parameter cuối cùng đều có thể sử dụng syntax mới của Swift để tạo các callbacks trông sạch sẽ hơn như bên dưới:

 

Kiểm soát truy cập code
Chúng ta thường sử dụng modifier điều khiển tiếp cận phù hợp để hợp thể code. Hợp thể tốt giúp chúng ta hiểu được cách các mảnh code tương tác mà không cần phải nhớ quy trình suy nghĩ của chúng ta hoặc hỏi lại dev.
Swift có cùng cơ chế kiểm soát truy cập của private, internalpublic nhưng Swift không có modifier kiểm soát truy cập protected – thường thông dụng trong các ngôn ngữ lập trình hướng đối tượng khác. Vì sao? Vì protected không đem lại cho chúng ta bất kì sự bảo vệ nào vì 1 subclass có thể thể hiện 1 method protected hoặc property với 1 method hoặc property public mới. protected cũng không mở rộng bất kì khả năng tối ưu hóa nào cho các compiler Swift như những override mới có thể đến từ bất kì đâu.Cuối cùng,protected hỗ trợ tính đóng gói một cách nghèo nàn vì nó ngăn các helpers của subclass tiếp cận thông tin mà subclass đã tiếp cận. Bạn có thể xem thêm tại Swift team’s thoughts on protected here.
Thử nghiệm và hợp thức hóa với Playgrounds
Một playground là 1 môi trường coding tương tác dành cho Swift. Chúng ta có thể tạo playgrounds để kiểm tra và xác nhận các ý tưởng, học Swift và chia sẻ các concepts với nhau. Lúc này các dev không cần phải tạo 1 project mới, mà chỉ cần chọn 1 option khi mở Xcode để tạo 1 playground.
Bạn cũng có thể tạo 1 playground mới từ trong chính Xcode:
Khi chúng ta đã có playground, tất cả những gì chúng ta phải làm là bắt đầu coding và ngay lập tức sẽ nhận được các kết quả:

Playgrounds là cách tốt để prototype và thể hiện các ý tưởng trong code mà không cần bắt đầu 1 project.

Sử dụng các optionals an toàn

Một property không bắt buộc là 1 property có 1 giá trị có giá trị hoặc không có giá trị (nil). Chúng ta có thể ngầm unwrap 1 optional bằng cách theo dõi thên của optional với 1 dấu cảm thán, như optionalProperty!. Nhìn chung đây là điều mà bạn muốn tránh, như được ám chỉ bởi dấu cảm thán với nghĩa là “danger!”

Có vài trường hợp khi sử dụng 1 optional unwrapped không công khai vẫn chấp nhận được. Ví dụ,IBOutlets mặc định là optionals unwrapped ngầm (khi chúng ta click vào và kéo chúng từ Interface Builder) vì UIKit cho rằng chúng ta đã treo các outlets đến Interface Builder. Các outlets đều tùy ý vì IBOutlets được thiết lập ở 1 số điểm sau khởi tạo và theo Swift rules, tất cả các properties bắt buộc phải có các giá trị sau khi khởi tạo. Một trường hợp khác là lấy tên của UIImage tồn tại trong catalog asset:


 

Đây là thiết lập giá trị mặc định của 1 constant property và sẽ không thể thực hiện được mà không có optional unwrapped ngầm. Đồng nghĩa rằng, ! vẫn có nghĩa là “danger!” và trong trường hơp này, chúng ta cần cẩn thận với các typos và xác nhận tên đã phù hợp trước khi chạy. Nhìn chung, nếu chúng ta phải sử dụng các giá trị nil, app có thể bị crash. Các giá trị unwrapping ngầm với ! sẽ nói với compiler là chúng ta biết optional sẽ nil trong runtime. Đây là canh bạc mạo hiểm trong hầu hết các tình huống, vì vậy nên sử dụng pattern if let để xác thực 1 optional có giá trị valid hay là nil:


 

Bỏ qua NSNumber

Objective-C sử dụng các primitives cho các numbers và Foundation Objective-C API đã chung cấp type NSNumber type để đóng họp và mở các primitives. Chúng ta đã có code trông giống như [array addObject:@(intPrimitive)] và [array[0] intValue] khi chúng ta cần quay qua quay lại giữa primitives và các loại đối tượng. Swift không có cơ chế bất tiện này. Thay vào đó, chúng ta có thể thực sự thêm các giá trị Int / Float / AnyObject đến Swift dictionaries và arrays.

Dưới đây là 1 số types Swift tường được sử dụng thay thế cho NSNumber:

  • Swift: Objective-C
  • Int: [NSNumber integerValue]
  • UInt: [NSNumber unsignedIntegerValue]
  • Float: [NSNumber floatValue]
  • Bool: [NSNumber boolValue]
  • Double: [NSNumber doubleValue]

Chúng ta từng sử dụng NSNumber để chuyển đổi giữa các types khác nhau trong Objective-C, nhưng ở Swift, 1 cách đặc thù để chuyển đổi các giá trị là sử dụng 1 constructor đối với type mà chúng ta nhắm tới. Ví dụ, nếu ta có 1 số userId từ 1 API, wrap nó trong 1 NSNumber, và cần phải thể hiện userId là 1 string, trong Objective-C chúng ta đã đánh[userId stringValue]. Swift không sử dụng NSNumber nữa (ngoại trừ khả năng tương thích ngược với Objective-C), bởi vì cấu trúc số trong Swift không có cùng giới hạn như Objective-C.

Lưu ý: Chúng có thể phải làm việc với NSNumber trong codebases gắn kết Objective-C hoặc codebases phụ thuộc vào các libs cũ mà không cần wrappers Swift. Trong trường hợp đó, NSNumber API vẫn không có gì thay đổi lớn.

Thay vào đó, trong Swift, chúng ta thực hiện các chuyển đổi tương tự với các constructors. Ví dụ, nếu userId là Int và chúng ta muốn 1 String, tất cả những gì phải làm là String(userId) để tạo chuyển đổi. Việc này dễ dàng hơn đóng và mở NSNumbers liên tục, nhưng tính đã dạng của các conversions mà NSNumber cung cấp đã thực sự mang đến 1 API tốt và đơn giản.

Giảm code Boilerplate với mặc định Arguments

Với Swift, tham số hàm có thể có các giá trị mặc định. Các arguments mặc định này sẽ giảm đi sự lộn xộn. Nếu hàm được gọi với việc sử dụng các giá trị mặc định, hàm call sẽ ngắn hơn vì arguments mặc định có thể bỏ qua được.

Ví dụ:

 

========
Dịch bởi: IDE Academy (còn tiếp)
Nguồn: SavvyApps