Category: Apple

  • Triển khai CI/CD cho iOS – SonarQube & Blackduck

    Overview

    Tiếp tục với series CI/CD cho iOS, hôm nay chúng ta sẽ triển khai CI với hai nền tảng kiểm tra source code rất nổi tiếng là SonarQube và Blackduck.

    Triển khai CI với Blackduck

    Khác với SonarQube, Blackduck không đánh giá chất lượng mà giúp chúng ta quản trị open source code và source code từ các thư viện được thêm vào. Giúp chúng ta đánh giá và quản lý được các rủi ro về bản quyền, bảo mật khi sử dụng source code có sẵn trên mạng cũng như lưu hành trong cộng đồng.

    Do Blackduck không có Server public như Sonar, nên mình sẽ giả định chúng ta có một Server Blackduck được đặt tại địa chỉ sau:

    https://blackduck.techover.io

    Sau khi truy cập vào, chúng ta sẽ thấy danh sách các project đã có ở Dashboard. Ở đây mình đã tạo sẵn một Project W95.CICD, nếu muốn tạo mới chúng ta sẽ ấn nút Create Project ở góc trên bên phải.

    Dashboard của Blackduck

    Cũng giống như Sonar, để có thể đồng bộ các dữ liệu Scan chúng ta cần có một Token. Để tạo Blackduck Token, chúng ta sẽ vào màn hình quản lý Access Tokens như sau:

    Ấn vào Profile ở góc trên bên phải, chọn My Access Tokens

    Sau khi chuyển đến màn hình quản lý Access Token, chúng ta ấn Create New Access Token và điền thông tin.

    Màn hình quản lý Access Tokens
    Nhập thông tin và ấn Create

    Sau khi có token, chúng ta sẽ lưu lại để sử dụng sau này. Lưu ý, token sẽ không hiển thị lại lần thứ 2 nên hãy copy và lưu lại ngay và luôn nhé. Ví dụ mình sẽ lưu lại token lại dưới đây:

    YWJmYzc5MDMS05N2VjLWFkNGE4ZMS05N2VjLWFkNGE4Z--=/

    Đối với Blackduck, chúng ta cũng sử dụng gần giống như Sonar nhưng thay vì CLI, chúng ta sẽ sử dụng Java Archive, vì vậy hãy đảm bảo bạn đã cài Java trong thiết bị Runner nhé.

    Tải về phiên bản .jar mới nhất của blackduck ở đây

    Sau khi tải xong, chúng ta sẽ giải nén và lưu và một thư mục trong thiết bị Runner, ở đây mình sẽ lưu ở địa chỉ sau:

    /Users/jena/CICD/synopsys-detect/synopsys-detect-6.4.1.jar

    Rồi xong, tới công chuyện luôn!!

    Tiếp đó chúng ta chỉ cần cấu hình file .gitlab-ci.yml như sau:

    stages:
      - Lint
    blackduck-detect:
      stage: Lint
      only:
          - cicd
      script:
        - java -jar /Users/jena/CICD/synopsys-detect/synopsys-detect-6.4.1.jar \
          --blackduck.url=https://blackduck.techover.io \
          --blackduck.api.token=YWJmYzc5MDMS05N2VjLWFkNGE4ZMS05N2VjLWFkNGE4Z--=/ \
          --detect.project.name=W95.CICD
      tags:
        - w95

    Vậy là xong, mỗi khi có commit/merge lên nhánh cicd (hãy thay bằng master/main/develop) thì hệ thống sẽ tự động scan source code và gửi kết quả lên Server.

    Quên mất, còn một bước cuối cùng nữa là bạn có thể che đi các thông tin nhạy cảm trong file cấu hình .gitlab-ci.yml, đề phòng trong trường hợp file cấu hình bị rò rỉ, các thông tin về Server Blackduck cũng như Token cũng sẽ không bị ảnh hưởng. Để làm việc này chúng ta sẽ cấu hình một số thông tin sau vào biến môi trường của Gitlab-CI

    BLACKDUCK_SERVER = https://blackduck.techover.io
    BLACKDUCK_TOKEN = YWJmYzc5MDMS05N2VjLWFkNGE4ZMS05N2VjLWFkNGE4Z--=/

    Và kết quả, file .gitlab-ci.yml sẽ trông như sau:

    stages:
      - Lint
    blackduck-detect:
      stage: Lint
      only:
          - cicd
      script:
        - java -jar /Users/jena/CICD/synopsys-detect/synopsys-detect-6.4.1.jar \
          --blackduck.url=${BLACKDUCK_SERVER} \
          --blackduck.api.token=${BLACKDUCK_TOKEN} \
          --detect.project.name=W95.CICD
      tags:
        - w95

    Rồi xong, tới công chuyện luôn!! Như vậy là chúng ta đã hoàn thành cấu hình CI với Blackduck để scan các lỗi hổng bảo mật, bản quyền. Mặc dù liên quan đến CI còn rất nhiều section như Coverity, Build & Compile nhưng mình xin phép tạm dừng hạng mục CI và chuyển sang CD. Rất mong được các bạn ủng hộ.

  • Triển khai CI/CD cho iOS – SonarQube & Blackduck

    Triển khai CI/CD cho iOS – SonarQube & Blackduck

    Overview

    Tiếp tục với series CI/CD cho iOS, hôm nay chúng ta sẽ triển khai CI với hai nền tảng kiểm tra source code rất nổi tiếng là SonarQube và Blackduck.

    Triển khai CI với SonarQube

    Đôi chút về SonarQube, đây là một nền tảng mã nguồn mở sử dụng để kiểm tra chất lượng của source code, đánh giá các lỗi ở nhiều mức độ và tiêu chí khác nhau. Mục đích cuối cùng là để thống kê và cải thiện chất lượng của source code theo mọi mặt cũng như giúp lập trình viên đánh giá chất lượng của chính mình. Vì vậy, việc sử dụng SonarQube để hỗ trợ quá trình phát triển, nâng cao chất lượng source code luôn được các doanh nghiệp lớn áp dụng.

    Chúng ta sẽ thực hiện CI với SonarQube server ở địa chỉ sau: https://sonarcloud.io, bạn có thể sử dụng tài khoản GitLab của mình để đăng nhập và tạo project ở đây. Sau một vài bước đăng nhập, tạo organization và chọn plan thì chúng ta sẽ đến với step đầu tiên.

    Đối với một số trường hợp có Server riêng để host SonarQube, các bạn hãy truy cập host và sử dụng tài khoản/mật khẩu được cung cấp bởi admin.

    Cấu hình SonarQube

    Việc đầu tiên, chúng ta sẽ cần tạo một project, ở đây mình sẽ tạo một project như hình sau:

    Điền Project Key và Display name sau đó ấn Set Up

    Trong một số trường hợp, bước config tạo Project sẽ do admin tạo, các bạn chỉ cần đăng nhập là xem được các project mình được phân quyền.

    Sau khi ấn Set Up, chúng ta sẽ được suggest 3 lựa chọn, ở đây mình sẽ chọn Manually để có thể sử dụng ở nhiều nền tảng khác nhau (Không chỉ riêng GitLab-CI).

    Lựa chọn Manually

    Tiếp đó chúng ta sẽ chọn các lựa chọn như hình sau:

    Chọn Other(…) -> macOS

    Tiếp đó chúng ta ấn Download để tải CLI của SonarQube về, giải nén và lưu vào một thư mục trong thiết bị runner. Giả sử mình sẽ lưu ở địa chỉ sau:

    /Users/jena/Projects/sonar-scanner/bin

    Mình sẽ thêm thư mục bin vào trong PATH của macOS bằng câu lệnh sau

    export PATH=$PATH:/Users/jena/Projects/sonar-scanner/bin

    Sau đó, bạn có thể chạy lệnh sau để kiểm tra xem cli đã được nhận vào PATH chưa. Sẽ có một số lỗi yêu cầu cấp quyền để chạy CLI, bạn hãy vào System Preference -> Security & Privacy -> Tab General -> Allow Anyway tất cả

    sonar-scanner -v
    Sau khi cli được add vào PATH, bạn có thể chạy bằng lệnh sonar-scanner

    Tiếp đó, chúng ta sẽ vào GitLab và cài đặt biến môi trường SONAR_TOKEN như hình sau

    Tiếp đó, chúng ta sẽ cấu hình CI ở file .gitlab-ci.yml như sau, phần script sẽ được gen cùng với SONAR_TOKEN, các bạn chỉ cần copy và paster vào là được:

    stages:
      - Lint
    sonar-scanner:
      stage: Lint
      only:
          - cicd
      script:
        - sonar-scanner -Dsonar.organization=w95 -Dsonar.projectKey=CICD.iOS -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.branch=master
      tags:
        - w95

    Sau khi commit file .gitlab-ci.yml lên branch cicd, chúng ta sẽ có kết quả như sau:

    Job Sonar Scanner chạy thành công với log như trên
    Màn hình thống kê trên SonarCloud.io cũng sẽ hiển thị các thông số của source code

    Theo như ảnh trên, Quality Gate đang đánh giá Passed tức là source code đạt chất lượng, nhưng thật ra mình scan Starter Project của iOS nên mới không có lỗi, còn code của mình thì lắm lỗi lắm :p

    Như vậy là chúng ta đã hoàn thành bước cấu hình CI sử dụng SonarQube cho một project iOS. Mỗi khi có commit, merge hay sự thay đổi trên branch cicd (bạn sẽ đổi thành master/develop/main) thì hệ thống sẽ tự động chạy CI và đẩy thống kê lên Sonar server. Chúng ta chỉ cần lên đó, tracking các thông số và sửa các lỗi bị cảnh báo là được.

    Do bài viết hơi dài, nên mình sẽ để phần Blackduck sang bài viết sau. Cảm ơn các bạn đã đọc!

    Authors

    LinhNB1

  • Triển khai CI/CD cho iOS – SwiftLint

    Triển khai CI/CD cho iOS – SwiftLint

    Hưởng ứng theo tinh thần của Editor team, mình đóng góp Series này để hưởng ứng Technopedia, không nhằm mục đích dự thi. Mong rằng các kinh nghiệm của mình sẽ giúp ích được cho cộng đồng trong lĩnh vực liên quan.

    Sam

    Để triển khai CI/CD cho một sản phẩm iOS có rất nhiều lựa chọn, chúng ta có thể sử dụng GitLab-CI, Xcode Server, Fastlane, Jenkins, Microsoft App Center, Circle CI, …
    Ở phạm vi bài viết này, chúng ta sẽ đề cập đến một nền tảng được tích hợp với GitLab: GitLab-CI

    Việc triển khai CI/CD cho một dự án iOS Swift bao gồm một số phần sau:

    1. Triển khai CI với SwiftLint
    2. Triển khai CI với SonarQube, Blackduck
    3. Triển khai CD đơn giản với Gitlab-CI Artifact
    4. Triển khai CD In-house với DeployGate.
    5. Triển khai CD OTA Inhouse trên Website với AWS S3 (Static Website Hosting)
    6. Triển khai CD với Appstore Connect

    Cài đặt và khởi tạo Runner

    Đầu tiên, các chúng ta cần cài đặt và khởi tạo Runner cho Gitlab Repo. Mình có viết một bải hướng dẫn ở đây
    Chú ý:
    Sau khi đăng ký runner, nhưng các job vẫn chạy trên container mặc định của GitLab-CI thì hãy chạy các lệnh sau:

    gitlab-runner install
    gitlab-runner start
    gitlab-runner status

    1. Triển khai CI với SwiftLint

    Đầu tiên, chúng ta sẽ đi đến việc cấu hình CI sử dụng SwiftLint để phân tích chất lượng source code.

    Tốt nhất chúng ta sẽ cài đặt và sử dụng Swiftlint tách biệt với source code của dự án như sau

    brew install swiftlint

    Sau khi cài đặt Swiftlint, ta có thể test bằng cách di chuyển vào thư mục root của source code và chạy lệnh

    swiftlint

    Kết quả lint source code sẽ hiển thị như sau, ví dụ ở đây ta có 16 lỗi.

    Sau đó, chúng ta cấu hình file .gitlab-ci.yml để chạy lint trên branch cicd như sau:

    stages:
      - Lint
    lint-source:
      stage: Lint
      only:
          - cicd
      script:
        - swiftlint

    Kết quả khi commit code lên branch cicd, chúng ta sẽ có kết quả log như sau:

    Mặc định Swiftlint job sẽ trả về kết quả thành công exit 0


    Ở đây chúng ta thấy, hệ thống đã phát hiện được 16 lỗi ở 3 files code. Tuy nhiên, job vẫn success và các merge request vẫn được phép tiếp tục vì Swiftlint vẫn trả về success thay vì error. Đây là một rủi ro, và để ép chặt các thành viên phải fix hết các lỗi Swiftlint trước khi được merge code, ta sẽ thêm tham số vào script như sau:

    script:
      - swiftlint --strict

    Kết quả thu được, hệ thống scan code và trả về lỗi nếu swiftlint chưa được fix hết.

    Swiftlint job sẽ trả về lỗi exit 1 khi sử dụng tham số –strict


    Trong trường hợp chúng ta muốn “dễ dãi”, cho phép merge code trong trường hợp các lỗi swiftlint vẫn còn hoặc muốn job trả về thành công để tiếp tục các job tiếp theo, ta sẽ cấu hình thêm một chút như sau:

    stages:
      - Lint
    lint-source:
      stage: Lint
      allow_failure: true
      only:
        - cicd
      script:
        - swiftlint --strict
    Các job fail khi cấu hình allow_failure = true


    Khi ấy, các job phía sau vẫn sẽ được thực hiện, merge request vẫn sẽ được approve nhưng với thông báo Warning đáng chú ý hơn.

    Như vậy là đã hoàn thành công đoạn triển khai CI với Swiftlint, ở bài viết tiếp theo, mình sẽ hướng dẫn các bạn triển khai CI với SonarQube, Blackduck

    Authors

    LinhNB1

  • Đừng lạm dụng Enum

    Đừng lạm dụng Enum

    Nhà tâm lý học người Mỹ Abraham Maslow có một câu nói rất nổi tiếng

    If you only have a hammer, you tend to see every problem as a nail. (Nếu dụng cụ duy nhất bạn có chỉ là một chiếc búa, thì mọi vấn đề đều trông giống cái đinh)

    Câu nói này rất phù hợp với lập trình. Mỗi vấn đề đều có nhiều cách tiếp cận với ưu nhược điểm riêng tuỳ theo ngữ cảnh và ràng buộc. Không có giải pháp nào luôn hợp lý hoặc luôn tệ trong tất cả các trường hợp, kể cả Singleton ?. Enum cũng vậy. Nó là một tính năng ngôn ngữ linh hoạt và mạnh mẽ, tuy nhiên việc lạm dụng enum không chỉ làm giảm chất lượng code mà còn khiến codebase khó mở rộng hơn.


    Đầu mục bài viết


    1-Bản chất enum

    Trong tài liệu của mình, Apple chỉ ra rằng enum được tạo ra để định nghĩa một type với mục đích chứa các giá trị liên quan tới nhau. Nói cách khác, hãy dùng enum để nhóm một tập giá trị hữu hạn, cố định, và có quan hệ với nhau. Ví dụ như enum định nghĩa phương hướng

    //Tạo enum Direction ở đây là hợp lý bởi
    //các case liên quan tới nhau và số lượng case là hữu hạn
    enum Direction {
        case north
        case south
        case east
        case west
    }
    

    2-Vấn đề của enum

    Một khi được định nghĩa, chúng ta sẽ không thể thêm case để mở rộng enum mà không làm ảnh hướng tới những chỗ nó được sử dụng. Điều này có thể mang lại lợi ích nếu ta không dùng default case bởi Xcode sẽ giúp tránh việc bỏ lọt code. Tuy nhiên, đây cũng là một nhược điểm lớn trong trường hợp code hiện giờ không quan tâm tới case mới đó.

    Dùng enum để model các Error phức tạp

    Chắc hẳn chúng ta đã từng dùng enum để nhóm các loại Error cho response API như dưới đây

    enum APIError: Error {
        case invalidData(Data)
        case parsingError(Error)
        case noConnection
        case unknown(code: Int, description: String)
        //... các case khác ...
    }
    

    Thoạt nhìn, việc tạo APIError là hợp lý bởi chúng đều là lỗi liên quan đến response API và giờ đây ta có thể gõ .parsingError hay .invalidData cực kì tiện lợi. Mặc dù vậy, hướng tiếp cận này có 2 nhược điểm lớn:

    • Ta không bao giờ muốn switch toàn bộ case của nó
    • Nó không phải là cách tối ưu bởi struct là công cụ tốt hơn để giải quyết bài toán này

    Trong quá trình sử dụng, ngoại trừ các case cần thiết của APIError, việc switch toàn bộ case là hơi thừa thãi. Có thể hiện tại ta chỉ quan tâm đến lỗi .noConnection để hiện alert riêng và các lỗi khác sẽ dùng chung một kiểu alert. Cũng có thể ta chỉ quan tâm đến một vài lỗi nhất định để xử lý logic code nhưng chắc chắn không bao giờ là tất cả case cùng lúc. Lý do là bởi ngoài việc cùng miêu tả các error response, các error trên không có quan hệ gì với nhau.


    Hơn nữa, về mặt logic, việc dùng enum ở đây là sai bởi số lỗi có thể xảy ra khi xử lý API là vô hạn. Điều này trái ngược trực tiếp với bản chất của enum được nhắc đến ở trên. Trong trường hợp này, model APIError bằng struct phù hợp hơn rất nhiều

    struct InvalidData: Error {
        let data: Data
    }
    
    struct ParsingError: Error {
        let underlyingError: Error
    }
    
    struct NoConnection: Error { }
    
    struct Unknown: Error {
        let code: Int
        let description: String
    }
    

    Nếu thực sự muốn nhóm các lỗi vào một kiểu Error, chỉ cần tạo riêng một protocol và conform chúng với protocol đó

    protocol APIError: Error { }
    
    extension InvalidData: APIError { }
    extension ParsingError: APIError { }
    extension NoConnection: APIError { }
    extension Unknown: APIError { }
    

    Việc model APIError bằng structprotocol giúp code linh hoạt hơn khi giờ đây việc tạo ra các Error mới không làm ảnh hưởng đến codebase. Ta cũng có thể cung cấp hàm khởi tạo custom cho chúng, hay conform từng lỗi với các protocol khác nhau một cách dễ dàng thay vì những switch statement cồng kềnh trong enum. Cuối cùng, việc thêm và truy cập biến trong struct đơn giản hơn so với associated value trong enum rất nhiều.


    Sử dụng enum để model các Error đơn giản và hữu hạn là điều nên làm. Tuy nhiên, nếu tập Error đó lớn, hoặc chứa nhiều data đính kèm như các lỗi liên quan đến API thì struct là một lựa chọn tốt hơn hẳn. Trong thực tế, Apple cũng chọn cách này khi tạo URLError xử lý cho Networking của Foundation.

    Dùng enum để config code

    Một sai lầm phổ biến nữa là dùng enum để config UIView, UIViewController, hoặc các object nói chung

    enum MessageCellType {
        case text
        case image
        case join
        case leave
    }
    
    extension MessageCellType {
        var backgroundColor: UIColor {
            switch self {
            case .text: return .blue
            case .image: return .red
            case .join: return .yellow
            case .leave: return .green
            }
        }
        
        var font: UIFont {
            switch self {
            case .text: return .systemFont(ofSize: 16)
            case .image: return .systemFont(ofSize: 14)
            case .join: return .systemFont(ofSize: 12, weight: .bold)
            case .leave: return .systemFont(ofSize: 12, weight: .light)
            }
        }
        
        //...
    }
    
    class TextCell: UITableViewCell {
        func style(with type: MessageCellType) {
            contentView.backgroundColor = type.backgroundColor
            textLabel?.font = type.font
        }
    }
    

    MessageCellType định nghĩa các style cho giao diện của cell ứng với từng loại message để tái sử dụng ở nhiều màn khác nhau. Các thuộc tính chung có thể kể đến như backgroundColor hay UIFont.


    Giống với APIError, vấn đề đầu tiên của MessageCellType là ta không muốn switch toàn bộ case của nó. Với mỗi loại cell, ta chỉ muốn dùng một type nhất định để config cell đó. Việc switch tất cả các case ở hàm cellForRow(at:) là không hợp lý bởi luôn phải trả ra fatalError hoặc một UITableViewCell bù nhìn để thoả mãn Xcode vì số lượng subclass của UITableViewCell là vô hạn ?‍♂️.


    Một vấn đề khác với MessageCellType là việc khó mở rộng. Bản chất của enum là tính hoàn thiện và hữu hạn. Khi thêm bất kì case mới nào, ta đều phải update tất cả các switch statement sử dụng nó. Điều này đặc biệt tệ trong trường hợp đang viết framework vì giờ đây thay đổi sẽ phá hỏng code từ phía client.
    Giải pháp cho MessageCellType là biến nó thành struct và tạo ra các biến static thuộc type này

    struct MessageCellType {
        let backgroundColor: UIColor
        let font: UIFont
    }
    
    extension MessageCellType {
        static let text = MessageCellType(backgroundColor: .blue, font: .systemFont(ofSize: 16))
        static let image = MessageCellType(backgroundColor: .red, font: .systemFont(ofSize: 14))
        static let join = MessageCellType(backgroundColor: .yellow, font: .systemFont(ofSize: 12, weight: .bold))
        static let leave = MessageCellType(backgroundColor: .green, font: .systemFont(ofSize: 12, weight: .light))
    }
    

    Refactor từ enum thành struct giúp việc thêm config mới không còn là vấn đề bởi nó không hề ảnh hưởng tới codebase. Một lợi ích nhỏ nữa là ta vẫn được gõ .join hoặc .leave khi truyền chúng vào trong function

    let cell: TextCell = TextCell()
    cell.style(with: .join)
    

    3-Tổng kết

    Trước khi tạo enum, hãy luôn nhớ rằng

    Enum dùng để switch. Nếu không chắc rằng mình muốn switch nó thì hãy sử dụng struct và protocol


  • Swift – Basic to advanced Closure

    Swift – Basic to advanced Closure

    Với nhiều bài toán, không phải lúc nào cũng đơn giản. Ví dụ chỉ với 2 số nguyên, thực tế có rất nhiều công thức áp dụng được với 2 số này, từ đơn giản như cộng, trừ, nhân, chia … đến phức tạp như hàm mũ, khai căn,… Nếu chỉ sử dụng cách định nghĩa sẵn các function ta không giải quyết tất các các case của bài toán. Giải pháp ở đây là chúng ta sử dụng closure.


    Đầu mục bài viết

    Closure cơ bản

    1. Tạo closure cơ bản
    2. Tạo tham số cho closure
    3. Trả về giá trị từ closure
    4. Truyền closure như 1 tham số vào Function

    Closure nâng cao

    1. Truyền closure với nhiều tham số vào Function
    2. Trả về closure từ functiion
    3. Lưu trữ dữ liệu với closure

    Các kiểu gọi hàm với closure

    Tổng kết


    Basic closure

    1. Tạo closure cơ bản

    Swift cho phép chúng ta sử dụng function như bất kỳ kiểu dữ liệu nào,ví dụ như string, integers,… Điều này có nghĩa rằng chúng ta có thể tạo ra 1 function và gán nó cho một biến, gọi function đó bằng cách sử dụng biến đó và thậm chí có thể gán function đó vào các function khác dưới dạng tham số.

    Function mà ta sử dụng theo cách này được gọi là closure, mặc dù cách hoạt động của nó giống function tuy nhiên cách viết khác nhau 1 chút.

    Ví dụ đơn giản để print 1 đoạn text :

    Ở đây mình tạo ra một function tuy nhiên không có tên, và gán nó cho biến driving. Ta có thể gọi driving() như thể nó là 1 hàm thông thường :

    2. Tạo tham số cho closure

    Khi khởi tạo closure, ta sẽ nhận ra nó không có tên nên sẽ không có bất kỳ vị trí nào để thêm tham số như function bình thường. Tuy nhiên không có nghĩa là closure không nhận tham số input, chỉ là nó có cách làm khác so với function: các tham số được liệt kê bên trong dấu {}.

    Cách tạo ra các tham số để closure có thể “chứa chấp” rất đơn giản, chỉ cần liệt kê chúng bên trong dấu ngoặc đơn ngay sau dấu ngoặc nhọn mở, sau đó thêm keyword in để Swift biết phần nào là phần bắt đầu closure.

    Ví dụ mình sửa driving() bên trên thành closure có chứa tham số

    Và một trong những điểm khác biệt nữa giữa closure và function là bạn không cần sử dụng tên tham số khi gọi closure.

    3. Trả về giá trị từ closure

    Closure có thể trả về giá trị, và cách viết tương tự như khai báo tham số: viết nó trong closure, ngay trước keyword in.

    Với closure driving() bên trên, mình sẽ trả về đoạn string kia thay vì print thẳng ra, để làm thế ta sử dụng → String trước keyword in, sau đó sử dụng return như function bình thường

    Bây giờ có thể gọi closure này và in ra giá trị String nó trả về

    4. Truyền closure như một tham số vào function

    Vì closure có thể được sử dụng như string, integer,…, bạn có thể truyền nó vào 1 function. Syntax của nó khá là rắc rối với newbie tuy nhiên nếu bạn đã hiểu về nó thì sẽ thấy không rắc rối lắm :))

    Đây là closure driving() gốc của chúng ta

    Nếu bạn muốn truyền closure này vào trong 1 function để nó có thể thực thi bên trong function đó, bạn phải chỉ định kiểu tham số là () -> Void.

    Ví dụ mình viết 1 function travel() mà nó nhận tham số là các kiểu action khác nhau

    Ta có thể gọi hàm travel() mà sử dụng closure driving

    Bên trên là 1 ví dụ về truyền closure như 1 tham số, tuy nhiên ta đang sử dụng () → Void, nói cách khác là không truyền vào tham số và cũng không nhận giá trị trả về.

    Tuy nhiên,1 closure vẫn có thể nhận tham số của chính nó khi chính closure đó đang là 1 tham số của function khác.

    Ví dụ, mình viết lại function travel() mà nó chỉ có 1 closure là tham số duy nhất, và closure đó nhận tham số là 1 String

    Và để thực thi function travel(), ta gọi nó với 1 tham số closure


    Advanced closure

    1. Truyền closure với nhiều tham số vào function

    Mình lại xin phép viết lại hàm travel() bên trên, tuy nhiên lần này hàm travel() của chúng ta sẽ cần 1 closure mà nó chứa một vài thông tin khác thay vì 1 string như bên trên, cụ thể nó sẽ chứa thông tin về địa điểm mà một người sẽ đến và tốc độ họ đi. Lúc này chúng ta cần sử dụng (String, Int) → String cho kiểu tham số của closure

    Để thực thi function travel(), ta gọi function này với tham số closure được truyền vào

    ( Bạn có thể thấy lạ với các keyword $0 và $1, hiện tại bạn không hiểu cũng không sao cả vì mình sẽ giải thích rõ hơn ở dưới =]] )

    Closure giống với function là nó có thể nhận bao nhiêu tham số cũng được, tuy nhiên bạn có thể thấy rằng một function mà nhận quá nhiều tham số thì sẽ rất khó hiểu dẫn đến confuse, điều này còn kinh khủng hơn với closure khi bản chất closure cũng đã rất phức tạp rồi. Vì thế để mọi thứ clear bạn nên chỉ sử dụng từ 1 đến 3 tham số  mà thôi.

    2. Trả về closure từ function

    Tương tự như việc bạn truyền tham số closure vào function, bạn cũng có thể nhận về 1 closure mà được trả về từ function.

    Syntax để return closure từ function có hơi rắc rối 1 chút, bởi vì nó dùng → 2 lần: một để chỉ định giá trị trả về của function và một để chỉ định giá trị trả về từ closure.

    Mình lại viết lại hàm travel() mà không nhận tham số, tuy nhiên lại có trả về 1 closure mà closure nhận tham số là String và trả về Void

    Chúng ta gọi travel() để nhận về closure đó, sau đó gọi nó như 1 function

    Còn 1 cách gọi nữa để gọi trực tiếp giá trị trả về từ travel() – cách này không được khuyến khích sử dụng:

    3. Lưu trữ dữ liệu với closure

    Nếu bạn sử dụng bất kỳ giá trị bên ngoài nào trong closure, Swift sẽ lưu và giữ chúng cùng closure, vì thế giá trị này có thể bị thay đổi kể cả nó không còn tồn tại.

    Ví dụ, việc lưu trữ giá trị trong closure xảy ra khi ta tạo 1 giá trị trong hàm travel() mà giá trị đó được sử dụng trong closure, ở đây ví dụ như ta muốn kiểm tra xem closure được gọi trả về bao nhiêu lần

    Mặc dù biến counter được tạo bên trong travel(), nó sẽ được lưu trữ bởi closure vì thế nến nó sẽ vẫn luôn tồn tại cho closure đó.

    Vì thế nếu ta gọi result(“London”) nhiều lần, biến đếm sẽ luôn tăng lên :


    Các kiểu gọi hàm với closure

    Ví dụ, ở đây định nghĩa 1 functiontypecalculationresultcallback với tham số đầu vào là 1 kiểu int và không có giá trị trả về, chúng ta cũng có thêm 1 hàm là multiplyNumber với 2 tham số nhận vào là kiểu int, không có giá trị trả về và kèm theo 1 tham số có tên là callback có kiểu là CalculationResultCallback. Trong ngôn ngữ lập trình nói chung, callback có nghĩa là lời gọi hàm sau, tức là sau quá trình sử lý logic và ra được 1 kết quả nào đó mà chúng ta cần xử lý thêm với kết quả đó.

    Hàm multiplyNumbers có ý nghĩa là, với 2 giá trị nguyên bất kỳ, nó sẽ tính phép nhân 2 số, sau đó sẽ trả về kết quả bằng 1 hàm mà ở đó, ta có thể tùy ý sử dụng kết quả theo ý ta muốn.

    Ta có các cách gọi hàm như sau:

    • Ta có thể thấy, 2 tham số đầu tiên có kiểu int được gọi hoàn toàn bình thường, nhưng với tham số thứ 3 với tên là callback, thay vì truyền vào 1 số, hoặc 1 tên hàm như trước, thì ta truyền vào hẳn 1 hàm,mà ở đó ta in ra dòng “Tích của 2 số là …”
    • Trong closure này,chúng ta có từ khóa in phân cách 2 nửa,bên trái là khai báo tham số nằm trong () và khai báo kiểu trả về là void,bên phải là lệnh được thực thi khi gọi đến closure này,toàn bộ closure được bao trong cặp dấu {}.
    • Giống như function bình thường,ta có thể bỏ void nếu hàm không trả về kết quả,và có thể bỏ đi luôn () bao tham số.
    • Đây chính là dạng gọi cơ bản nhất của closure
    • Swift quy định,với những hàm khai báo closure là tham số nhưng ở vị trí cuối cùng trong danh sách tham số: ví dụ như trên,ta có thể gọi hàm bằng cách gọi hàm với 2 tham số int bình thường,kéo theo 1 tham số closure được bao bằng dấu {},trường hợp này được gọi là Trailing closure
    • Để closure gọn hơn nữa, có 1 tính năng gọi là Shorthands, bản chất là thay vì khai báo tham số với tên biến cụ thể thì chúng ta sử dụng cú pháp $0,$1,$2,… để thay thế cho các tham số ở vị trí 0,1,2,.. Tất nhiên,nếu hàm chỉ có 1 tham số mà ta gọi đến $1 thì hàm sẽ báo lỗi.
    • Đối với shorthands, ta không thể khai báo tham số như những trường hợp trên nữa,đổi lại sẽ ngắn gọn hết sức có thể..
    • Tuy nhiên,với hàm có nhiều tham số,ta khó xác định $0,$1,$2,… là gì.Vì thế nên chỉ sử dụng shorthands với hàm đơn giản chỉ có từ 1-3 tham số mà thôi.

    Tổng kết

    Về bản chất

    • Closure là 1 function.
    • Nhưng là function không đầy đủ tên function và thân function , mà chỉ có mỗi thân function .
    • Mục đích của nó không phải gọi function bằng tên, mà là được chèn vào tham số của 1 function khác.
  • Hướng dẫn tạo và sử dụng Manually Signing App

    Hướng dẫn tạo và sử dụng Manually Signing App

    Link tham khảo: https://help.apple.com/developer-account/

    Code Signing là một thứ bắt buộc để ta có thể cài đặt ứng dụng vào devices thật, hoặc để upload lên AppStore Connect. Có hai cách để cài đặt code signing, “Automatically manage signing” hoặc “Manually manage signing”. Bài viết này sẽ hướng dẫn cài đặt manual code signing.

    Để cài đặt app, bạn cần có :

    • Signing certificate (Personal Information Exchange, .p12)
    • Provisioning profile (.mobileprovision)

    Signing certificate là chứng chỉ giúp xác định danh tính để cài app

    Provision profile (development hoặc distribution) chứa những thông tin về appID, các devices mà app có thể cài đặt, thông tin certificate để signing app. Lưu ý rằng nếu ứng dụng có chứa các extensions, bạn cần thêm các provision profile tương ứng 

    Mỗi dự án sẽ có những certificate và provision profile riêng. Như ảnh dưới, ta có 2 certificate, cho môi trường dev và distribute, ta cũng có các provision dev và distribute tương ứng, kèm theo đó là những provision profile của các extension của app.

    Sau đây là hướng dẫn tạo manually signing app:

    B1. Tạo certificate cho app

    1. Ở Certificates, Identifiers & Profiles, chọn Certificates

    2. Chọn nút (+)

    3. Chọn loại certificates mà mình muốn và chọn nút “Tiếp tục”

    4. Tạo certificate signing request

    4.1. Mở app Keychain Access ở máy

    4.2. Chọn Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority. 

    4.3. Điền thông tin như email, name, bổ trống CA Email Address

    4.4. Chọn “Save to disk” và chọn tiếp tục

    5. Chọn file đuôi .certSigningRequest đã tạo ở b4

    6. Chọn “Tiếp tục” và “Tải về” máy. (File certificate sẽ có đuôi .cer)

    B2. Đăng ký AppID

    AppID sẽ định danh app của bạn trong provisioning profile. Có 2 loại AppID: explicit AppID (sử dụng riêng từng app) và wildcard AppID (sử dụng chung 1 số app). Wildcard AppID sẽ chỉ enable được một số Capabilities, nếu muốn sử dụng những Capabilities khác, bạn phải tạo explicit AppID

    Các bước tạo AppID:

    1. Trong Certificates, Identifiers & Profiles, chọn “Identifiers”, rồi chọn (+)
    2. Chọn AppIDs 
    3. Điền name, descriptions, chọn các loại Capabilities mà app sẽ dùng
    • Nếu chọn Explicit App ID, bạn phải điền giống bundleID của app trong Xcode
    • Nếu chọn Wildcard App ID, bạn phải điền bundle ID với hậu tố (VD: com.domainname.*)

    B3. Đăng ký devices

    Đăng ký một device:

    1. Trong Certificates, Identifiers & Profiles, chọn Devices, rồi chọn (+)
    2. Chọn platform, điền device name, device ID (UDID)
    3. Chọn tiếp tục, chọn “Register” để hoàn tất đăng ký

    Đăng ký nhiều device:

    Bạn có thể dùng app “Configurator 2” trên MacAppStore hoặc tạo file .txt chứa thông tin (mỗi dòng chứa deviceID, device name, platform name cách nhau bởi tab-delimited)

    B4. Tạo provisioning profile

    1. Trong Certificates, Identifiers & Profiles, chọn Profiles, rồi chọn nút (+) 
    2. Chọn loại provisioning profile mà bạn muốn tạo, rồi chọn “Tiếp tục”

    3. Chọn App ID mà mình đã tạo ở Bước 2, chọn “Tiếp tục”

    4. Chọn Certificate mà mình đã tạo ở Bước 1, chọn “Tiếp tục”

    5. Chọn các device đã được tạo ở Bước 3, chọn “Tiếp tục”

    6. Điền profile name, rồi chọn “Generate”

    7. Chọn “Download” để tải về

    8. Sau khi đã tải về, click double vào các certificate và nhập mật khẩu để add vào keychain

    • Tắt Automatically manage signing trong Xcode 
    • Import các provision profile tương ứng 

    Nếu status không còn báo đỏ nữa là bạn đã import thành công. Giờ run và build thôi.

  • The Application’s Life Cycle

    The Application’s Life Cycle

    Application’s life cycle – Vòng đời của 1 chương trình. Đây là 1 phần cơ bản nhưng cực kỳ quan trọng trong việc lập trình một ứng dụng.  Tuy có thể coi là 1 kỹ năng trấn phái nhưng không phải ai cũng nắm được rõ và đẩy đủ về vòng đời này (minh chứng là vẫn bị tester bắt nhiều bug về các case abnormal liên quan đến các state của app). Chính vì thế mình viết bài này để có thể giới thiệu 1 cách chi tiết về vòng đời của app và cách sử dụng để tránh những lỗi không đáng có. 

    iOS App life cycle - Brian - Medium


    Một application sẽ có các trạng thái như sau: 
    Not running: Là trạng thái application chưa được bắt đầu hoặc đã chạy nhưng bị terminated bởi system. 
    Inactive: Application đang chạy ở Foreground nhưng không nhận bất cứ sự kiện tương tác nào và cũng không thể xử lý các sự kiện (có thể là bị một vài sự kiện tác động vào trong quá trình chạy, ví dụ như có cuộc gọi đến hay tin nhắn chẳng hạn). 1 app cũng có thể ở trong trạng thái này khi chuyển từ state này sang state khác. 
    Active: Application đang chạy ở Foreground và đang nhận các sự kiện bình thường. Cách duy nhất để đến trạng thái Active là thông qua Inactive. Ở trạng thái này, khi người dùng tương tác với UI, họ có thể nhìn thấy phản hồi cho những hành động của họ.
    Background: Application đang chạy ở background và đang thực thi code. Ở trạng thái này UI của app không được hiển thị nhưng mà nó lại vẫn đang chạy (nếu có đăng ký background task với OS). Hầu hết các app chuyển trạng thái sang suspended thông qua trạng thái này.
    Suspended: Application đang chạy ở background nhưng không thể thực thi code. Thường thì sẽ do chính system sẽ tự động đưa app về trạng thái này và lúc đó app vẫn đang trong memory. Trong trường hợp low memory, hệ thống có thể sẽ tự kill app của mình khi app đang ở trạng thái suspended mà không thông báo gì. 
    Lưu ý rằng: Theo chuẩn của Apple thì chỉ hệ thống mới có thể kill app. 


    Về cơ bản thì 1 application có các trạng thái như trên, và trong app chúng ta cũng có các event tương ứng để được notify khi bắt đầu hay đã vào các trạng thái trên. Các hàm đó được list trong AppDelegate. Các trạng thái chuyển đổi qua lại được gọi là transition giữa các trạng thái.
    – application:willFinishLaunchingWithOptions  ——   Method này được gọi sau khi app của chúng ta khởi chạy thành công. Nó là method đầu tiên được chạy từ app delegate. Chúng ta có thể thực thi các đoạn code nếu khởi chạy thành công.
    – application:didFinishLaunchingWithOptions    ——   Method này được gọi trước khi window của app được hiển thị. Bạn có thể hoàn thiện giao diện của mình và cung cấp root viewcontroller cho window.
    applicationDidBecomeActive ——   Method này được gọi để báo cho app của bạn biết khi nó chuyển trạng thái từ In-Active sang Active hoặc hệ thống và user khơi động app hoặc trong trường hợp user bỏ quan các gián đoạn làm app ngay lập tức chuyển sang In-Active (như là có cuộc gọi đến hoặc tin nhắn). Bạn nên dùng method này để chạy lại các tác vụ đang bị dừng (hoặc chưa chạy) khi app bắt đầu chạy lại.
    applicationWillResignActive ——   Method này được gọi để báo cho app biết rằng nó sắp chuyển từ trạng thái Active sang In-Active . Nó xãy ra khi trường hợp bị gián đoạn (có cuộc gọi tới hoặc SMS) hay là khi user tắt app đi. Bạn nên dùng method này để dừng các task đang chạy hoặc vô hiệu hoá timer trong app, hoặc nhiều thứ khác 
    applicationDidEnterBackground  ——   Method này được gọi để báo cho app biết nó đang không chạy ở dưới foreground. Bạn có khoảng tầm 5 giây để thực thi các task . Trong trường hợp bạn muốn có nhiều thời gian hơn để xử lý, bạn có thể yêu cầu hệ thống cấp cho thời gian thực thi bằng cách gọi hàm beginBackgroundTask(expirationHandler:) . Nếu như method của bạn không được thực thi và trả về trước thời gian hết hạn thì app sẽ bị hệ thống chấm dứt và xoá khỏi bộ nhớ.
    applicationWillEnterForeground  ——   Method này được gọi như là 1 phần trong việc chuyển trạng thái từ Background sang Active. Bạn nên dùng method này để hoàn thành các thay đổi đối với app trước khi nó xuống Background. applicationDidBecomeActive sẽ được gọi ngay khi method này đã hoàn thành việc chuyển trạng thái của app từ In-Active sang Active.
    applicationWillTerminate  ——   Method này được gọi khi app của bạn sắp bị hệ thống khai tử khỏi bộ nhớ. Bạn nên dùng method này để thực thi các tác vụ dọn dẹp. Bạn có tầm khoảng 5 giây để thực thi tác vụ. Nếu hàm của bạn không trả về trước thời gian hết hạn, hệ thống sẽ tự động khai tử app kèm cã task đang thực thi của bạn khỏi bộ nhớ. Method này cũng được gọi trong trường hợp app đang chạy ở dưới background( không bị suspended) nhưng hệ thống lại cần phải huỷ nó vì vài lí do gì đó. Bạn không nên đợi applicationWillTerminate được gọi rồi mới lưu lại data. Trong 1 vài trường hợp hi hữu, applicationWillTerminate sẽ không được gọi trước khi app bị khai tử (Vd như trong trường hợp máy của bạn reboot lại thì method này sẽ không được gọi).

    Đó là tất cả về iOS Application’s life cycle.
    Cảm ơn mọi người đã theo dõi bài viết. Hi vọng bài viết này có thể giúp ích cho các bạn.
    Mọi ý kiến đóng góp các bạn vui lòng comment ở bên dưới để mình có thể hoàn thiện hơn ở các bài viết sắp tới.

    Thanks all from with love <3
    KhanhVD1.

  • iOS: Một số cách lưu dữ liệu phổ biến

    iOS: Một số cách lưu dữ liệu phổ biến

    Lời mở đầu

    Đối với các ứng dụng iOS, việc lưu dữ liệu là việc bắt buộc. Việc lưu dữ liệu rất cần thiết đối với các ứng dụng từ lớn cho tới nhỏ. Bạn có thể lưu dữ liệu lớn như các dữ liệu lấy được từ các API, cho đến các trạng thái hoặc các cài đặt của ứng dụng. Bài viết này mình sẽ giới thiệu tổng quát về các cách chúng ta có thể lưu dữ liệu trong iOS. Bài viết này chỉ mang tính giới thiệu cho từng loại thường dùng cho việc gì, các cách thao tác trên từng loại mình sẽ tách ra làm các bài riêng cho các bạn dễ hiểu và đỡ bị ngợp.

    1. Lưu dữ liệu bằng file

    Các dữ liệu cơ bản thường gặp khi sử dụng ứng dụng như String(VD: json, xml ..) và Data(VD: image)

    Đối với những kiểu dữ liệu cơ bản này chúng ta có thể thực hiện thao tác lưu(save), lấy(get) dữ liệu thông qua các hàm đã được cung cấp sẵn trong class đó.

    Chúng ta thường dùng cách này để lưu những dữ liệu như:

    • Lưu lại log của ứng dụng
    • Lưu lại hình ảnh, cache data
    • Lưu lại nội dung có dữ liệu lớn
    • Lưu lại nội dung dạng json hoặc xml

    Về cơ bản thì với cách này các bạn có thể lưu được rất nhiều thứ và nó có thể được quản lý, sắp xếp tùy ý. Do đuôi file không bị cố định nên bạn có thể lưu nội dung lên file có đuôi mình muốn và mở bằng các ứng dụng hỗ trợ.

    Để quản lý thư mục lưu file, chúng ta nên sử dụng NSFileManager. Ngoài ra NSFileManager cũng cung cấp cho chúng ta các hàm để save(lưu), get(lấy) dữ liệu. Nhưng các hàm này bản thân các class như String và Data cũng có rồi nên NSFileManager thường được dùng để quán lý thư mục.

    Hướng dẫn lưu dữ liệu ra file

    2. Lưu dữ liệu phân cấp

    Property Lists

    Là các file có đuôi .plist, dùng để lưu dữ liệu có dạng như một hệ thông phân cấp kiểu Array hoặc Dictionary với dung lượng không quá lớn.

    Trong iOS, Property List chỉ lưu được một số kiểu dữ liệu cơ bản như:

    • String
    • Data
    • Date
    • Number
    • Bool
    • Array
    • Dictionary

    Ngoài ra chúng ta cũng có thể lưu được các object. Để làm điều đó chúng ta cần mã hóa(encode) nó về dạng Data rồi khi cần dùng thì giải mã(decode) dữ liệu trở lại.

    NSUserDefault

    NSUserdefault là một file Property List, đã được Apple viết sẵn 1 class dạng singleton. Nó giúp các iOS developer thuận tiện và tiết kiệm thời gian hơn trọng việc lưu/lấy dữ liệu cần thiết ở file mà NSUserdefault đã tạo sẵn cho mỗi project của bạn.

    Property List thường được dùng để:

    • Lưu dữ liệu đơn giản có dung lượng thấp
    • Lưu lại các setting của ứng dụng
    • Lưu lại các trạng thái của ứng dụng

    3. Lưu dữ liệu sử dụng SQLite

    Đối với các ứng dụng có nhiều dữ liệu phức tạp, với các mối quan hệ chồng chéo giữa các dữ liệu thì việc sử dụng cách 1 với 2 không thể đáp ứng được các yêu cầu về tốc độ tìm kiếm, sắp xếp và thậm chí là hiệu năng về việc dọc và lưu dữ liệu mà ứng dụng yêu cầu. Với những dạng dữ liệu như này chúng ta nên trang bị cho ứng dụng một phương thức tốt hơn đó chính là SQLite.

    SQLite

    SQLite là một hệ thống cơ sở dữ liệu quan hệ nhỏ gọn,hoàn chỉnh có thể cài đặt bên trong các ứng dụng khác.

    Đặc điểm của SQLite là gọn, nhẹ và đơn giản. Không cần cài đặt, không cần cấu hình hay khởi động mà có thể sử dụng ngay. Dữ liệu database cũng được lưu ở một file duy nhất. Đối với các ứng dụng mobile SQLite rất thích hợp để sử dụng.

    SQLite thường được sử dụng khi nào:

    • Khi bạn có một khối dữ liệu lớn cần phải lưu lại
    • Khi bạn có một tập các dữ liệu liên quan móc nối với nhau
    • Khi bạn muốn lưu lại các dữ liệu mà API trả về

    Một số thư viện hay sử dụng

    • FMBD(Flying Meat Database): Là một thư viện hỗ trợ bạn trong việc thao tác với SQLite . Nói cách khác FMDB là một Objecttive-C wrapper của SQLite. Công việc của FMDB là giúp bạn thoải mái hơn trọng việc thực hiện các câu lệnh truy vấn trong SQLite.
    • CoreData: Là một framework được Apple xây dụng để hỗ trợ chúng ta thao tác với SQLite Database theo hướng đối tượng mà không phải quan tâm tới các câu lệnh truy vấn của SQLite. Nó sẽ coi các bản ghi trong SQLite Database như một object, table như class.

    Nên sử dụng FMBD(Flying Meat Database) hay CoreData

    CoreData là một Framework được Apple phát triển để làm việc với SQLite Database vì vậy việc sử dụng CoreData sẽ được khuyến khích hơn cả. CoreData giúp việc viết và đọc code rõ ràng hơn việc sử dụng FMDB để làm việc trực tiếp với SQLite. Nó tốt cho việc bảo trì cũng như phát triển code sau này. Ngoài ra Apple còn hỗ trợ cả việc cache dữ liệu, tích hợp CoreData với UITableView, một class mà hầu hết các ứng dụng đều sử dụng.

    Bạn đọc dữ liệu từ database lấy ra một list các object và hiển thị nó bằng UITableView nó thực sự rất tiện lợi đối với các iOS Dev.

    4. Keychain

    Keychain là một lưu trữ bảo mật với những dữ liệu nhỏ gọn, nhạy cảm như mật khẩu, số tài khoản ngân hàng, mã bảo mật hay một số thông tin mà ta muốn bảo mật không muốn cho người khác biết được.

    Ngoài ra Apple cũng cung cấp một dịch vụ Keychain Sharing để chia sẻ keychain giữa các ứng dụng của cùng một nhà phát triển. Ví dụ như Facebook và Messenger, nếu ta đăng nhập ở Facebook và lưu password vào Keychain thì sang Messenger chúng ta có thể sử dụng password để đăng nhập nhanh ở ứng dụng này.

    5. Realm

    Realm là một database sử dụng core C++ với tham vọng thay thế SQLite với các ưu điểm về tốc độ và dễ sử dụng. Hiện nay Realm rất được ưa chuộng trên thế giới vì nó có rất nhiều ưu điểm vuợt trội so với những cách còn lại.

    Ưu điểm của Realm so với CoreData:

    • Dễ sử dụng hơn
    • Tốc độ query nhanh hơn
    • Quản lý dữ liệu dễ dàng trực quan hơn
    • Tài liệu tham khảo tốt
    • Open source

    Khi nào nên sử dụng Realm cho dự án của bạn:

    • Khi project của bạn cần lưu lại số lượng lớn các bản ghi
    • Khi project của bạn có nhiều dữ liệu móc nối với nhau, có mối quan hệ phức tạp
    • Khi dự án của bạn cần lưu lượng lớn các thông tin từ API

    Tổng kết

    Bài viết này mình đã giới thiệu với các bạn về một số cách lưu dữ liệu phổ biến trong swift. Mỗi khi các bạn muốn lưu một loại dữ liệu nào hãy cân nhắc kiểu dữ liệu nó là gì, mức độ quan trọng của nó như nào. Từ đó bạn có thể chọn được cách lưu dữ liệu tối ưu nhất với loại dữ liệu đó. Các bài viết tiếp theo mình sẽ giới thiệu với các bạn cách triển khai lưu dữ liệu cho từng cách.

    Cảm ơn các bạn đã theo dõi bài viết! Chúc các bạn thành công.

  • Push Notification trên iOS Simulator

    Push Notification trên iOS Simulator

    Như các bạn đã biết, để dùng APNS (Apple Push Notification service) thì chúng ta cần phải có device thật. Nhưng chuyện đó đã là quá khứ khi ở bản 11.4 beta, Apple đã cho phép test push notification ngay trên simulator. Tuyệt vời !!! ?

    Để có thể push notification trên simulator, bạn cần:
    Bước 1: Tải Xcode 11.4 beta hoặc các phiên bản mới hơn tại link nè: https://developer.apple.com/download/

    Bước 2: Tạo project và grant permission 
    Appdelegate.swift, import framework UserNotifications, và yêu cầu quyền nhận notification ở hàm application(_:didFinishLaunchingWithOptions:)

    Bước 3: Tạo file APNS payload
    APNS payload là một file json dictionary chứa đựng các thông tin của Notification như kiểu thông báo, nội dung thông báo… Bạn có thể vào đây để xem thêm chi tiết:
    https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification

    Mình tạo file payload thêm 1 key “Simulator Target Bundle” như sau:

    Trong đó “yourBundleID” là bundleID app của bạn, bundle project của mình là “com.self.NotificationSimulator

    Bước 4:  Giờ kéo thả vào simulator thôi!
    Giờ bạn hãy kéo file payload vừa tạo vào simulator, xem điều kì diệu gì xảy ra nhé 

    Simulator đã có notification ?

    Ngoài cách kéo thả file APNS vào Simulator, ta còn có thể dùng câu lệnh Command để gửi noti. Ở Xcode 11.4 này đã có thêm command xcrun simctl push hỗ trợ việc bắn notification.

    xcrun simctl push <simulator-identifier> <path-to-payload-file>

    trong đó <simulator-identifier> là ID của simulator, <path-to-payload-file> là đường dẫn đến file payload. Bạn có thể lấy ID simulator như sau:

    Nếu bạn ngại việc copy identifier, bạn có thể dùng xcrun simctl push booted <path-to-payload-file> để push notification ngay trên simulator đang mở. Và kết quả:

    Kết luận

    Giờ đây ta có thể test push notification thật đơn giản trên simulator. Ta có 2 cách để test:
    – Kéo thả file APNS vào simulator
    – Trỏ đường dẫn file APNS hoặc Json payload qua command line
    Sau bài viết này, mình sẽ giới thiệu các bạn về Leanplum – một marketing platform cho mobile, và xem điểm giống và khác nhau giữa Leanplum vs Firebase nhé

    Nguồn: https://swiftsenpai.com/xcode/simulating-push-notifications-in-ios-simulator/

  • Cách lấy UDID của thiết bị iPhone, iPad, iPod…

    Cách lấy UDID của thiết bị iPhone, iPad, iPod…

    + UDID là gì?
    UDID là viết tắt của Unique Device Identifier là hình thức cho nhận dạng thiết bị duy nhất và là một chuỗi chữ số xác định duy nhất iPad hoặc iPhone… của bạn từ tất cả các thiết bị khác.
    + UDID dùng khi nào?
    Nó dùng ở nhiều chỗ, nhưng dùng nhiều nhất với lập trình viên là để đăng kí thiết bị trên trang Developer Apple, chuẩn bị cho quá trình build file .ipa theo hình thức Adhoc…
    Dưới đây là các cách lấy UDID mà các bạn có thể tham khảo. ?

    Cách 1: Dùng thiết bị chạy MacOS.

    B1: Nối thiết bị iDevice với máy tính của bạn.
    B2: Mở “Finder” và click vào các bước giống hình.
    1) Nhấp chuột tên device.
    2) Nhấp chuột vào thông tin device, đến khi hiện ra thông tin UDID như hình.

    Cách 2: Dùng thiết bị chạy Window.

    Mở ứng dụng “iTunes”, chọn vào hình “thiết bị”.

    Chọn mục “Summary” , kích chuột vào dòng “Serial Number” đến khi hiện ra “UDID” như hình dưới.

    Hình mình mượn trên mạng vì ko có thiết bị window. 😀

    Cách 3: Dùng ngay iDevices.
    (Cách này khá hữu dụng, vì nó giúp lấy UDID ngay trên điện thoại…trong trường hợp khách hàng, hoặc sếp… của bạn ở xa yêu cầu gửi cho bản build .ipa để cài đặt… thì bạn có thể gửi ngay hướng dẫn này cho họ ?)


    Truy cập đường link https://get.udid.io/ và làm theo các bước dưới hình

    Kết quả là dãy số ở bước 11.
    Trên đây là 1 số cách mình thấy khá thông dụng, tất nhiên còn nhiều hình thức khác.
    Hi vọng bài viết hữu dụng cho các bạn. ?