Blog

  • [AWS] Phát triển ứng dụng Lambda bằng Java

    [AWS] Phát triển ứng dụng Lambda bằng Java

    Như các bạn đã biết hiện nay môi trường thực thi sử dụng trong Lambda phần lớn đang sử dụng Node hay Python. Tuy nhiên trên thực tế đôi khi bạn cần sử dụng một môi trường thực thi khác như Java chẳng hạn. Trên thực tế thì AWS cũng đang hỗ trợ khá nhiều môi trường thực thi khác nhau. Có nhiều lý do dẫn tới việc chúng ta phải sử dụng một môi trường thực thi nào đó tuỳ vào tình hình dự án. Trong bài viết này tôi sẽ hướng dẫn các bạn xây dựng ứng dụng Lamba sử dụng môi trường thực thi là Java.

    Các công cụ cần thiết

    Docker

    Chúng ta cần Docker bởi vì công cụ thực thi SAM CLI sẽ sử dụng docker container để thực thi ứng dụng. Bạn thao khảo đường dẫn sau để cài đặt Docker

    SAM

    Chúng ta sẽ sử dụng SAM vì chúng ta cần một môi trường thực thi có thể chạy trên môi trường cục bộ và có thể debug được. Để cài SAM bạn làm theo hướng dẫn sau:

    brew tap aws/tap
    brew install aws-sam-cli
    

    Chúng ta sử dụng brew để cài SAM nên bạn cần cài brew trước. Nếu chưa cài brew thì bạn có thể thao khảo cách cài brew như sau:

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    hieunv@HieuNV ~ % brew --version
    Homebrew 2.2.10
    Homebrew/homebrew-core (git revision f0179; last commit 2020-03-22)
    Homebrew/homebrew-cask (git revision 0a88ae; last commit 2020-03-22)
    

    Để kiểm tra xem bạn đã cài đặt thành công chưa, bạn sử dụng lệnh sau:

    hieunv@HieuNV ~ % sam --version
    SAM CLI, version 0.45.0
    

    Trên Windows thì bạn thao khảo đường dẫn này

    Oracle JDK

    Chúng ta sẽ sử dụng môi trường thực thi Java nên việc cài đặt Oracle JDK là đương nhiên đúng không. Các bạn tham khảo cách cài đặt Oracle JDK tại đây nhé.

    Maven

    SAM sẽ sử dụng maven để build nên chúng ta cần cài đặt thêm maven. Để cài đặt Maven các bạn sử dụng lệnh sau:

    brew install --ignore-dependencies maven
    

    Các bạn chú ý, chúng ta cần sử dụng --ignore-dependencies để bỏ qua việc cài đặt Open JDK nhé. Mặc định maven sẽ sử dụng Open JDK. Tuy nhiên chúng ta đã cài đặt Oracle JDK rồi nên không cần cài Open JDK nữa.

    Tài liệu tham khảo:

    Tạo project bằng SAM

    • Tạo một project mới
    hieunv@HieuNV hieunv % sam init -r java11
    Which template source would you like to use?
    	1 - AWS Quick Start Templates
    	2 - Custom Template Location
    Choice: 1
    
    Which dependency manager would you like to use?
    	1 - maven
    	2 - gradle
    Dependency manager: 1
    
    Project name [sam-app]:
    
    Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
    
    AWS quick start application templates:
    	1 - Hello World Example: Maven
    	2 - EventBridge Hello World: Maven
    	3 - EventBridge App from scratch (100+ Event Schemas): Maven
    Template selection: 1
    
    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: java11
    Dependency Manager: maven
    Application Template: hello-world
    Output Directory: .
    
    Next steps can be found in the README file at ./sam-app/README.md
    
    • Trước khi thực thi bạn cần build project trước
    hieunv@HieuNV hieunv % cd sam-app
    hieunv@HieuNV sam-app % sam build
    Building resource 'HelloWorldFunction'
    /usr/local/bin/mvn is using a JVM with major version 13 which is newer than 11 that is supported by AWS Lambda. The compiled function code may not run in AWS Lambda unless the project has been configured to be compatible with Java 11 using 'maven.compiler.target' in Maven.
    Running JavaMavenWorkflow:CopySource
    Running JavaMavenWorkflow:MavenBuild
    Running JavaMavenWorkflow:MavenCopyDependency
    Running JavaMavenWorkflow:MavenCopyArtifacts
    
    Build Succeeded
    
    Built Artifacts  : .aws-sam/build
    Built Template   : .aws-sam/build/template.yaml
    
    Commands you can use next
    =========================
    [*] Invoke Function: sam local invoke
    [*] Deploy: sam deploy --guided
    
    • Khởi động ứng dụng (trước khi khởi động bạn cần đảm bảo rằng Docker đang hoạt động)
    hieunv@HieuNV sam-app % sam local start-api
    Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
    You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
    2020-03-22 22:07:45  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
    

    Chúng ta thử truy cập vào http://127.0.0.1:3000/hello bằng Postman. Nếu các bạn chưa chạy lần nào thì sẽ phải chờ hơi lâu một chút để Docker tải image cần thiết.

    start-api

    Trong bài viết này tôi đã hướng dẫn các bạn cách viết một API bằng Lambda sử dụng môi trường thực thi Java. Hy vọng bài viết sẽ giúp ích cho dự án của các bạn.

  • iOS/Swift: Một số kỹ thuật truyền dữ liệu phổ biến trong swift

    iOS/Swift: Một số kỹ thuật truyền dữ liệu phổ biến trong swift

    Lời mở đầu

    Trong Swift, chúng ta có khá nhiều cách để truyền dữ liệu qua lại giữa các đối tượng. Bài viết này mình muốn chia sẻ với các bạn về một số kỹ thuật phổ biến và ưu nhược điểm của nó. Bài viết này mình sẽ đề cập đến 3 cách phổ biến đó là Delegation, Closure và NotificationCenter Observation

    Bài toán

    Màn hình của mình cần làm có một UITableView, trong UITableView này chứa các UITableViewCell các cell thì có chứa 2 UIButton Dark và Light. Việc của mình phải làm là mỗi khi người dùng tương tác với button ở trong cell thì UIViewController sẽ bắt được sự kiện và hiển thị lên màn hình thông tin cell và hành động mà người dùng vừa tương tác.

    Các bạn tải về project bắt đầu này để tiện hơn trong quá trình thực hành nhé:

    Delegation

    Delegation là một design pattern cho phép đối tượng gửi thông điệp(data, message …) đến đối tượng khác khi có một sự kiện xảy ra. Ví dụ ta có 2 đối tượng A và B, trên B thực hiện hành động gửi thông điệp sang A để A thực hiện hành động dựa trên kết quả hành động trên B.

    Do chúng bài toán của chúng ta cần bắt hành động của người dùng khi họ action lên các button trên cell vì thế trong trường hợp này trong UITableViewCell chúng ta cần tạo protocol cho MyTableViewCell.swift cụ thể như sau:

    protocol ActionOnCell: class {
        func didTapDark(indexPath: IndexPath?)
        func didTapLight(indexPath: IndexPath?)
    }

    Ở đây mình tạo ra protocol định nghĩa các action trên cell. Khi người dùng bấm vào button Dark nó sẽ call protocol này tương tự với Button Light.

    Tiếp đến chúng ta tạo thêm 2 variables để lưu delegate và indexPath:

    var indexPath: IndexPath?
    weak var delegate: ActionOnCell?
    • indexPath để lưu lại cell mà ngươi dùng tương tác.
    • delegate để biết đối tượng nào đang conform protocol của MyTableViewCell

    Đối với delegate chúng ta cần khai báo weak để tránh tham chiếu strong dẫn đến Retain cycle, không giải phóng được các object này vì nó đang tham chiếu tới nhau.

    Tiếp đến chúng ta cần gọi các protocol tương ứng khi người dùng tương tác lên các button trên cell:

        @IBAction func dark(_ sender: Any) {
            if let delegate = delegate {
                delegate.didTapDark(indexPath: indexPath)
            }
        }
    
        @IBAction func light(_ sender: Any) {
            if let delegate = delegate {
                delegate.didTapLight(indexPath: indexPath)
            }
        }

    Vậy là chúng ta đã setup protocol cho MyTableViewCell.swift giờ tất cả những UIViewController nào sử dụng cell này đều có thể conform protocol của nó để bắt được event khi người dùng tương tác lên các button cell.

    Giờ chúng ta quay lại file ViewController.swift kéo xuống hàm cellForRowAt indexPath của tableview và update như sau:

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "\(MyTableViewCell.self)", for: indexPath) as! MyTableViewCell
            cell.indexPath = indexPath
            cell.delegate = self
            return cell
        }

    Chúng ta gán lại giá trị indexPath cho các cell. Và set delegate cho Cell là self(viewController hiện tại)
    Lúc này XCode sẽ báo lỗi Cannot assign value of type ‘ViewController’ to type ‘ActionOnCell?’ vì chúng ta chưa conform protocol ActionOnCell trên ViewController.

    Vì vậy chúng ta sẽ tạo 1 extension của ViewController và conform ActionOnCell

    extension ViewController: ActionOnCell {
        func didTapDark(indexPath: IndexPath?) {
            showDark(indexPath: indexPath)
        }
    
        func didTapLight(indexPath: IndexPath?) {
            showLight(indexPath: indexPath)
        }
    }

    Khi người dùng tap vào button trên cell nó sẽ call protocol tương ứng và trả dữ liệu về cho viewController vì vậy bạn có thể sử dụng dữ liệu mà bạn muốn để update dữ liệu lên màn hình.

    Các bạn Build project sẽ nhận được kết quả như sau:

    NotificationCenter observation

    Là một cơ chế gửi thông báo cho phép truyền phát thông tin đến các đối tượng đã đăng ký quan sát notification đó.

    Chúng ta sẽ dựa trên 2 cơ chế post và observer của NotificationCenter để gửi và nhận dữ liệu giữa các đối tượng. Đối với notification thì chúng ta có thể sử dụng gửi thông tin từ một đối tượng đến nhiều đối tượng khác đã đăng ký theo dõi notification đó.

    Cụ thể trong bài toán này chúng ta sẽ làm như sau:
    Tạo 2 notification Name đinh nghĩa việc người dùng bấm vào Button Dark và Light:

    extension Notification.Name {
        static let didTapDarkNotification = NSNotification.Name(rawValue: "didTapDarkNotification")
        static let didTapLightNotification = NSNotification.Name(rawValue: "didTapLightNotification")
    }

    Mở file MyTableViewCell.swift thực hiện việc phát(post) thông báo tại 2 event của 2 button trên cell cụ thể như dưới đây:

        @IBAction func dark(_ sender: Any) {
            NotificationCenter.default.post(name: .didTapDarkNotification, object: indexPath)
        }
    
        @IBAction func light(_ sender: Any) {
            NotificationCenter.default.post(name: .didTapLightNotification, object: indexPath)
        }

    Nhiệm vụ của 2 hàm này là để khi nào người dùng bấm vào nut Dark hoặc Light thì NotificationCenter sẽ gửi đi một notification, những đối tượng nào đang lắng nghe các notification name đó sẽ nhận được thông điệp.

    Vì vậy chúng ta cần add observer cho ViewController.swift để nó nhận được thông tin khi người dùng tương tác lên các button trên cell.
    Chúng ta sẽ làm như sau:

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            NotificationCenter.default.addObserver(self, selector: #selector(didTapDark(noti:)), name: .didTapDarkNotification, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(didTapLight(noti:)), name: .didTapLightNotification, object: nil)
        }
    
        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
            NotificationCenter.default.removeObserver(self)
        }

    Chúng ta cần đăng ký theo dõi notification ở viewWillApper và Remove theo dõi notification khi viewWillDisappear vì nếu chúng ta không remove view controller sẽ luôn nhận được thông báo khi mà nó còn trong view hierarchy dẫn đến những lỗi không mong muốn.

    // MARK: NotificationCenter handle
    extension ViewController {
        @objc func didTapDark(noti: Notification) {
            showDark(indexPath: noti.object as? IndexPath)
        }
    
        @objc func didTapLight(noti: Notification) {
            showLight(indexPath: noti.object as? IndexPath)
        }
    }

    Giờ build ứng dụng và các bạn sẽ nhận được kết quả tương tự như phần delegation

    Closure

    Nếu các bạn muốn tìm hiểu rõ hơn về Closure hãy đọc tài liệu tham khảo của Apple: https://docs.swift.org/swift-book/LanguageGuide/Closures.html

    Ý tưởng ở đây chúng ta sẽ tạo 1 closure callback có 2 tham số là action type và dữ liệu truyền lại là index path.

    Mở file MyTableViewCell.swift và thêm 1 enum định nghĩa các kiểu action trên cell

    enum ActionType {
        case dark
        case light
    }

    Tạo closure cho MyTableViewCell

    var didTapButton: ((ActionType, IndexPath?) -> Void)?

    Gọi closure khi người dùng bấm vào các nút trên cell

        @IBAction func dark(_ sender: Any) {
            if let didTapButton = didTapButton {
                didTapButton(.dark, indexPath)
            }
        }
    
        @IBAction func light(_ sender: Any) {
            if let didTapButton = didTapButton {
                didTapButton(.light, indexPath)
            }
        }

    Tiếp đến để nhận được event khi người dùng bấm vào các button chúng ta cần gán giá trị cho closure ở nơi mà bạn muốn. Cụ thể trong trường hợp này là ViewController.swift

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "\(MyTableViewCell.self)", for: indexPath) as! MyTableViewCell
            cell.indexPath = indexPath
            cell.didTapButton = {[weak self] (action, index) in
                guard let self = self else {return}
                if action == .dark {
                    self.showDark(indexPath: index)
                } else {
                    self.showLight(indexPath: index)
                }
            }
            return cell
        }

    Giờ run ứng dụng chúng ta sẽ được kết quả như 2 cách trên.

    Đánh giá

    • Delegation: là cách dùng rất phổ biến và đễ sử dụng nhât khi truyền dữ liệu giữa các đối tượng. Nó dựa vào các protocol để truyền dữ liệu qua lại giữa các đối tượng. Thường dùng trong các trường hợp truyền dữ liệu dạng 1 – 1. Tuy nhiên nếu bạn muốn sử dụng delegate dạng 1 – n hãy tham khảo bài viết này https://magz.techover.io/2019/08/09/swift-design-patterns-multicast-delegate/
    • NotificationCenter: Là dạng truyền nhận dữ liệu 1 – n hoặc 1 – n. Và khi một thông báo được đẩy đi tất cả những đối tượng đã đăng ký nhận thông báo đó sẽ nhận được thông báo. Khi bạn dùng cần phải kiểm soát chặt các notification. Trong mỗi trường hợp khác nhau chúng ta lại có cách đăng ký notification và remove nó ở các chỗ khác nhau.
      VD:
      – Đối với các trường hợp bình thường chúng ta sẽ phải thêm notification ở ViewWillAppear và remove nó ở viewWillDisappear để đảm bảo lần viewWillAppear tiếp theo nó không đăng ký lại dẫn đến bị lặp nhiều lần.
      – Chúng ta không thể để ở viewDidLoad vì nếu remove ở ViewWillDisappear thì khi pop lại sẽ mất notification, nếu không remove thì khi nó vẫn còn trong view hierarchy chưa bị giải phóng nó sẽ vẫn nhận được notification mặc dù chungs ta không mong muốn điều đó.
      Trong tất cả thì cách này là cách rắc rối nhất. Bạn chỉ nên dùng khi muốn thực hiện dạng truyền dữ liệu 1-n
    • Closure: Là cách dùng khá phổ biến hiện tại, vì nó là cách viết khá gọn gàng vì vậy tiết kiệm được thời gian code. Tuy nhiên cách viết của nó khá khó hiểu với người mới. Các bạn cũng có thể nhận thấy sự phổ biến của nó thông qua các hàm completion, callback mà Apple đã sử dụng rất nhiều trong các API của họ.

    Tổng kết

    Mình hi vọng bài viết giúp cho các bạn có thể lựa chọn các cách truyền dữ liệu phù hợp với bài toán của mình. Chúc các bạn thành công!

    Nếu bài viết có vấn đề gì các bạn hãy comment xuống dưới để mình update nhé!

  • [AWS] Serverless và SAM, bạn chọn dùng cái nào?

    [AWS] Serverless và SAM, bạn chọn dùng cái nào?

    Mình đã viết khá nhiều bài sử dụng Serverless, tại sao mình lại viết bài này. Thực ra mình cũng mới bắt đầu làm AWS Lambda được một thời gian ngắn. Dự án đầu tiên mình làm Lambda thì đã các bạn đi được đã chọn Serverless để phát triển. Dự án thứ hai mình làm với AWS Lambda thì khách hàng đưa cho mình bộ mã nguồn đã được cấu hình sử dụng Serverless. Mọi thứ đều có vẻ ổn cho đến một ngày mình quyết định thử debug Lambda bằng Visual Studio Code. Mọi thứ trở nên phức tạp và mình tìm thấy SAM, dường như nó đã giải quyết vấn đề của mình nên mình quyết định viết bài này để cho các bạn nếu mới đến với thế giới AWS thì có thể dễ dàng lựa chọn thứ mình cần.

    Chọn Serverless hay SAM?

    Dự án đầu tiên mình dùng Serverless và viết bằng JavaScript, mọi thứ đều ổn vì mình chỉ dùng Serverless có kết hợp với Serverless Offline để chạy các hàm Lambda API trên máy tính cá nhân được. Việc debug cũng không gặp trở ngại gì do Serverless Offline có hỗ trợ debug. Thế nhưng đến dự án tiếp theo, ngôn ngữ được chọn làm môi trường thực thi là Python và mình thực sự gặp khó khăn. Mình vẫn có thể chạy được các hàm Lambda trên máy tính cá nhân nhưng không thể debug đươc. Và thế là mình bắt đầu tìm hiểu để giải quyết vấn đề này. Rồi mình tìm thấy SAM và mọi thứ dường như được giải quyết.

    Ngôn ngữ nào được hỗ trợ?

    • Serverless Offline hỗ trợ những ngôn ngữ sau:
      • Python
      • Ruby
      • Node
    • SAM hỗ trợ nhưng ngôn ngữ sau:
      • Python
      • Ruby
      • Node
      • Java
      • .NET Core

    Được hỗ trợ như thế nào?

    • Serverless Offline là plugin được cá nhân phát triển. Nó không phải gói được hỗ trợ chính thức từ AWS.

    • SAM được hỗ trợ chính thức từ AWS.

    Hỗ trợ debug như thế nào?

    • Serverless Offline chỉ hỗ trợ debug với Node.

    • SAM thì có vẻ như đã hỗ trợ tất cả các trình thực thi ở trên. Mình đã thử debug với Java thì thấy vẫn OK.

    Môi trường thực thi

    • Serverless chạy trực triếp trên máy host.
    • SAM thì sử dụng container trong docker để thực thi.

    Các bạn có thể tham khảo hướng dẫn sử dụng Serverless ở các tài liệu sau nhé:

    Kết luận

    SAM dường như có những lợi thế hơn hẳn so với Serverless. Nếu bạn quyết định phát triển bằng Node thì bạn sẽ không gặp nhiều khó khăn khi dùng Serverless hay SAM. Nếu bạn chọn một môi trường thực thi khác như Python hay Ruby hay bất kỳ môi trường nào khác thì lựa chọn SAM sẽ là quyết định sáng suốt hơn đấy. Mình sẽ hướng dẫn các bạn sử dụng SAM trong loạt bài viết về SAM sau nhé.

  • Basic Test in Swift (P1)

    Basic Test in Swift (P1)

    Trong lập trình, để có thể tự kiểm tra app để hoạt động chính xác hay chưa, thì ta phải test hết toàn bộ các case xảy ra. Tuy nhiên, nếu làm bằng 1 cách test thủ công thì rất tốn thời gian.
    Để có thể test tự động, thì có thể dùng Unit test. Vậy ở bài viết này, mình sẽ giới thiệu về cách sử dụng Unit test trong lập trình iOS.

    Nội dung bài viết

    • Unit test là gì?
    • Giới thiệu về unit test trong iOS
    • Useful Test
    • Khởi tạo Test Target
    • Run the test
    • Functional test Demo

    Unit test là gì?

    • Là phương pháp dùng để kiểm tra tính đúng đắn của một đơn vị source code. Một Unit (đơn vị) source code là phần nhỏ nhất có thể test được của chương trình, thường là một phương thức trong một lớp hoặc một tập hợp các phương thức thực hiện một mục đích thiết yếu.
    • Bạn viết các test case để Xcode tiến hành test các test case đó.

    Giới thiệu về Unit test trong iOS

    Xcode cung cấp 3 kiểu test chính:

    • Functional test: tập trung vào test các func.
    • Performance tests: tập trung vào đo lương thời gian app của bạn thực thi xong các task trên các loại thiết bị khác nhau.
    • User Interface tests (UI Tests): tập trung vào những tác vụ của người dùng trên UI.

    Note:

    • Functional test & performance test: Là những đoạn test mà bạn tự viết để test sự hoạt động của các func trong app.
    • UI Test: Ghi lại những thứ mà bạn tương tác trên UI của app.

    Useful Test

    1 test case được coi là useful khi:

    • Test case phải có khả năng fail: Nếu test đó không thể fail, thì việc test sẽ rất vô giá trị, bạn nên xốa nó đi.
    • Test case phải có khả năng success: Nếu test đó không thể success, thì việc test sẽ rất vô giá trị, tương tự ở trên, bạn nên xốa nó đi.
    • Test case phải được refactor và được viết đơn giản

    Khởi tạo 1 Test Target

    Để viết được test, trước hết cần tạo 1 Unit test target để viết test:

    Điền tên cho class rồi chọn Next.

    1. Xcode imports sẵn cho bạn frameworks XCTest và class được tạo là subclass của XCTestCase.
    2. func setUp(): dùng để thiết lập lại trạng thái trước mỗi lần test.
    3. func tearDown(): dùng để thực hiện dọn dẹp sau khi mỗi lần test kết thúc.
    4. measure: dùng để đo thời gian thực hiện xong việc test -> giúp test performance.

    Trình tự mỗi khi thực hiện test 1 test case như sau:

    Run the Tests:

    Có 3 cách để run test:

    • Product ▸ Test or Command-U: Cách này sẽ run tất cả test classes trong project.
    • Click vào button mũi tên ở Test navigator.
    • Click chọn nút hình kim cương để run 1 test case cụ thể.

    Basic Functional Test Demo

    Ở ví dụ này, mình sẽ tiến hành việc test xem dữ liệu học sinh ở file json đã chính xác chưa.

    Tạo 1 class Person:

    class Person: Decodable {
        let name: String
        let age: Int
        
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    

    Tạo 1 class Service chứa các logic thực hiện việc lấy dữ liệu:

    class Service {
        func getListStudent() -> [Person] {
            var students: [Person] = []
            guard let url = Bundle.main.url(forResource: "config", withExtension: ".json") else {
                return students
            }
            
            
            do {
                let data = try Data(contentsOf: url)
                let results = try JSONDecoder().decode([Person].self, from: data)
                students += results
            } catch {
                print("Can get data, error: \(error)")
            }
            return students
        }
    }

    Tiến hành việc viết test thông tin cho sinh viên đầu tiên:

    1. @tesable import + tên project: Cho phép unit tests truy cập đến toàn bộ các dữ liệu kiểu internal của project.
    2. Khai báo 1 biến kiểu service.
    3. Khởi tạo sut mỗi lần bắt đầu thực hiện test 1 test case.
    4. Set sut = nil mỗi khi kết thúc việc test 1 test case.
    5. Viết func test để test thông tin học sinh 1.
    6. Viết các hàm để test.

    Note: Khi viết func để test thì phải tên func phải luôn bắt đầu với từ test để Xcode có thể biết đó là 1 test.

    Thực hiện tiến hành test func, nhận được kết quả dữ liệu cần kiểm tra đã chính xác:

    Để đảm bảo đây là 1 Userful test, thực hiện sửa đổi name của sinh viên 1 trong file json thành "Cuong1" và tiến hành test lại func, nhận được kết quả sau:

    Việc test thất bại, đồng thời XCode cũng thông báo ra kết quả để biết sai ở đâu.

    Ở phần tiếp theo, mình sẽ nói về performance test, UI Test và 1 vài thứ hay ho mà Xcode support cho việc test.

  • iOS/Testflight: Cách sử dụng iOS Beta testing

    iOS/Testflight: Cách sử dụng iOS Beta testing

    Lời mở đầu

    Ở bài viết trước mình đã hướng dẫn cho các bạn cách upload ứng dụng lên store. Nhưng ứng dụng nào trước khi đẩy lên store chúng ta cũng phải có giai đoạn kiểm thử. Điều đó đảm bảo ứng dụng của bạn phát sinh ít lỗi trong quá trình người dùng sử dụng cũng như đảm bảo ứng dụng chạy đúng theo yêu cầu. Vì vậy hôm nay mình sẽ chia sẻ với các bạn tất tần tật về Testflight ứng dụng được Apple phát hành với nhiệm vụ giúp tester dễ dàng tiếp cận bản build hơn.

    Testflight là gì?

    Testflight là một ứng dụng được Apple cung cấp để cho Developer của họ upload ứng dụng lên nhằm mục đích kiểm thử ứng dụng. Ứng dụng này gồm 2 phần: Tính năng quản lí cho developer nằm trong App store connect và ứng dụng Testflight là ứng dụng cho tester.

    Làm thế nào để upload bản build lên Testflight?

    Nếu các bạn chưa biết cách upload bản build của mình lên Testflight hãy đọc bài viết trước của mình nhé: iOS/App store connect: Các bước đẩy ứng dụng của bạn lên App store

    Định nghĩa các loại tester trên Testflight

    • Internal tester: Đây là những App store connect user được phân quyền Admin, App manager, legal, developer … có thể quyền truy cập vào quản lý ứng dụng của bạn. Thường thì đó là những người trong đội phát triển ứng dụng hoặc khách hàng. Số lượng tối đa cho internal tester là 25. Với mỗi lần bạn submit bản build lên App store connect hoàn thành, tất cả các internal tester sẽ nhận được thông báo về phiên bản mới nhất.
    • External tester: Đây là những người dùng không nằm trọng đội phát triển ứng dụng nhưng họn muốn trải nghiệm, kiểm thử ứng dụng của bạn. Các extenal tester không có quyền truy cập vào App store connect. Nhưng họ vẫn có thể tải và cài đặt phiên bản ứng dụng mà bạn muốn họ test. Hiện nay Apple đang giới hạn tối đa external tester là 10,000. Để external tester có thể cài đặt và kiểm thử ứng dụng của bạn thì bạn cần Submit bản build mà bạn muốn cho bên apple review gần giống như việc submit bản build lên app store. Khi bản build đó được approve thì tất cả các External tester sẽ nhận được thông báo và có thể tải và cài đặt ứng dụng dựa trên bản build đó.

    Internal testers

    Để thêm một internal tester bạn cần truy cập vào App store connect đăng nhập và truy cập vào mục Users and Assess

    Bấm vào dấu + để thêm mới một user:

    Điền đầy đủ thông tin và bấm invite để gửi email mời họ.

    Lưu ý: Mục email là tài khoản apple(apple id) vì vậy những người muốn vào nhóm internal tester phải có tài khoản apple. Khi bạn add user này vào đồng nghĩa họ cũng sẽ có quyền truy cập vào App store connect để quản lý ứng dụng của bạn. Vì vậy hãy chú ý mục phân quyền(Role) cho user mà bạn muốn add.

    App store connect sẽ gửi một thư mời tới user mà bạn vừa thêm. User này cần truy cập vào hòm thư của họ để mở thư mời và chọn Active your account.

    Khi đó bạn sẽ được điều hướng sang trang của apple và bạn đăng nhập vào để active tài khoản của bạn. Hoàn thành bước này là bạn đã add tài khoản đó vào App store connect.

    Nhưng để các tài khoản này có thể tải và cài đặt những bản build trên Testflight các bạn cần mời họ vào nhóm App store connect users(Internal tester). Vì vậy chúng ta sẽ chuyển qua mục My app

    Một danh sách các ứng dụng của bạn được hiện ra. Hãy chọn ứng dụng mà bạn đang muốn quản lý. Chuyển sang tab Testflight > App store connect user > bấm dấu + để thêm user vào App store connect user.

    Lúc này một danh sách các user hiện ra. Bạn hãy chọn User lúc trước bạn đã invite ở bước trên và bấm Add để Testflight gửi thư mời cho tài khoản đó.

    Thư mời sẽ nằm trọng hòm thư của người dùng mà bạn vừa gửi. Bấm vào View In Testflight và hoàn thành các bước đơn giản để hoàn tất việc add Internal tester. Thư mời có định dạng như sau:

    Vậy là hoàn tất việc add Internal tester. Từ giờ mỗi khi có bản build mới được upload lên TestFlight là người dùng này sẽ nhận được cả mail và thông báo.

    External testers

    Như đã giới thiệu ở trên, external tester là những user không thể truy cập vào App store connect của bạn nên chúng ta cần cung cấp thông tin bản kiểm thử cũng như cần chọn bản build cho apple review.

    Cập nhật thông tin kiểm thử

    Bạn mở mục Test information và điền đầy đủ các thông tin cho bản kiểm thử này. Nó là bắt buộc nên bạn cần phải điền đầy đủ thông tin cho nó. Nhớ bấm Save để lưu lại thông tin nhé.

    Hãy điền đầy đủ thông tin cho mục Test information

    Tạo nhóm External tester

    Chọn mục Add Group để thêm mới nhóm External testers:

    Đặt tên nhóm > Bấm Create để tạo nhóm tester mới.

    Khi tạo thành công sẽ hiển thị một giao diện như dưới

    Do nhóm vừa tạo nên chưa có tester nào. Để thêm tester chúng ta bấm vào dấu +

    • Add new testers: Đây là cách thêm thủ công, sử dụng khi ban muốn thêm số lượng ít tester
    • Add existing testers: Thêm các tester đã từng test ứng dụng rồi
    • Import from CSV: Sử dụng file CSV để thêm tester, sử dụng khi bạn muốn thêm số lượng lớn các tester

    Ở bài này mình sẽ chỉ nói đến Add New Testers. Bạn điền thông tin của tester và bấm add để invite họ vào group.

    Lúc này 1 thư mời sẽ được gửi tới hòm thư của email đó họ chỉ cần accept là được.

    Chọn bản build dành cho external tester

    Chuyển sang mục Builds và bấm vào dấu + để chọn bản build bạn muốn external tester kiểm thử.

    Một danh sách các bản build được hiển thị, chọn bản mà bạn muốn test. Và bấm Add

    Lúc này bạn đã Add thành công, giờ chờ để apple review.

    Lúc này trạng thái của bản build đang là waiting for review có nghĩa là ứng dụng của bạn đang trong hàng đợi để nhân viên của apple review. Để được approve bản build của bạn phải không vi phạm điều luật apple đã đưa ra trong App Store Review Guideline. Việc review sẽ tốn một khoảng thời gian lớn tùy thuộc vào ứng dụng của bạn và thời gian bạn submit. Tuy nhiên một khi Apple đã approve cho version của bạn thì những bản build tiếp theo sẽ không phải review cho đến khi bạn thay đổi version.

    Sau khi apple họ Approve bản build của bạn, một thông báo sẽ được gửi cho tất cả các tester trong nhóm. Để họ có thể tài và cài ứng dụng lên thiết bị của họ.

    Vậy là chúng ta đã hoàn thành việc tạo nhóm external tester và thêm bản build cho nhóm external tester.

    Tester cần làm gì?

    Tải ứng dụng TestFlight trên App store

    Khi có bản mới thông báo sẽ báo về mail các bạn chỉ cần bấm vào để sử dụng. Khi đó bạn sẽ được chuyển qua ứng dụng TestFlight và có thể Tải cũng như cài đặt ứng dụng.

    Tổng kết

    Bài viết này mình đã chia sẻ với các bạn về cách thêm internal tester, external tester để hỗ trợ việc tester kiểm thử ứng dụng. Hi vọng nó giúp các bạn phần nào bớt bỡ ngỡ khi tiếp cận TestFilght.

    Ngoài ra còn một số cách đẩy bản build cho tester kiểm thử khác không phải của apple cũng khá được ưa chuộng.
    Các bạn có thể tham khảo bài viết: https://magz.techover.io/2019/12/17/cach-upload-1-app-ios-len-deploygate/

  • [React] Kết hợp các mẫu lập trình để sử dụng hiệu quả Runtime Type Checking

    [React] Kết hợp các mẫu lập trình để sử dụng hiệu quả Runtime Type Checking

    Như các bạn đã biết, Static Type Checking giúp chúng ta kiểm soát kiểu dữ liệu tốt hơn khi viết mã nguồn. Các bạn có thể sử dụng Flow hoặc TypeScript đều được. Tuy nhiên khi dữ liệu được liên kết với API chẳng hạn, khi đó bạn không thể kiểm soát được giá trị được truyền vào cho biến nào đó. Do đó chúng ta vẫn cần thêm một bước nữa để hạn chế các lỗi tiềm ẩn bằng Runtime Type Checking. Trong bài viết này tôi sẽ hướng dẫn các bạn sử dụng Runtime Type Checking như thế nào cho hiệu quả.

    Runtime Type Checking là gì?

    Runtime Type Checking là quá trình xác minh lại kiểu dữ liệu của giá trị truyền vào cho biến có đúng với kiểu dữ liệu mong muốn hay không. Trong ứng dụng React khi bạn khai báo propTypes cho component nào đó chính là lúc bạn đang định nghĩa kiểu dữ liệu mong muốn. Tại thời điểm thực thi các định nghĩa này sẽ được kiểm tra giúp bạn kiểm soát việc binding dữ liệu từ API vào component có tương thích với nhau hay không.

    Định nghĩa propTypes

    React cung cấp gói prop-types giúp bạn định nghĩa các thuộc tính cần cho Runtime Type Checking. Trước đây thì gói này nằm trong React, hiện tại thì nó đã tách ra thành một gói riêng rồi. Các bạn khai báo propTypes như sau:

    import React from 'react';
    import PropTypes from 'prop-types';
    
    export function Octocat(props) {
      const { login, avatar_url, name } = props;
      return (
        <ul>
          <li>{login}</li>
          <li>{name}</li>
          <li>
            <img src={avatar_url} alt={login} />
          </li>
        </ul>
      );
    }
    
    Octocat.propTypes = {
      login: PropTypes.string,
      avatar_url: PropTypes.string,
      name: PropTypes.string
    };
    

    Trong quá trình thực thi nếu dữ liệu được truyền vào component không đúng với định nghĩa propTypes bạn sẽ nhận được một thông báo tương tự như sau:

    prop-types

    Kết hợp với Spread Attributes

    Bạn có để ý rằng khi chúng ta khai báo giá trị truyền vào trong props thì khi sử dụng component này bạn phải khai báo như sau:

    <Octocat login="abc" avatar_url="def" name="xyz" />
    

    Nếu số lượng thuộc tính trong component này tăng lên thì việc viết mã nguồn sẽ khá là vất vả. Rất may là trong JSX cung cấp cho bạn tính năng Spead Attributes và bạn có thể viết code như sau:

    const data = {login: "abc" avatar_url: "def" name: "xyz"};
    <Octocat {...data} />
    

    Toán tử ... được gọi là toán tử spead.

    Kết hợp với Destructuring Props

    Các bạn có thấy rằng khi khai báo Octocat component tôi đã truyền props như làm tham số của một hàm và sau đó mới gán lại vào các biến. Với Destructuring Props bạn có thể viết mã nguồn đơn giản hơn như sau:

    import React from 'react';
    import PropTypes from 'prop-types';
    
    export function Octocat({ login, avatar_url, name }) {
      return (
        <ul>
          <li>{login}</li>
          <li>{name}</li>
          <li>
            <img src={avatar_url} alt={login} />
          </li>
        </ul>
      );
    }
    
    Octocat.propTypes = {
      login: PropTypes.string,
      avatar_url: PropTypes.string,
      name: PropTypes.string
    };
    

    Tài liệu tham khảo:

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết sẽ giúp các bạn viết mã nguồn tốt hơn.

  • [AWS] Sử dụng API Gateway Lambda Authorizers với JWT như thế nào?

    [AWS] Sử dụng API Gateway Lambda Authorizers với JWT như thế nào?

    Một trọng những vấn đề quan trọng trong các dự án đó là điều khiển quyền truy cập. Với các ứng dụng xây dựng trên nền tảng AWS việc điều khiển truy cập cũng phức tạp hơn. Trong bài viết này tôi sẽ hướng dẫn các bạn cách tôi đã làm để điểu khiển truy cập với các API sử dụng API Gateway Lambda Authorizers.

    Luồng xác thực Lambda Authorizer

    Luồng xác thực của Lambda Authorizer được minh hoạ trong hình sau:

    auhorizer

    Các bược xác thực như sau:

    1. Máy khách gửi yêu cầu lên API Gateway API có kèm theo Bearer Token.
    2. API Gateway kiểm tra cấu hình authorizer đã được cấu hình tương ứng với hàm Lambda. Nếu nó tồn tại thì Lambda Authoirizer sẽ được gọi.
    3. Lambda Authorizer sẽ thực hiện xác thực bằng Bearer Token đã được gửi lên.
    4. Nếu việc gọi Lambda Authrorizer thực hiện thành công, hàm Lambda sẽ trả về thông tin chứa chính sách IAM và thông tin người dùng.
    5. API Gateway sử dụng thông tin trả về từ Lambda Authorizer để kiểm tra quyền truy cập:
    • Trường hợp nhận được thông tin từ chối truy cập thì API Gateway sẽ trả về mã 403 và từ chối truy cập tới API từ máy khách.
    • Trường hợp nhận được thôn tin cho phép truy cập thì phương thức sẽ được thực thi.

    Định nghĩa Lambda Authorizer

    • Khai báo authorizer trong serverless.yml:
    functions:
      authorizer:
        handler: src.api.authorizer.lambda_handler
        cors: true
    
    • Định nghĩa hàm Lambda Authorizer:
    import jwt
    
    
    def lambda_handler(event, context):
        try:
            token = event.get("authorizationToken").split(" ")[1] # lấy thông tin token trong Authorization header
            claims = jwt.decode(token, "secret", algorithms=["HS256"]) # decode xem token có hợp lệ không
            return {
                "principalId": claims["uid"], # lấy thông tin user đề gán vào IAM
                "policyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Action": "execute-api:Invoke",
                            "Effect": "Allow", # cho phép nếu token hợp lệ
                            "Resource": event["methodArn"],
                        }
                    ],
                },
            }
        except:
            return {
                "principalId": "denied",
                "policyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Action": "execute-api:Invoke",
                            "Effect": "Deny", # từ chối nếu token không hợp lệ
                            "Resource": event["methodArn"],
                        }
                    ],
                },
            }
    

    Định nghĩa hàm Lambda cần điều khiển quyề truy cập

    • Khai báo hàm Lambda trong serverless.yml
    functions:
      test:
        handler: src.api.test.lambda_handler
        events:
          - http:
              method: get
              path: api/test
              cors: true
              authorizer: authorizer # khai báo Lambda Authorizer
    
    • Định nghĩa hàm Lambda:
    import json
    
    
    def lambda_handler(event, context):
        headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
        body = {"status": "success"}
        return {
            "statusCode": 200,
            "headers": headers,
            "body": json.dumps(body),
        }
    

    Test thử Lambda với Authorizer

    • Trường hợp không truyền token trên Authorizer Header, API Gateway sẽ trả về 403

    No Auth

    • Trường hợp có truyền token trên Authorization Header, API Gateway sẽ cho phép phương thức được thực thi

    Token được tạo như sau

    (zpn) hieunv@HieuNV lambda % python
    Python 3.7.7 (default, Mar 10 2020, 15:43:33)
    [Clang 11.0.0 (clang-1100.0.33.17)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import jwt
    >>> jwt.encode({'uid': 'hieunv'}, "secret", algorithm='HS256')
    b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJoaWV1bnYifQ.xuZSlS_3lw6NvGvw_fQ2qXGBWiv2HpXTFtYtO85lQac'
    

    Truyền token lên Authorizer Header

    Bearer Auth

    Tài liệu tham khảo:

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết có thể giúp các bạn cài đặt việc điều khiển truy cập dễ dàng hơn với các ứng dụng trên nền tảng AWS.

  • iOS/Swift: Custom UIProgressView

    iOS/Swift: Custom UIProgressView

    Lời mở đầu

    Có thể các bạn đã biết progress view của Apple nó khá là đơn giản và ýt thuộc tính để chúng ta có thể sử dụng cũng như thay đổi để phù hợp với thiết kế. Trong dự án gần đây mình đã có cơ hội để động vào nó. Trong thời gian đầu mình tự mày mò thì progress bar không hỗ trợ việc như vậy. Vì vậy mình viết bài này để chia sẻ về cách mình đã custom lại progress này. Hi vọng nó giúp các bạn gặp khó khăn với việc custom lại progress bar.

    Vấn đề cần giải quyết: Làm thế nào để bo tròn góc của progress bar

    Đây là progress view mình cần đạt được:

    Đây là progress bar mặc đinh của apple:

    Phân tích

    Khi thử đọc tài liệu về UIProgressView. Không có thuộc tính nào giúp mình có thể đạt được điều mình muốn.

    Để bo tròn cả progressview thì ta có thể dùng layer.cornerRadius, nhưng để bo tròn thằng progress chạy bên trong thì sao? Chúng ta cần tìm được cái view đó và bo nó lại.

    Giải quyết bài toán

    Bước 1: Chúng ta cần tạo constraint height cho progressView

    Chúng ta có 2 cách để tăng height cho ProgressView:
    1. Sử dụng Transform Scale: Nó sẽ làm cornerRadius chạy không đúng
    2. Sử dụng constraint height: Nên chúng ta chon cách này

    Tuy rằng chúng ta đã constraint height của nó bằng 16 nhưng thực tế height của progressView vẫn = 2. Vì vậy khi set cornerRadius chúng ta không thể sử dụng frame.height/2 được mặc dù đó là cách tốt nhất đối với các view khác.

    Bước 2: Bo tròn góc cho trackView

    progressBar.layer.cornerRadius = 8.0
    progressBar.clipsToBounds = true

    Bước 3: Bo tròn progressView

            if let sublayers = progressBar.layer.sublayers, sublayers.count > 1 {
                sublayers[1].cornerRadius = 8.0
            }
            progressBar.subviews[1].clipsToBounds = true

    Nếu để ý kĩ các bạn sẽ thấy progress bar này gồm 2 view chồng lên nhau đó là Progress View và track view. Và theo thứ tự trong lập trình thì thằng đầu tiên là thằng nằm dưới có index = 0. Chúng ta nhìn thấy progress view vì nó nằm trên Track View -> nó có index = 1

    Lưu ý: Nó chỉ đúng khi chúng ta không thêm subview, sublayer cho ProgressView. Vậy nên trong trường hợp này chúng ta sẽ ổn.

    If let giúp chúng ta handle trường hợp crash app, khi các layer của progress bị remove. (nó thường k xảy ra, nhưng vì an toàn chúng ta nên thêm dòng này)

    sublayers[1]: là layer của track view
    subviews[1]: là trackView

    Kết quả thu được thật mỹ mãn 😀

    Ngoài ra các bạn có thể sử dụng image cho progress view cũng như trackView để có 1 progress đẹp hơn.

    progressBar.progressImage = UIImage(named: "2697")

    Các bạn có thể tải về file assets ở dưới:

    Tổng kêt

    Mình hi vọng bài viết dưới đây giúp các bạn hiểu rõ hơn về UIProgressView và dễ dàng hơn khi làm việc với nó.

  • [React] Static Type Checking là gì? Flow và TypeScript, bạn chọn cách nào?

    [React] Static Type Checking là gì? Flow và TypeScript, bạn chọn cách nào?

    Static Type Checking và Runtime Type Checking là hai thuật ngữ ban đầu cảm giác có vẻ không quen thuộc cho nhưng nó lại lai thứ có lẽ các bạn đang dùng hàng ngày. Như các bạn biết thì JavaScript là ngôn ngữ định kiểu động, nghĩa là kiểu dữ liệu chỉ được xác định tại thời điểm thực thi. Do đó khi lập trình chúng ta không cần xác định kiểu dữ liệu cho biến. Điều này vô hình chung làm cho mã nguồn của chúng ta trở nên khó đọc hơn và khó debug hơn nữa. Chính vì vậy việc xác định kiểu dữ liệu giúp cho chúng ta kiểu soát và debug dễ dàng hơn. Trong bài viết này tôi sẽ chia sẻ với các bạn cách công đồng đang làm để hỗ trợ hai kiểu kiểm tra kiểu dữ liệu này.

    Static Type Checking là gì?

    Hiện nay có hai xu hướng để kiểm tra kiểu dữ liệu tĩnh là FlowTypeScript. Static Type Checking sẽ phân tích mã nguồn tĩnh trước khi biên dịch mã nguồn sang mã JavsScript(ES5) là mã nguồn mà trình duyệt có thể đọc được. Vậy thì chúng khác nhau ở điểm nào?

    Flow

    Flow là một phần mở rộng của Babel cung cấp cho bạn việc kiểm tra kiểu dữ liệu tĩnh bằng cách sử dụng các chỉ thị bằng comment mà bạn thêm vào mã nguồn.

    Ví dụ cách sử dụng flow như bên dưới:

    // @flow
    function square(n: number): number {
      return n * n;
    }
    
    square('2'); // Error!
    

    TypeScript

    TypeScript là ngôn ngữ dựa trên JavaScript được Microsoft phát triển. TypeScript khác với Flow là nó không phân tích mã nguồn dự trên các chỉ thị bằng comment. Kiểu dữ liệu được hỗ trợ từ trong ngôn ngữ luôn. Dữ liệu được kiểm tra tại thời điểm phân tích cú pháp của chương trình dịch. Ngoài ra thì TypeScript hỗ trợ rất mạnh OOP nên nó rất phù hợp với các bạn quen thuộc với các ngôn ngữ định kiểu như Java và C#.

    const add = (x: number, y: number) => {
      return x + y;
    };
    

    Sử dụng Static Type Checking có lợi ích gì?

    • Khi sử dụng Static Type Checking bạn được hỗ trợ việc kiểm soát dữ liệu tại thời điểm viết mã nguồn, tất cả việc gán hoặc gọi hàm với kiểu dữ liệu không phù hợp sẽ được phát hiện tại thời điểm này.

    • IDE hỗ trợ việc kiểm soát kiểu dữ liệu giúp bạn viết code nhanh hơn và sai sốt ít hơn.

    • Mã nguồn dễ đọc hơn cho người mới.

    Bạn mất gì khi sử dụng Static Type Checking?

    Static Type Checking mang những lợi ích nhất định trong việc kiểm soát kiểu dữ liệu, tuy nhiên nó cũng có những nhiểm điểm:

    • Bạn phải viết code nhiều hơn thì mới kiểm soát được kiểu dữ liệu, việc này đương nhiên sẽ tốn công sức. Tuy nhiên so với việc mất thời gian vào debug thì có lẽ nó vẫn tốt hơn.

    • Bạn vẫn không kiểm soát được kiểu dữ liệu tại thời điểm chạy chương trình, lý do là nếu chương trình của bạn có liên kết với API thì không có gì đảm bảo kiểu dữ liệu của bạn chạy đúng với backend cả. Đương nhiên cái này là vấn đề không thể giải quyết được rồi. Tôi sẽ hướng dẫn các bạn giải quyết vấn đề này trong bài viết Runtime Type Checking sau nhé.

    Kết luận

    Đây chỉ là kết luận mang tính cá nhân của mình :). Các bạn tự suy ngẫm để đưa ra lựa chọn phù hợp nhé.

    • Việc viết theo Flow có cảm giác không tự nhiên lắm, thường xuyên phải thêm comment khiến bạn khá mất thời gian. Thêm một vấn đề nữa là lúc viết code sử dụng Flow khá tốn tài nguyên của máy. Về vấn đề này thì mình thấy TypeScript có vẻ ổn hơn.

    • TypeScript không đơn giản là một sự mở rộng của JavaScript, Microsoft đã thêm vào nhiều thứ hơn thế và nó cũng không dựa trên các tiêu chuẩn của ES6, ES7, … (có support ES6, ES7, … nhưng có những thêm vào nhiều thứ khác nữa). Về điểm này thì Flow có vẻ tốt hơn.

    • Sau sự ra đời của React Hooks thì mọi thế mạnh của TypeScript gần như không còn nữa so với Flow, bản thân mình cũng không dùng TypeScript nữa.

    • Một điểm quan trong nữa là cả TypeScript và Flow đều không thể xác mình kiểu dữ liệu lúc thực thi nên bạn vẫn cần Runtime Type Checking.

    Các bạn có dùng Static Type Checking không? Mình thì đã không dùng nữa và chuyển sang xác mình toàn bộ bằng Runtime Type Checking rồi. Thời gian tốn cho debug cũng không phát sinh nhiều so với việc phải viết mã nguồn xác định kiểu dữ liệu.

    Cảm ơn các bạn đã theo dõi bài viết. Các bạn cùng chờ bài viết mình hướng dẫn về Runtime Type Checking nhé.

  • iOS/App store connect: Các bước đẩy ứng dụng của bạn lên App store

    iOS/App store connect: Các bước đẩy ứng dụng của bạn lên App store

    Lời mở đầu

    Bài này mình sẽ chia sẻ với các bạn làm thế nào để đẩy được ứng dụng của bạn lên App store.

    Nếu các bạn chưa biết tạo certificate để chuẩn bị cho việc đẩy ứng dụng lên store thì hãy đọc bài viết này của mình trước nhé!

    iOS: Hướng dẫn tạo certificate, App ID, Provisioning Profile để tải ứng dụng lên App store

    Tạo ứng dụng trên App store connect

    Bước 1: Đăng nhập vào App store connect
    App store connect là trang quản lý ứng dụng của bạn trên store của apple. Để truy cập vào trang này chúng ta đăng nhập tài khoản Apple ID vào App store connect
    Đăng nhập thành công chúng ta sẽ có giao diện như hình dưới:

    Bước 2: Chọn My Apps để sang màn hình quản lý ứng dụng của bạn.

    Bước 3: Chọn + sau đó chọn New App

    Bước 4: Điền đầy đủ thông tin liên quan đến ứng dụng của bạn.

    • Platforms: Ứng dụng bạn tạo chạy trên nền tảng nào?
    • Name: Đây là tên ứng dụng của bạn
    • Primary language: Ngôn ngữ mặc định mà người dùng nhìn thấy là gì? Nếu ứng dụng của các bạn chỉ phát hành 1 ngôn ngữ cho 1 quốc gia thì bạn chọn ngôn ngữ mà Ứng dụng đang sử dụng. Nếu ứng dụng của bạn phát hành trên toàn thế giới, mình nghĩ bạn nên chọn là Tiếng Anh vì khi ngôn ngữ máy của người dùng không nằm trong danh sách ngôn ngữ các bạn hỗ trợ nó sẽ hiển thị Tiếng Anh.
    • Bundle ID: Đây là bundle ID của ứng dụng của bạn. Nó phải khớp với Bundle ID bạn sử dụng trong XCode.
    • SKU: Viết tắt của Stock-Keeping Unit nó giúp apple quản lý kho ứng dụng của họ khi bạn upload ứng dụng của bạn lên store. Nó phải là duy nhất nên mình hay dùng trùng với Bundle Id.
    • User Access: Quyền truy cập vào quản lý ứng dụng này

    Điền đầy đủ thông tin và bấm vào nút Create để tạo mới ứng dụng.

    Bước 4: Chọn loại ứng dụng
    Bạn hãy chọn category đúng với ứng dụng của bạn

    Cập nhật thông tin ứng dụng cho phiên bản mới

    Bước 1: Chọn tab iOS App > 1.x Prepare for Submission > Cập nhật thông tin phiên bản mới (nếu đây không phải là bản đầu tiên)

    Bước 2: Cập nhật bộ ảnh giới thiệu ứng dụng của bạn.
    Ảnh phải có định dạng là JPG hoặc PNG. và sử dụng dạng màu RGB. Video Preview chỉ chấp nhận định dạng M4v, MP4 hoặc MOV và không vượt quá 500MB

    Apple yêu cầu bạn phải có ýt nhất 5 ảnh giới thiệu về ứng dụng của bạn. Nếu ứng dụng của bạn chỉ hỗ trợ Iphone thì bạn cần chuẩn bị 5 hình cho Iphone 6.5 Display và 5 hình cho Iphone 5.5 Display. Nếu có hỗ trợ Ipad thì cần thêm 5 hình cho Ipad 12.9 Display
    Cụ thể kích thước các bạn xem ở link này.

    Bước 3: Nhập thông tin ứng dụng của bạn

    • Promotional text: là đoạn text quảng cáo cho phép bạn thông báo tới người dùng truy cập App store của mình về bất cứ tính năng ứng dụng nào mà không yêu cầu gửi cập nhật. Nó sẽ xuất hiện phía trên mô ta của bạn trên appstore và chỉ dành cho khách hàng sử dụng iOS 11 trở lên và macOS 10.13 trở lên.
    • Keywords: Là chuuỗi những từ khóa mà bạn muốn người dùng tìm thấy ứng dụng của bạn.
    • Description: Mô tả về ứng dụng của bạn.
    • Support URL: Link hỗ trợ

    Bước 4: Cập nhật thông tin chung của ứng dụng

    • App store icon: Đây là icon ứng dụng của bạn yêu cầu kích thước 1024×1024
    • Copyright: Thường là tên cty
    • sign-in required: Nếu ứng dụng của bạn yêu cầu Login mới sử dụng được hãy tích vào và điền thông tin USername password để nhân viên apple review.

    Bước 5: Chọn dạng release

    Bạn có thể chọn tự động release ứng dụng khi nhân viên của Apple chấp thuận ứng dụng của bạn. Hoặc chọn ngày để release

    Bước 6: Bấm save – Bước quan trọng nhất =))

    Bước 7: Chọn file build cho phiên bản này để nhân viên của apple review.

    Bâm dấu + để chọn bản build mà bạn muốn apple review

    Bản build mà bạn chọn cho apple review chính là bản được xuât hiện trên store.

    Nếu bạn chưa up bản nào lên thì hãy đọc tiếp bước dưới đây.

    Cách tạo file build trên XCode

    Chuẩn bị trước khi Archive:

    Bạn cần tăng version của app nếu đây là bản release tiếp theo của bản trước đó:
    Ví dụ: Nếu bản trên store đang là 1.0 thì version của bản này phải > 1.0

    Bạn cần tăng bản build của version app: Với mỗi 1 version apple yêu cầu các build version mới phải > build version cũ. Ví dụ: Nếu Bản testflight của bạn đang là 1.0 (1) thì bạn cần tăng build cho nó lớn hơn(1)

    Nếu không để ý các bước chuẩn bị, khi upload lên xcode sẽ thông báo lỗi và lúc đó các bạn sẽ phải archive lại từ đầu. Sẽ mất rất nhiều thời gian nên hãy cẩn thận ở bước này.

    Để thay đổi version và build version của app bạn chọn App Target > General

    Để Archive ứng dụng bạn chọn Product > Archive
    Lưu ý: Archive bị disable khi sử dụng device là similator(máy ảo) vì vậy bạn cần chọn máy thật hoặc chọn Generic iOS Device

    Khi Archive thành công sẽ hiển thị popup quản lý file Archive. Nếu bạn lỡ tay tắt pop up thì có thể mở lại bằng cách Chọn Window > Organizer

    Hãy chọn file bạn vừa archive xong. Bấm vào nút Distribute App

    Chọn App Store connect và bấm Next

    Chọn Upload > Next

    Tiếp tục bấm Next

    Ở đây bạn chọn Automatically manage signing để xcode tự động tạo hoặc update certificate cho bạn. > Next
    Hoặc bạn cũng có thế chọn bằng tay

    Màn hình review file ipa của bạn được hiển thị ra: Lúc này bấm > Upload

    Chờ đợi apple verify file ipa của bạn. Khi này có thể xảy ra 2 trường hợp

    • Apple trả về lỗi: Copy lỗi paste lên google là có ng trả lời giúp nhé
    • Apple trả về thành công: Bạn đã đẩy được file lên testflight và phải chờ khoảng 15 phút để file ipa đó có thể hoạt động được. Khi nào file ipa sẵn sàng sẽ có thông báo tới các tài khoản test được đăng ký ở Testflight.

    Lúc này bạn quay lại trang App Store connect để kiểm tra trạng thái.

    Lúc này hãy đi pha 1 tách trà và chờ đợi. Uống xong trà là nó sẽ xong ấy mà :)). Bao giờ nó chuyển sang Ready to test thì bạn có thể sử dụng để đẩy lên store.

    Tuy nhiên để đảm bảo an toàn cho từng bản build, với mỗi bản build này bạn cần xây dụng một file checklist để kiểm tra 1 loạt các tính năng chính của ứng dụng trước khi Submit to review

    Tiếp theo bạn quay lại bước 7 ở trên. Chọn file vừa mới upload lên.

    Vậy là hoàn tất các bước chuẩn bị. Giờ chúng ta bấm Submit for Review

    Vậy là các bạn đã hoàn tất việc đẩy ứng dụng lên store.

    Sau 1 khoảng thời gian từ 1 -> x ngày để apple review ứng dụng của ban. Nếu cuối tuần nó sẽ lâu hơn vì cuối tuần là ngày nghỉ nhân viên nó k làm việc
    Lúc này sẽ có 2 trường hợp xảy ra

    • Apple reject bản build của bạn: Vì có thể bạn vi phạm chính sách nghiêm ngặt mà apple đã đặt ra. Hãy đọc bài App Store Review Guideline để không bị reject.
    • Apple Approve bản build của bạn: Xin chúc mừng bạn đã đẩy lên store thành công.

    Tổng kết

    Vậy là mình đã hướng dẫn các bạn upload thành công 1 ứng dụng lên store. Chúc các bạn thành công!