Author: minhnn44

  • Một số tips Debug ứng dụng với Xcode

    Một số tips Debug ứng dụng với Xcode

    Overview

    Debug là một kỹ năng không thể thiếu của một lập trình viên, kỹ năng debug giỏi giúp ích rất nhiều trong việc tìm hiểu và giải quyết vấn đề phát sinh. Thêm vào đó, Xcode cung cấp rất nhiều công cụ hỗ trợ quá trình debug, nếu tận dụng được hết thì bạn có thể dễ dàng xử lý nhiều vấn đề khó. Bài viết tổng hợp lại một số kinh nghiệm và kiến thức của mình trong quá trình debug ứng dụng với Xcode.

    Debugging Process

    Việc có một process cụ thể sẽ giúp ta biết được sẽ phải làm gì tiếp theo trong từng giai đoạn, giúp việc debug diễn ra suôn sẻ. Theo như những gì mình học và thực hành được từ thực tế, một flow debug sẽ có các bước như sau:

    1. Discover: Xác định lỗi, đơn giản là chúng ta cần phải biết được lỗi đang gặp phải là gì, là crash app, freeze app, memory leak, business chạy không đúng, data không
    2. Locate: Xác định vị trí đoạn code gây ra lỗi
    3. Inspect: Kiểm tra flow, dữ liệu, logic để tìm ra nguyên nhân khiến đoạn code chạy sai
    4. Fix: Tất nhiên là sửa lại đoạn code cho đúng
    5. Confirm: Đảm bảo là lỗi đã được giải quyết và không có ảnh hưởng, hậu quả gì để lại

    Debugger Tools

    Tiếp theo chúng ta sẽ đi qua một lượt những gì Xcode cung cấp trong Debugger Tools. Đây là màn hình capture các thành phần Debugger Tools Xcode cung cấp cho chúng ta:

    Debug Navigator

    Debug Navigator là khu vực bên trái, gồm có 2 thành phần chính:

    1. Debug Gauge: Là thước đo các chỉ số của ứng dụng như CPU, Memory, Disk I/O, GPU, … Theo dõi các chỉ số trên Debug Gauge có thể giúp ích trong quá trình Discover các bug liên quan đến Memory Leak, CPU Usage, Read/Write Data, FPS, …
    2. Process View: Hiển thị các Threads đang chạy và Call Stack Trace đến breakpoint tại thời điểm ứng dụng dừng để debug. Sử dụng Process View giúp ích trong việc Locate các đoạn code gây nên lỗi, các bạn có thể dễ dàng tìm được đoạn một đoạn code/hàm được gọi từ đâu chỉ bằng việc nhìn vào Call Stack thay vì đọc và search từng dòng code.
    Nhìn vào CallStack ta có thể thấy, Breakpoint được gọi có nguồn gốc từ viewDidLoad() của ViewController

    Breakpoint

    Breakpoint là khái niệm rất quen thuộc ở mọi Debugger Tools, chính là đánh dấu cho vị trí được đánh dấu để debug, khi ứng dụng chạy đến vị trí code này, Debugger Tools sẽ tự động dừng ứng dụng và thực hiện quá trình debug, hiển thị các thông tin tại thời điểm và vị trí đặt Breakpoint.

    Chúng ta có thể đặt nhiều Breakpoint, ứng dụng sẽ dừng ở Breakpoint đầu tiên chạy tới.

    Source Editor

    Source Editor là khu vực để thay đổi source code, tuy nhiên trong khi ứng dụng đang ở chế độ debug chúng ta có thể di chuyển pointer tới các instance cần kiểm tra, và inspect trực tiếp các thông tin của instance đó. Đối với một số data type đặc biệt như UIImage, chúng ta có thể view trực tiếp ảnh từ Source Editor.

    Inspect UIImage
    Inspect các thông tin của instance

    Debug Bar

    Debug Bar bao gồm bộ các chức năng hỗ trợ trong quá trình debug, dưới đây là một số chức năng mình hay sử dụng:

    1. Hide/Show: Hiển thị hoặc Ẩn khu vực debug bên dưới Debug Bar
    2. Breakpoint Activation: Enable/Disable các Breakpoint, khi disable thì ứng dụng sẽ không dừng lại khi xử lý qua các vị trí đặt Breakpoint
    3. Continue/Pause: Tạm dừng/Tiếp tục việc thực thi code của ứng dụng
    4. Step controls: Thực thi dòng code tại vị trí hiện tại; Nếu vị trí hiện tại là function, jump vào function đó; Jump ra vị trí đang gọi đến function chứ vị trí hiện tại
    5. Debug View Hierarchy: Hiển thị cấu trúc các Controllers, Views dưới dạng 3D. Hỗ trợ việc theo dõi vị trí và mối quan hệ của các UIComponents tại một thời điểm

    Variables View

    Variables View là khu vực bên trái, phía dưới Debug Bar, nơi hiển thị các instance của ứng dụng tại thời điểm debug. Tuy nhiên sẽ chỉ hiển thị các instance được sử gọi đến trong block/function chứa vị trí Breakpoint.

    Ngoài việc hiển thị, chúng ta còn có thể inspect/edit thông tin các instance

    Console & LLBD

    Console View là nơi hiển thị các thông tin chúng ta in ra console. Ngoài ra, Console còn được tích hợp thêm LLDB Command, giúp cho việc Debug đã dễ lại càng trở nên dễ dàng hơn. Mình sẽ liệt kê một số LLDB Command thông dụng thường đươc sử dụng trong khi Debug.

    • po variable: In ra giá trị của một biến
    • p variable: In ra toàn bộ thông tin của một biến
    • fr v: In ra toàn bộ thông tin của các biến
    • e expression: Thực thi một expression, ví dụ: e i=10 sẽ thay đổi giá trị của i bằng 10; e var $x=10 sẽ khai báo thêm một biến có giá trị 10.
    Ví dụ sử dụng LLDB Command

    Ngoài ra còn rất nhiều command hữu ích khác, các bạn có thể tham khảo thêm ở đây.

    Trên đây là một số kiến thức và kinh nghiệm của mình, nếu có gì thiếu xót mong được các bạn góp ý. Cảm ơn đã ủng hộ!

    Authors

    MinhNN44

  • iOS Home Screen Action

    iOS Home Screen Action

    Overview

    Khi sử dụng một số ứng dụng phổ biến như Zalo, Telegram hay YouTube, … trên các thiết bị iOS 13 trở lên, hẳn chúng ta đã từng nhìn thấy một số option mới khi long press ở Home Screen như hình dưới. Ở bài viết này, chúng ta sẽ cùng tìm hiểu cách để tạo ra những option đó.

    Các option hay shortcut này được gọi là Home Screen Actions (hay Home Screen Quick Actions), được hỗ trợ từ iOS 13 trở lên. Đúng như cái tên của nó, đây là những shortcut được thiết kế để thực hiện trực tiếp lựa chọn các action của ứng dụng tại màn hình Home thay vì phải mở và thao tác nhiều lần bên trong ứng dụng.

    Hãy lấy ví dụ như chúng ta có ứng dụng Safari, khi muốn mở Techover trên Safari ta có một số lựa chọn như sau:

    1. Mở Safari -> Tap vào thanh địa chỉ -> Nhập magz.techover.io -> ấn Go
    2. Mở Safari -> Tap New Tab -> Chọn BookMark -> Chọn magz.techover.io

    Có thể thấy, để thực hiện công việc trên chúng ta đều cần ít nhất 4 bước, lựa chọn đầu tiên còn yêu cầu người dùng nhập địa chỉ thủ công. Thay vì thế, ta có thể đơn giản hoá bằng cách đưa magz.techover.io vào một shortcut của Safari, chỉ với hai lần chạm, ta đã có thể mở trang web trên Safari một cách nhanh chóng.

    Home Screen Action có hai loại, Static và Dynamic:

    1. Static: Luôn cố định, không thể thay đổi tuỳ chỉnh được
    2. Dynamic: Có thể tuỳ chỉnh, thay đổi tuỳ theo hoạt động của ứng dụng

    Giờ chúng ta sẽ đi tìm hiểu, làm sao để cấu hình cho từng loại Home Screen Action.

    Home Screen Static Action

    Đối với Static Actions, chúng ta sẽ cài đặt ở trong Info.plist với cấu trúc sau:

    <key>UIApplicationShortcutItems</key>
    <array>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypeHome</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>Techover</string>
            <key>UIApplicationShortcutItemSubtitle</key>
            <string>Open Techover</string>
            <key>UIApplicationShortcutItemType</key>
            <string>Techover</string>
        </dict>
        <dict>
            <key>UIApplicationShortcutItemIconType</key>
            <string>UIApplicationShortcutIconTypeCompose</string>
            <key>UIApplicationShortcutItemTitle</key>
            <string>New</string>
            <key>UIApplicationShortcutItemSubtitle</key>
            <string>Create New Post</string>
            <key>UIApplicationShortcutItemType</key>
            <string>Create</string>
        </dict>
    </array>

    Trong đó:

    • Key UIApplicationShortcutItemIconType là define Icon cho Action, các bạn có thể tham khảo list value ở đây
    • Key UIApplicationShortcutItemTitle là define Title cho Action
    • Key UIApplicationShortcutItemSubtitle là define SubTitle, dòng chữ nhỏ phía dưới Title
    • Key UIApplicationShortcutItemType là một String dùng để định danh, phải là duy nhất, dùng để handle xử lý Action ở trong code.

    Sau khi setting xong, chúng ta sẽ có kết quả như sau:

    Home Screen Dynamic Action

    Đối với Dynamic Actions, chúng ta có thể thêm bất cứ chỗ nào bên trong xử lý của ứng dụng bằng đoạn code sau:

    let application = UIApplication.shared
    var shortcuts = application.shortcutItems
    shortcuts?.append(UIApplicationShortcutItem(type: "FavoriteAction",
                                                localizedTitle: "Home Screen Action",
                                                localizedSubtitle: "My first Technopedia post",
                                                icon: UIApplicationShortcutIcon(systemImageName: "star.fill"),
                                                userInfo: ["Marked Post": "Minhnn44-1" as NSSecureCoding]))
    shortcuts?.append(UIApplicationShortcutItem(type: "FavoriteAction",
                                                localizedTitle: "iOS Distribution Methods",
                                                localizedSubtitle: "My seconds Technopedia post",
                                                icon: UIApplicationShortcutIcon(systemImageName: "star.fill"),
                                                userInfo: ["Marked Post": "Minhnn44-2" as NSSecureCoding]))
    application.shortcutItems = shortcuts

    Trong đó, UIApplication.shared.shortcutItems là một [UIApplicationShortcutItem], mỗi một UIApplicationShortcutItem tượng trưng cho một Action ở Home Screen với các tham số khởi tạo:

    • type: String định danh của Action
    • localizedTitle: Title
    • localizedSubtitle: SubTitle
    • icon: icon
    • userInfo: Dictionary để lưu các thông tin cần truyền thêm

    Action Handle

    Việc handle khi người dùng ấn các action sẽ được thực hiện bên trong SceneDelegate như sau.
    Đối với trường hợp ứng dụng chưa được mở (not running), chúng ta sẽ handle bên trong hàm scene(_:willConnectTo:options:) như sau:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        /** Process the quick action if the user selected one to launch the app.
            Grab a reference to the shortcutItem to use in the scene.
        */
        if let shortcutItem = connectionOptions.shortcutItem {
            // Save it off for later when we become active.
            savedShortCutItem = shortcutItem
        }
    }

    Trong trường hợp ứng dụng đang ở background, chúng ta sẽ handle bên trong hàm windowScene(_:performActionFor:completionHandler:) như sau:

    func windowScene(_ windowScene: UIWindowScene,
                     performActionFor shortcutItem: UIApplicationShortcutItem,
                     completionHandler: @escaping (Bool) -> Void) {
        let handled = handleShortCutItem(shortcutItem: shortcutItem)
        completionHandler(handled)
    }
    
    func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
        /** In this sample an alert is being shown to indicate that the action has been triggered,
            but in real code the functionality for the quick action would be triggered.
        */
        switch shortcutItem.type {
        case "Techover":
            print("Techover Selected")
        case "Create":
            print("Create New Post Selected")
        case "FavoriteAction":
            if let markedPost = shortcutItem.userInfo?["Marked Post"] as? String {
                print("Open \(markedPost)")
            }
            break
        default:
            break
        }
        return true
    }

    Vậy là xong, nếu có thắc mắc hoặc góp ý thì các bạn comment phía dưới để mình giải đáp và cái thiện nhé!

    Authors

    MinhNN44

  • Hiểu và phân biệt các Distribution Methods

    Hiểu và phân biệt các Distribution Methods

    Overview

    Đối với lập trình viên iOS, việc archive và distribute app là một kỹ năng quan trọng cần phải có. Xcode cung cấp cho người dùng khá nhiều phương pháp để distribute một ứng dụng, việc hiểu và lựa chọn phương pháp phù hợp với từng yêu cầu sẽ giúp ta rất nhiều.

    Xcode cung cấp một số phương pháp Distribution như sau:

    1. App Store Connect
    2. Ad Hoc
    3. Enterprise
    4. Development
    Distribution Methods

    1. App Store Connect

    Đây là phương pháp Distribution được sử dụng nhiều nhất trong những dự án làm Product, khi phát triển các ứng dụng đã, đang và sẵn sàng đưa lên App Store. Phương pháp này sẽ tạo ra một bản Distribution nhằm mục đích upload trực tiếp ứng dụng lên App Store Connect. Xcode cho phép lựa chọn hỗ trợ upload tự động hoặc xuất ra .ipa file để người dùng upload thủ công. Sau khi upload ứng dụng lên App Store Connect, ta có thể test lại ứng dụng thông qua TestFlight và Submit For Review để chờ Apple review qua lần cuối trước khi chính thức đưa ứng dụng lên App Store.

    Lưu ý: Phương pháp này chỉ hỗ trợ các ứng dụng được build & distribute với certificate của tài khoản Apple Developer thông thường (99$).

    Có 2 lựa chọn: Upload tự động hoặc xuất .ipa

    2. Ad Hoc

    Phương pháp Ad Hoc có cùng một cơ chế và cấu hình như App Store Connect, mục đích là để kiểm thử ứng dụng mà không cần đến App Store Connect/TestFlight. Các cấu hình có phân chia Sandbox/Production như APNs sẽ được nhận Production khi sử dụng Ad Hoc. Ad hoc hỗ trợ xuất ra file .ipa có thể cài đặt trực tiếp vào iPhone thông qua Xcode hoặc iTunes, trình duyệt Safari (OTA). Có một số điểm mạnh của Ad Hoc có thể liệt kê:

    1. Tạo ra phiên bản hoàn toàn giống với App Store, thuận lợi cho việc test trước khi go live
    2. Không phụ thuộc vào App Store Connect: Không mất thời gian chờ Processing Binary, không cần phải invite user vào TestFlight
    3. Linh động và đa dạng trong việc cài đặt ứng dụng, hỗ trợ cả OTA

    Tuy vậy, Ad Hoc cũng có những hạn chế:

    1. Thiết bị iPhone cần đăng ký liên kết với tài khoản Apple Developer để có thể cài đặt được ứng dụng
    2. Mỗi tài khoản Apple Developer có giới hạn 100 thiết bị, Apple chỉ cho phép xoá mỗi năm một lần vào thời điểm Renew Payment
    3. Mỗi lần đăng ký thêm một thiết bị, cần build và distribute lại ứng dụng mới có thể cài được trên thiết bị mới
    4. Ứng dụng sẽ không thể sử dụng khi certificate/provisioning profile dùng để distribute hết hạn

    3. Enterprise

    Enterprise là phương pháp khác biệt nhất trong 4 phương pháp, việc đầu tiên cần phải kể đến là yêu cầu cần phải có tài khoản Apple Developer phải tham gia vào chương trình Enterprise (299$). Việc đăng ký tham gia chương trình Enterprise khá nhiều thủ tục và yêu cầu những thông tin khó tiếp cận hay sở hữu (DUNS). Bù lại, những lợi ích phương pháp này mang lại rất lớn.

    Đầu tiên, đó là việc Distribute ứng dụng đến người dùng một cách không giới hạn mà không cần sử dụng đến App Store. Mục đích của phương pháp Enterprise là tạo ra một bản ứng dụng với cấu hình Production với mục đích phát hành nội bộ bên trong một tổ chức, doanh nghiệp thay vì công khai trên App Store. Việc này sẽ đảm bảo tính bảo mật của doanh nghiệp, sự nhanh chóng và linh hoạt trong việc cập nhật vì không bị phụ thuộc vào App Store. Theo đó, sẽ có nhiều mục đích khác được sử dụng như phát hành game lậu, ứng dụng lậu, các ứng dụng vi phạm bản quyền hoặc bất cứ chính sách nào đó của App Store, hoặc đơn giản là vì không muốn phụ thuộc vào App Store.

    Ngoài việc phá bỏ các rườm rà liên quan đến thiết bị của Ad Hoc, Enterprise vẫn giữ được các điểm mạnh như hỗ trợ nhiều phương pháp cài đặt (Xcode, OTA, …). Tuy vậy, ứng dụng vẫn có thể không thể sử dụng nếu certificate/provisioning profile bị hết hạn.

    4. Development

    Development là phương pháp tạo một bản ứng dụng với môi trường Sandbox, có thể cài đặt với những tài khoản được đăng ký liên kết với Apple Developer Account dưới role Developer hoặc Admin. Đây là phương pháp thường sử dụng để phân phối bản build cho Tester trong giai đoạn IT khi không muốn tác động đến môi trường Production. Phương pháp này cũng có điểm hạn chế về việc phải build lại ứng dụng khi thêm một tài khoản mới. Phương pháp cũng hỗ trợ các hình thức cài đặt giống như Ad Hoc (Xcode, iTunes, OTA).

    Authors

    MinhNN44

  • Triển khai CD cho dự án phát triển Website với Gitlab-CI và AWS S3

    Triển khai CD cho dự án phát triển Website với Gitlab-CI và AWS S3

    Article overview

    Giả sử chúng ta phát triển một sản phẩm Website với ReactJS và sử dụng Static Website Hosting của AWS S3. Mỗi lần deploy đều cần thực hiện build source và upload manual.
    Mục tiêu là triển khai CD để thay thế cho các công việc manual không cần thiết và giảm thiểu các sai sót ngoài ý muốn.

    Tổng quan về các công nghệ sử dụng:

    • ReactJS Website
    • Gitlab-CI
    • AWS S3, AWS CLI
    • Môi trường MacOS

    Table of contents

    Chúng ta cần một số bước sau:

    • Liên kết và khởi tạo Runner với Gitlab repository
    • [Cài đặt và cấu hình môi trường tại thiết bị chạy service runner](## Cài đặt và cấu hình môi trường tại thiết bị chạy service runner)
    • [Cấu hình các job CI/CD với .gitlab-ci.xml và Gitlab-CI](## Cấu hình các job CI/CD với gitlab-ci.xml và Gitlab-CI)

    Cài đặt và cấu hình môi trường tại thiết bị chạy service runner

    Giả định ở thiết bị MacOS chạy service runner đã build được Website ReactJS, chúng ta sẽ skip qua phần cài đặt cho ReactJS. Đầu tiên, chúng ta cần cài đặt AWS CLI.
    Sau khi cài đặt xong, ta thực hiện config với thông tin của AWS User có quyền deploy lên AWS S3 với câu lệnh sau:

    $ aws configure
    AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
    AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    Default region name [None]: us-west-2
    Default output format [None]: json

    Cấu hình các job CI/CD với gitlab-ci.xml và Gitlab-CI

    Đầu tiên, thay vì upload bằng tay các file trong thư mục build/ chúng ta sẽ sử dụng command aws s3 sync như sau:
    aws s3 sync build/ s3://ww95 với ww95 là tên của Bucket đang Host Website.
    Sau đó, chúng ta sẽ cài đặt command package.json như sau:

      "scripts": {
        "build": "react-scripts build", // Build source code để deploy
        "deploy": "aws s3 sync build/ s3://ww95" // Thực hiện deploy lên S3
      },

    Tiếp đó, ta sẽ cấu hình .gitlab-ci.yml để hệ thống tự động deploy khi có thay đổi trên nhánh master.

    stages:
      - Deployment
    deploy:
      stage: Deployment
      before_script: []
      only:
          - master
      allow_failure: true
      script:
        - yarn install
        - yarn build
        - yarn deploy

    Tuy nhiên, khi chạy thực tế trên Gitlab-CI ta sẽ gặp lỗi sau:

    Treating warnings as errors because process.env.CI = true.
    Most CI servers set it automatically. 
    Failed to compile.

    Để giải quyết vấn đề này ta phải setting lại process.env.CI = false bằng hai cách sau:

    • Thay đổi cấu hình .gitlab-ci.yml từ yarn build -> CI=false yarn build.
    • Cài đặt biến môi trường trong Gitlab-CI như ảnh sau

    Sau đó merge code vào master, và hưởng thành quả. Từ giờ các bạn không cần phải deploy bằng tay nữa rồi, chúc các bạn may mắn.

    Authors

    MinhNN44