Skip to main content

Tại sao chúng ta nên dùng Interface Builder (XIB) thay vì Storyboard?


Tôi rất thích sử dụng Interface Builder (IB) khi lập trình iOS. Nó giúp tôi làm giảm bớt rất nhiều dòng code mà có thể xuất hiện trên các view layer. Nhưng sử dụng storyboards cũng mang lại rất nhiều vấn đề. Bài viết này tôi sẽ giải thích cho các bạn vì sao sử dụng file xib riêng biệt giúp hiệu suất của tôi hiệu quả hơn là sử dụng Storyboard.
Note: Đây hoàn toàn là ý kiến và quan điểm cá nhân của riêng tôi, do vậy bạn vẫn có thể làm theo cách khác nếu như nó phù hợp và hiệu quả hơn với bạn.

Storyboard thực chất là cái gì?

Có thể coi storyboard như là một bản thiết kế của ứng dụng, bao gồm cả giao diện và luồng chạy của ứng dụng. Sẽ thật là tuyệt vời nếu như bạn join vào một project mà team đó sử dụng storyboards và team đó phối hợp, phân chia công việc rất hiệu quả. Bạn sẽ có cái nhìn rõ ràng về cấu trúc thiết kế của ứng dụng và từ đó bạn sẽ hiểu project đó rất nhanh.
Mặc dù storyboard mang lại rất nhiều lợi ích, nhưng bên cạnh đó nó cũng mang lại một số phiền phức như sau: git merge conflicts, mutable variables, coupled view controllers.

Git merge conflicts

Tưởng tượng rằng bạn đang làm ở màn hình login. Một người khác trong team bạn làm ở màn hình register. Cuối ngày, cả hai đều cố push code lên remote git và BOOM!. Conflict ở khắp mọi nơi.
Tùy thuộc vào độ lớn của project team bạn và mức độ hỏng ở storyboard, bạn sẽ có nhiều hoặc ít conflict, nhưng chắc chắn giải quyết đống conflict này không phải là điều dễ chịu gì.

Mutable variables

Khi sử dụng storyboard, bạn sẽ không thể tạo hàm custom init, do vậy bạn sẽ gặp vấn đề khi muốn truyền dữ liệu từ màn này sang màn khác.
Truyền dữ liệu khi sử dụng storyboard thường là qua cách override phương thức prepareForSegue ở ViewControllerA để đặt giá trị cho variable bên ViewControllerB. Vấn để ở đây là không có gì ngăn bạn thực hiện chuyển segue mà không thiết lập giá trị đó.
Việc không set giá trị của biến unwrapped đó mà đáng lẽ ra đã được set trước khi thực thi segue là một trong những nguyên nhân gây crash app mà sử dụng storyboard.

Coupled view controllers

Khi bạn sử dụng storyboard, bạn thiết lập segue giữa những view controllers. Nếu ViewController A có segue đến ViewControllerB, chúng sẽ bị dính chặt vào nhau. ViewController A biết rằng ViewControllerB sẽ được hiển thị sau đó.
View controller không nên có gắn kết như vậy. Một view controller không nên biết về view controller tiếp theo trong navigation flow. Một trong những cách phổ biến để giải quyết vấn đề này đó là sử dụng navigation coordinators và ủy quyền cho navigation flow thực thi tới đối tượng mới

Sử dụng Xibs

Tạo một file xib cũng đơn giản như tạo một Class


View Controller có thể thiết lập theo đoạn mã dưới đây:
//
//  SampleViewController.swift
//  MediumSamples
//
//  Created by Fernando Ortiz on 1/4/17.
//  Copyright © 2017 Fernando Martín Ortiz. All rights reserved.
//
import UIKit

class SampleViewController: UIViewController {
    
    // This Very important attribute is necessary for this view controller
    // to perform some actions.
    //
    // Note that, in this case, this attribute is declared as 'let', 
    // it's IMMUTABLE.
    let veryImportantAttribute: String

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // In this custom initializer we can send dependencies that are needed
    // from this view controller to work properly.
    // We simply can't forget to pass data. In that case, the project won't compile.
    init(veryImportantAttribute: String) {
        self.veryImportantAttribute = veryImportantAttribute
        super.init(nibName: "SampleViewController", bundle: nil)
    }
    
    // This will be never called, so we don't need to care
    // about its implementation.
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
Lưu ý rằng bạn có thể tạo hàm custom init, do vậy bạn có thể bỏ qua việc truyền dữ liệu từ View Controller này sang View Controller khác.

Chỉnh sửa AppDelegate

AppDelegate cần phải chỉnh sửa để cho phép khởi động ứng dụng mà không cần sử dụng storyboard:
//
//  AppDelegate.swift
//  MediumSamples
//
//  Created by Fernando Ortiz on 1/3/17.
//  Copyright © 2017 Fernando Martín Ortiz. All rights reserved.
//
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        self.window = UIWindow(frame: UIScreen.main.bounds)
        
        let attribute = "Sample"
        let initialViewController = SampleViewController(veryImportantAttribute: attribute)
        
        window!.rootViewController = initialViewController
        window!.makeKeyAndVisible()
        
        return true
    }
}
Đầu tiên chúng ta khởi tạo UIWindow. Sau đó chúng ta khởi tạo root view controller sử dụng custom initializer. Cuối cùng ViewController đó sẽ được đặt làm root view controller của window và window sẽ tạo key visible và bắt đầu flow.

Điểm khác biệt khi sử dụng file XIB riêng biệt

Sử dụng file XIB riêng biệt cho từng View Controller có những lợi thế sau: Phân tách rõ ràng từng màn hình, qua đó tránh được tình trạng 2 người cùng làm ở 1 file và gây conflict khi merge code. Nó cho phép chúng ta dùng những immutable variable. Khi bạn khởi tạo view controlelr, nó sẽ được tạo bởi những thành phần sẵn có, do vậy bạn sẽ không mắc phải những sai lầm khi truyền dữ liệu. Phân tách các View ra theo từng View Controller riêng rẽ, không liên quan gì đến nhau. Tuân thủ qui tắc loosely coupling. Sau này cũng sẽ dễ dàng hơn cho việc maintain code, dễ thay thế.

Khi nào sử dụng Storyboard?

Xib và Storyboard thực ra cũng chỉ là công cụ, tài nguyên để chúng ta sử dụng. Không có công cụ nào là hoàn hảo cả. Và vẫn có những trường hợp sử dụng Storyboard là phương án tối ưu hơn. Ví dụ như:
Khi bạn đang phát triển ứng dụng độc lập. Như vậy sẽ không có conflict khi merge code. Ứng dụng đã có sẵn một flow rõ ràng, các View Controller cố định. Ở trường hợp này, storyboard sẽ đi theo flow của app mà không gặp vấn đề gì.
Hy vọng qua bài viết trên các bạn sẽ hiểu rõ được sự khác nhau giữa file Xib và Storyboard, từ đó chọn ra được phương án tối ưu nhất cho project của mình.

Comments

Popular posts from this blog

Alamofire vs URLSession

Alamofire vs URLSession: a comparison for networking in Swift Alamofire and URLSession both help you to make network requests in Swift. The URLSession API is part of the foundation framework, whereas Alamofire needs to be added as an external dependency. Many  developers  doubt  whether it’s needed to include an extra dependency on something basic like networking in Swift. In the end, it’s perfectly doable to implement a networking layer with the great URLSession API’s which are available nowadays. This blog post is here to compare both frameworks and to find out when to add Alamofire as an external dependency. Build better iOS apps faster Looking for a great mobile CI/CD solution that has tons of iOS-specific tools, smooth code signing, and even real device testing? Learn more about Bitrise’s iOS specific solutions! This shows the real power of Alamofire as the framework makes a lot of things easier. What is Alamofire? Where URLSession...

Swift Tool Belt, Part 1: Adding a Border, Corner Radius, and Shadow to a UIView with Interface Builder

During my iOS work, I’ve assembled a set of code that I bring with me on every iOS project. I’m not talking about large frameworks or CocoaPods here. These are smaller Swift extensions or control overrides that are applicable to many projects. I think of them as my tool belt. In this post, I’ll show you an extension that will add a border, a corner radius, and a shadow to any UIView, UIButton, or UILabel and allow you to preview what it will look like in Interface Builder. Back in 2014, I wrote a blog post on Expanding User-Defined Runtime Attributes in Xcode where I added a border, corner radius, and shadow to a UIView using Interface Builder’s user-defined runtime attributes. This solution had no type checking—you had to type the property you wanted to modify by hand and often had to look up what it was called. You also had to run your project in order to see the effect of the runtime attribute. Starting with Xcode 6 , there is a new mech...

Frame vs Bounds in iOS

This article is a repost of an answer I wrote on Stack Overflow . Short description frame = a view’s location and size using the parent view’s coordinate system ( important for placing the view in the parent) bounds = a view’s location and size using its own coordinate system (important for placing the view’s content or subviews within itself) Details To help me remember frame , I think of a picture frame on a wall . The picture frame is like the border of a view. I can hang the picture anywhere I want on the wall. In the same way, I can put a view anywhere I want inside a parent view (also called a superview). The parent view is like the wall. The origin of the coordinate system in iOS is the top left. We can put our view at the origin of the superview by setting the view frame’s x-y coordinates to (0, 0), which is like hanging our picture in the very top left corner of the wall. To move it right, increase x, to move it down increase y. To help me remember bound...