Category: iOS

  • 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/

  • 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ó.

  • 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!

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

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

    Lời mở đầu

    Như các bạn đã biết App store là chợ ứng dụng của Apple, nơi chứa tất cả những ứng dụng trên toàn thế giới được apple công nhận. Tất cả những ứng dụng muốn đưa lên Appstore đều phải trải qua những bước kiểm duyệt nghiêm ngặt của Apple. Mà trong các công ty chỉ 1 số người có quyền có thể upload ứng dụng lên store thôi. Vì vậy mình muốn viết bài này để chia sẻ cho tất cả các anh em cùng biết.

    Giải thích các thuật ngữ

    Trước khi đi vào chi tiết mình muốn các bạn nắm rõ các thuật ngữ trước, để đảm bảo các bạn hiểu rõ vấn đề và giúp mọi việc trở nên dễ dàng hơn

    .ipa (iOS App store Package) là file lưu giữ ứng dụng iOS. Nó chỉ có thể được cài đặt trên thiết bị iOS.

    Archive: Là hành động tạo file để upload lên app store.

    Apple ID: là tài khoản của người dùng được Apple cung cấp để đăng nhập trên các sản phẩm của Apple. Đối với lập trình viên chúng ta cần sử dụng tài khoản thông qua trang App Developer. Để có thể thành apple developer bạn phải nâng cấp tài khoản Apple ID của mình. Và chi phí cho tài khoản cá nhân là 99$/năm. Hoặc nếu bạn làm việc trong công ty bạn cũng có thể yêu cầu người có quyền mời bạn vào dự án và cấp quyền cho bạn.

    Certificate: Đây là chứng chỉ của Apple. Apple dùng nó để xác nhận xem app đó có thể hoặc còn hoạt động được nữa hay không. Thường thời hạn của các Certificate sẽ là 1 năm. Có 2 kiểu certificate đó là:
    – Development certificate: Nó giúp bạn build app trên thiết bị thật đã được đăng ký(Giới hạn 100 devices)
    – Distribution certificate: Nó giúp XCode xác nhận và có thể đẩy file .ipa lên app store connect.

    Bundle ID: Nó là duy nhất và định danh ứng dụng của bạn

    Device identifiers: là những device được đăng kí sử dụng trong môi trường phát triển(development)

    Provisioning profiles: Nó cho phép cài đặt ứng dụng trên môi trường Development và Distribution. Nó bao gồm cả signing certificates, device identifiers và bundle ID.
    – Development: Nó giúp bạn có thể cài ứng dụng trên các Device đã được đăng kí
    – Distribution: Nó dùng để đưa app lên Itunes connect -> đưa lên store

    App store connect: Đây là trang quản lý ứng dụng của bạn. Đây là nơi mà bạn chuẩn bị các tài liệu, hình ảnh(metadata) và các thông tin liên quan tới ứng dụng của bạn. Nó giúp bạn gửi thông tin này đến bên kiểm duyệt ứng dụng của apple và cho biết trạng thái ứng dụng của bạn.

    Các bước chuẩn bị

    Để tải ứng dụng lên App store connect chúng ta cần phải chuẩn bị các thông tin liên quan đến ứng dụng như: App ID, Certificates, Provisioning profile …

    Bước 1: Chuẩn bị một tài khoản apple id đã được nâng cấp lên Apple Developer.

    Để có thể tải ứng dụng lên Store. Apple yêu cầu người dùng phải có tài khoản Developer. Để trở thành Apple Developer chúng ta có 2 cách:

    • Nâng cấp tài khoản Apple Id thường với giá 99$/năm cho tài khoản cá nhân.
    • Sử dụng tài khoản Apple Developer của người khác mời vào dự án đang làm.

    Sau khi hoàn tất công việc trên chúng ta đăng nhập vào trang Apple Developer

    Bước 2: Tạo distribution certificate

    Chọn mục Certificates, IDs & Profiles để mở trang quản lý.

    Để tạo mới certificates ta bấm vào dấu +

    Nếu bạn đung sử dụng XCode có version >= 11 thì chọn Apple Distribution, nêu không thì chọn iOS Distribution( App store Ad Hoc). Sau đó bấm tiếp tục.

    Đến đây Apple bắt chúng ta phải tải lên file Certificate signing request. Để tạo file này chúng ta cần mở ứng dụng Keychain Access để tạo.

    Một popup sẽ hiện ra, bạn hãy điền đầy đủ thông tin như dưới hình và chọn Save to disk

    Điền đầy đủ thông tin như hình và bấm Continue(tiếp tục). Lúc này nó sẽ hiện popup cho chúng ta lưu file này.
    Lưu ý: Bạn nên tạo các thư mục để quản lý các file certificate cho mỗi một ứng dụng. Nhằm mục đích sau này có thể cần đến.

    Bây giờ hãy quay lại trang web và chọn file Certificate signing request vừa tạo. Và bấm Continue( tiếp tục).
    Vậy là ta đã tạo được Distribution Certificate. Với các Certificate khác, các bạn cũng làm tương tự.

    Tiếp theo chúng ta cần mở certificate vừa tạo để tải certificate về cài đặt trên Keychain Access của máy đang sử dụng.

    Bấm vào Download để tải về certificate này. Tải về xong bạn bấm double click để cài đặt nó vào Keychain Access.

    Lưu ý: Bạn nên export private key của certificate này và lưu nó cẩn thận. Nếu không có private key các device khác sẽ không sử dụng được Certificate này.

    Bước 3: Tạo App ID

    Với mỗi một ứng dụng trên store đều phải có một App Id nhằm định danh cho nó khi ở trên Store.

    Mở tab Identifiers > Bấm dấu +

    Một trang mới xuất hiện, bạn chọn App IDs và bấm Continue(Tiếp tục)

    Trang đăng ký App ID xuất hiện Bạn hãy điền các thông tin ứng dụng của bạn vào các ô trống và bấm Continue (Tiếp tục) để hoàn thành việc tạo App ID.

    Bước 4: Tạo Provisioning Profile – Distribution

    Mở tab Profiles > Chọn dấu + để tạo mới provisioning profile

    Lúc này màn hình Register A New Provisioning profile được hiển thị. Bạn muốn tạo loại profile nào thì tích vào mục đó. Trong trường hợp này chúng ta cần tạo profile để upload app lên store vì vậy chúng ta sẽ chọn mục Distribution – App Store như hình dưới và bấm Continue(Tiếp tục):

    Màn hình Generate A Provisioning Profile được hiển thị:
    Bạn muốn tạo Profile cho ứng dụng nào thì chọn AppID của ứng dụng đó và bấm Continue

    Tiếp đến bạn cần chọn Distribution certificate mà bạn đã tạo ở Bước 2. Trong trường hợp bạn tạo nhiều distribution Certificate nó sẽ hiển thị dưới dạng list cho bạn chọn.
    Chọn certificate và bấm Continue

    Vậy là bạn đã tạo thành công profile cho distribution phục vụ cho việc tải ứng dụng lên App store connect.

    Tải về profile bạn vừa tạo và bấm double click để cài đặt.

    Tổng kết

    Trong bài viết này mình đã giúp các bạn làm quen với một số keywords và cách để tạo các file certificates, app id, profile …. Đó là những bước đầu để chuẩn bị cho việc đưa ứng dụng của các bạn lên store.

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

  • iOS/Auto Layout – Phần 7: UI Debugging

    iOS/Auto Layout – Phần 7: UI Debugging

    Lời mở đầu

    Giao diện người dùng(User Interface) của một ứng dụng hiện đại là một cấu trúc phức tạp. Nó liên quan đến các thành phần UI, bố cục và bản thiết kế dẫn đến quá trình gỡ lỗi(debugging) trở nên khá phức tạp. Việc biết các công cụ có sẵn giúp bạn giảm hàng giờ ngồi gỡ lỗi và làm cho việc tìm ra vấn đề trở nên đơn giản hơn.

    Trong bài viết này mình sẽ giới thiệu về các công cụ gỡ lỗi giao diện do Apple cung cấp.



    1. View Hierarchy Debugger

    View hierarchy debugger cung cấp khả năng kiểm tra và hiểu hệ thống phân cấp UI. Các thông tin bao gồm Phân cấp ứng dụng(app’s hierarchy), các view, các ràng buộc và view controller được hiển thị dưới dạng danh sách phân cấp. Object inspector và size inspector và thậm chí là mô hình 3D có thể tương tác được. Xịn sò vl 😀

    Nó được đặt ở thanh debug. Để mở tính năng này bạn bấm vào biểu tượng được khoanh đỏ hình dưới đây. Khi ở chế độ này XCode sẽ dừng ứng dụng của bạn ở trạng thái hiện tại.

    Mở debug view hierarchy

    Tính năng này có thể sử dụng được cho iOS, tvOS, macOS, máy ảo(simulator) và thiết bị thật.

    2. lldb

    Nếu bạn thích console hơn là các công cụ trực quan thì UIKit cũng cung cấp các APIs logging rất ổn. Có một vấn đề nhỏ đó là những APIs private. Nhưng chúng ta cũng không dùng debug cho các bản release.

    -[UIView recursiveDescription]

    Nó sẽ hiển thị dưới dạng như sau:

    <UIView: 0x7f9b29c08770; frame = (100 100; 50 50); layer = <CALayer: 0x600001b516e0>>

    Nó sẽ hiển thị các thông tin bố cục, bản vẽ, CALayer và các trường cụ thể bổ sung.

    -[UIViewController _printHierarchy]

    Tương tự UIViewController cũng hiển thị các thông tin như dưới đây:

    (lldb) po [viewController _printHierarchy]
    <UISplitViewController 0x7fb23a605920>, state: disappeared, view: <UILayoutContainerView 0x7fb23d80dc60> not in the window
    | <UINavigationController 0x7fb23b817000>, state: disappeared, view: <UILayoutContainerView 0x7fb23d80e280> not in the window
    |    | <MasterDetailTemplate.MasterViewController 0x7fb23a606140>, state: disappeared, view: <UITableView 0x7fb23b05ac00> not in the window
    + <UINavigationController 0x7fb23b01d600>, state: appeared, view: <UILayoutContainerView 0x7fb23a503150>, presented with: <_UIFullscreenPresentationController 0x7fb23d808f20>
    |    | <MasterDetailTemplate.ModalViewController 0x7fb23a409660>, state: appeared, view: <UIView 0x7fb23a51dfc0>
    • | là biểu tượng cho biết các child view controllers
    • + đại diện cho Modal presentation

    _printHierarchy là phương thức đặc biệt hữu ích khi được gọi triên rootViewCOntroller của UIWindow:

    po [[[UIWindow keyWindow] rootViewController] _printHierarchy]

    Các phương thức ở trên đều dành cho Objective-C. Nếu bạn đang sử dụng swift thì cần tạo một số extension như sau:

    #if DEBUG
    
    extension UIWindow {
    
        class var key: UIWindow {
            let selector: Selector = NSSelectorFromString("keyWindow")
            let result = UIWindow.perform(selector)
            return result?.takeUnretainedValue() as! UIWindow
        }
    }
    
    extension UIView {
        
        var recursiveDescription: NSString {
            let selector: Selector = NSSelectorFromString("recursiveDescription")
            let result = perform(selector)
            return result?.takeUnretainedValue() as! NSString
        }
    }
    
    
    extension UIViewController {
        
        var printHierarchy: NSString {
            let selector: Selector = NSSelectorFromString("_printHierarchy")
            let result = perform(selector)
            return result?.takeUnretainedValue() as! NSString
        }
    }
    
    #endif

    Khi tạo xong các extension này chúng ta có thể sử dụng nó ở console bằng cách:

    po view.recursiveDescription
    po UIWindow.key.rootViewController!.printHierarchy

    3. Instruments – Core Animation

    Kiểm tra cấu trúc UI để tối ưu hóa hiệu suất. Điều đầu tiên chúng ta cần làm là đo khung hình mỗi giây. Con người có cảm nhận ứng dụng chạy mượt khi nó có thể hoạt động ở ~ 60FPS (Frames per second). Và đó chính là mục đích của chúng ta.

    Lưu ý: Nó không hoạt động trên Simulator

    Để mở chúng ứng dụng này chúng ta sử dụng tính năng search(command + space) đánh từ khóa Instruments:

    Mở ứng dụng Instruments lên và chọn core animation

    Core Animation đo hiệu năng đồ họa của ứng dụng. Trong ví dụ này, mình chỉ tập trung vào rendering performance. Ứng dụng của mình hiện tại là một UITableView với các custom cell. Bây giờ nó chỉ hiển thị section và row.

    Khi cuộn mình nhận thấy nó hơi giật. Cấu hình ứng dụng với Instruments cho thấy hiệu suất cách xa mức 60 FPS. Khi cuộn (từ giây thứ 4->23) hiệu suất giảm xuống còn ~44 FPS giảm 16 so với mục tiêu đặt ra.

    Tôi nghi ngờ vấn đề ở đây liên quan đến cell rendering. Với tính năng Color Blended Layers debug được bật, ta có thể hiểu rõ hơn về những gì đang xảy ra.

    Color blended layer chỉ ra các view layers đang được vẽ chiingf lên nhau.

    Để hiểu tại sai nó lại là vấn đề, chúng ta cần biết hình ảnh cuối cung được tạo ra như thế nào. Pixel cuối cùng mà người dùng nhìn thấy là một bố cục pha trộn các pixel trên cùng và bên dưới cùng tọa độ. Nó có nghĩa là mọi view layer phải được render để tạo hình ảnh cuối cùng. Khi view layer mờ đục, blending(Pha trộn) có thể được tối ưu hóa bằng cách không vẽ các layer bên dưới.

    Để bẻ cong góc và đổ bóng cho View, tôi sử dụng CALayer properties:

    let cardView = UIView()
    cardView.layer.cornerRadius = 10.0
    cardView.layer.shadowColor = UIColor.black.cgColor
    cardView.layer.shadowOpacity = 0.5
    cardView.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
    cardView.layer.shadowRadius = 4.0

    Điều này làm cho các phần view của tôi có các layer trong suốt, nó gây ra việc blending không cần thiết. Bởi vì background color được tô, nên rendering có thể được tối ưu. Ta sẽ set một solid background cho View của mình và sử dụng hình nên mà không có alpha:

    let cardView = UIImageView(image: UIImage(named: "cardBackground"))

    Đây là một thủ thuật đơn giản giúp chúng ta giải quyết việc bị blending.

    Lúc này hiệu suất của ứng dụng đã tốt hơn, cho cảm giác cuộn rất mượt.

    Bạn cũng có trể truy cập nhanh vào Color Blended Layers từ iOS Simulator.

    4. Pixie

    Pixie giúp bạn thực hiện thiết kế một cách hoàn hảo. Công cụ này cho phép bạn kiểm tra từng pixel của ứng dụng theo đúng nghĩa đen.

    Pixie có thể được tải từ website của Apple. XCode > Open Developer Tool > More Developer Tools… trong công cụ bổ xung cho XCode.

    Tổng kết

    Mình hi vọng bài này giúp các bạn có thể giúp các bạn tận dụng tool debug của apple để làm việc tốt hơn 😀




  • SQLite with FMDB in Swift (P2)

    SQLite with FMDB in Swift (P2)

    Nội dung bài viết:

    • Truy vấn insert
    • Truy vấn select
    • Truy vấn delete
    • Truy vấn update

    Truy vấn INSERT

    • Câu truy vấn này dùng để thêm bản ghi vào table
    • Có 2 cách để viết 1 câu truy vấn insert.
      Cách 1:

    Cách 2: Trong trường hợp bạn tạo 1 bản ghi mới đầy đủ các trường của bảng ghi, thì k cần liệt kê cụ thể tên các trường như cách 1:

    • Để thực hiện truy vấn INSERT vào table Book ở bài viết trước, việc đầu tiên là viết 1 câu truy vấn:
    let query = "insert into Book (name, author, price) values ('Kill the mockingbird', 'HarperLee', 85000)"

    Đây là 1 câu truy vấn thực hiện sự thay đổi đến database, nên ta vẫn sử dụng lệnh executeUpdate(…) để thực hiện câu query:
    Ở đây, vì table Book được khởi tạo với thuộc tính id tự động tăng, nên ta có thể bỏ qua việc khai báo id.

    func insertBook() {
        if openDatabaseConnectionAtPath(path: getDatabasePath()) {
            let query = "insert into Book (name, author, price) values ('Kill the mockingbird', 'HarperLee', 85000)"
            do {
               try database.executeUpdate(query, values: nil)
            } catch let err {
               print("Insert failed, error: \(err.localizedDescription)")
            }
        }
        database.close()
    }

    Ở đây, table Book có cả 4 trường đều khác nil, tuy nhiên trường id và price có thuộc tính default nên có thể không cần khai báo trong câu lệnh insert, nhưng nếu bạn insert thêm 1 bản ghi mà không có trường name hay author thì FMDB sẽ trả về lỗi và không thể insert được.

    Note: Bạn có thể thực hiện nhiều câu truy vấn cùng 1 lúc, bằng cách gộp chúng vào 1 câu truy vấn chung, mỗi câu truy vấn nhỏ ngăn cách nhau bằng 1 dấu ;

    • Ngoài ra, còn có 1 cách thực hiện câu lệnh executeUpdate thuận tiện hơn bằng cách sử dụng thuộc tính values như sau:
    • Ở đây, nameauthor không được cụ thể ngay từ câu truy vấn, mà được để 1 dấu ? có tác dụng như 1 placeholder để truyền trường name, author vào sau.
    • Khi thực hiện lệnh executeQuery, điền lần lượt các giá trị muốn truyền tương ứng vào các dấu ? vào thuộc tính values.

    Truy vấn SELECT:

    • Câu truy vấn này dùng để lấy các bản ghi từ table.
    • Cách viết câu truy vấn Select:
      + SELECT column1, column2,… FROM table_name; // Lấy 1 vài trường cụ thể
      + SELECT * FROM table_name; // Lấy tất cả các bản ghi
      + Ngoài ra có thể kết hợp thêm Where, Order by,… vào câu truy vấn để lọc các bản ghi
    • Ví dụ thực hiện câu truy vấn lấy tất cả các sách trong table Book:
    Lấy tất cả các bản ghi và sắp xếp theo giá tăng dần
    Lấy tất cả các tên tác giả mà có trường id = 2
    • Câu truy vấn select k làm thay đổi DB, nên sử dụng lệnh executeQuery(…) để thực hiện.
    • Câu truy vấn này trả về 1 tập dữ liệu FMResultSet, từ đó convert về kiểu dữ liệu mà ta mong muốn.

    Truy vấn DELETE

    • Dùng để xóa bản ghi đang có trong table.
    • Cách viết câu truy vấn delete:
      DELETE FROM table_name WHERE condition;
    • Ví dụ thực hiện câu truy vấn delete:

    Truy vấn UPDATE:

    • Dùng để thay đổi giá trị của bản ghi đang có trong table.
    • Cách viết câu truy vấn:
      UPDATE table_name SET column1 = value1, column2 = value2, … WHERE condition;
    • Ví dụ thực hiện câu truy vấn:

    Kết luận:

    • Nên define các trường thành Constant để tránh gõ nhầm khi viết câu truy vấn.
    • Sử dụng lệnh executeUpdate để thực hiện thay đổi DB, dùng lệnh executeQuery để thực hiện việc lấy các bản ghi.
    • Các câu lệnh có thể gộp vào với nhau, nhưng ngăn cách với nhau bằng dấu ;.
    • Luôn đóng liên kết tới DB sau khi thực hiện xong câu truy vấn.
  • iOS/Auto Layout – Phần 6: Những trường hợp đặc biệt trong Auto Layout

    iOS/Auto Layout – Phần 6: Những trường hợp đặc biệt trong Auto Layout

    Lời mở đầu

    Trong bài viết này mình sẽ chia sẻ về cách tạo constraint cho một số view đặc biệt và những lưu ý khi chúng ta tạo các constraint cho các view.



    1. UISrollView

    UIScrollView là một view tạo constraint khá là phức tạp và khó hiểu với người mới. Thật không may là nó lại được sử dụng trong khá nhiều trong ứng dụng của bạn. Nhưng nó sẽ trở nên đơn giản hơn nếu bạn hiểu được bản chất của nó.

    UIScrollView là một view mà nó cho phép cuộn và phóng to nội dung của nó.

    Khi tạo constraint cho scroll view bạn cần tạo constraint cho 2 bước:
    – Bước 1 là cần phải tạo các constraints cố định khung(kích thước và vị trí) của scroll view so với super view của nó.
    – Bước 2 là cần phải tạo các constraint cho các view trong scrollview.

    1.1 Tạo constraint cho UIScrollView không sử dụng Content Layout Guide

    Sử dụng trong trường hợp bạn sử dụng bản XCode version < 11 hoặc bạn sử dụng XCode 11 nhưng không muốn dùng Content Layout Guide của UIScrollView.

    Giờ chúng ta kéo 1 scrollView vào và tạo constraint nó với superview như hình dưới:

    Vậy là ta đã hoàn thành bước 1: tạo constraint xác định vị trí, kích thước của Scroll View. Trong trường hợp này mình đang để scrollview có kích thước bằng kích thước của màn hình.

    Tiếp theo chúng ta đến bước 2 tạo constraint cho các view trong scroll view.
    Chúng ta cần kéo vào 1 UIView để chứa tất cả các View.
    Lúc này chúng ta sẽ tạo constraint cho View này bằng cách kéo chuột phải từ view đó vào scrollview và giữ Shift để add nhiều constraint như hình dưới:

    Việc Fix equal height, equal width với scroll view giúp các bạn xác định vị trí cũng như kích thước của nội dung scrollview.

    Giờ chúng ta có thể kéo các View mình muốn vào trong view này và sắp xếp chúng theo ý muốn. Ở trường hợp này mình muốn kéo vào 1 label và 1 button. Nên mình sẽ constraint theo như hình phía dưới.

    Tiếp theo, do trong trường hợp này mình làm scrollview cuộn theo chiều dọc nên mình sẽ bỏ constraint equal height của view với scrollview đi. Khi đó ta cần sửa lại chỉ số constraint bottom của button sao cho phù hợp với thiết kế, giả sử trường hợp này mình sẽ để là 40 points. Ta sẽ được kết quả như hình:

    Tiếp đến các bạn cần tìm constraint bottom của View Content và sửa lại giá trị của nó về giá trị mình mong muốn( thường sẽ để là 0) Constraint này đại diện cho khoảng cách bottom giữa scrollview và contentview khi mà content view có size lớn hơn size của scroll view.

    Vậy là xong.
    Bước cuối cùng chúng ta test lại scroll view bằng cách set 1 đoạn text dài cho Label.
    kết quả sẽ được như hình dưới:

    1.2 Tạo constraint cho UIScrollView với Content Layout Guide

    Từ XCode 11 trở đi Apple đã cho thêm 1 thuộc tính Content Layout Guide cho UIScrollView giúp mọi người tạo constraint cho nó 1 cách dễ dàng hơn. Khi bạn kéo vào 1 UIScrollView ở XCode 11 nó sẽ mặc định được bật tính năng này. Khi này bên trong scroll view sẽ có 2 layout guide là Content Layout guide và Frame Layout Guide

    Nó khác với cách tạo 1.1 là thay vì nó constraint trực tiếp với ScrollView thì nó lại constraint với các Layout guide của scroll view.

    Content Layout guide: Chọn ContentView sử dụng chuột phải kéo vào Content layout guide và tạo các constraint ContentView với content layout guide leading, top, trailing, bottom như sau:

    Frame Layout guide: Chọn content View sử dụng chuột phải kéo vào Frame Layout guide và tạo các constraint equal width nếu muốn cuộn dọc hoặc equal height nếu muốn cuộn ngang.

    Còn lại chúng ta vẫn làm giống ở mục 1.1.

    2. UILabel

    Thông thường 1 UILabel chỉ cần 2 constraint để xác định được vị trí cũng như kích thước của nó. Thế nên rất nhiều bạn khi tạo constraint cho UILabel thường bị thiếu constraint, dẫn đến trường hợp khi build app lên bị vỡ layout.

    2.1 Nên tạo ít nhất 4 Constraint cho UILabel

    UILabel là view có thể tự thay đổi kích thước của nó dựa trên content size (text, font ..) của nó. Vì vậy nếu bạn không tạo đủ constraint cần thiết cho UILabel trong một số trường hợp nội dung của UILabel sẽ không hiển thị đúng như mong đợi.

    Nếu bạn chỉ sử dụng 2 constraint sẽ dẫn đến trường hợp khi nôi dung của label dài ra nó sẽ bị tràn ra ngoài màn hình.

    2.2 Nên set thêm constraint height cho các UILabel nếu nó làm nhiệm vụ giữ trục phát triển View

    Trong trường hợp UILabel làm nhiệm vụ giữ trục của view mà nó được gán giá trị empty sẽ dẫn đến trường hợp bị vỡ layout vì khi UILabel có text = “” (empty) height của nó sẽ = 0 vì thế ta cần tạo constraint cho nó và để giá trị là greater than or equal (>=). Khi đó các label giữ trục của view sẽ không làm vỡ layout nữa vì giá trị height nhỏ nhất của nó hiện tại không phải là 0 nữa.

    Các bạn theo dõi sự khác nhau của 2 hình dưới đây.

    Có set height >=
    Không set height

    3. UIButton

    UIButton cũng là một View có thể tự thay đổi kích thước dựa trên nội dung của nó. Vì vậy chúng ta cũng cần lưu ý khi tạo constraint cho nó. Đối với buton chúng ta chỉ cần 2 constraint là có thể xác định vị trí cũng như kích thước của nó. Nhưng để nó có thể hiển thị đúng trong nhiều trường hợp thì chúng ta cần nhiều hơn 2 constraint.

    Không nên set width cho button có chứa text

    Vì UIButton nó sẽ tự thay đổi kích thước theo nội dung bên trong, nên khi text dài ra hoặc ngắn đi nó sẽ tự thay đổi kích thước của button cho phù hợp. Nếu chúng ta gán constraint width cho nó thì dễ dẫn đến trường hợp khi text dài ra button sẽ hiển thị mất chữ.
    Vì vậy thay vì các bạn set constraint width cho UIButton để đạt được kết quả như file design thì chúng ta sẽ sửu dụng thuộc tính insets để căn chỉnh khoảng cách nội dung của UIButton.

    Ở đây mình sử dụng content insets để căn khoảng cách của nội dung button với các cạnh của nó.

    4. Priority: Độ ưu tiên khi các View

    Với mỗi view đều có 2 loại Priority đó là:
    – Content Hugging Priority (Vertical, horizontal)
    – Content Compression Resistance Priority (Vertical, horizontal)

    Content Hugging Priority

    Là độ ưu tiên ôm nội dung của View. View nào có độ ưu tiên của nó lớn hơn thì nó sẽ ưu tiên nội dung của view đó hiển thị đúng với các chỉ số. Các view lân cận có độ ưu tiên nhỏ hơn sẽ phải tự dãn content để lấp dầy khoảng trống còn lại theo chiều ngang hoặc dọc.
    Nó sẽ hoạt động nếu nội dung của các chiều đang không lấp đầy khoảng trống có sẵn trong super view.

    Theo dõi hình ảnh dưới đây để hiểu rõ hơn:

    Hình này Label có Hugging priority lớn hơn của Button.
    Vì vậy nó sẽ ưu tiên ôm nội dung của Label và dãn button.
    Trường hợp này thì Button có Hugging priority lớn hơn của Label.
    Nên nó sẽ ưu tiên ôm nội dung của Button và dãn Label.

    NOTE: Nếu Priority của 2 View bằng nhau nó sẽ sảy ra lỗi constraint. Vì vậy các bạn cần xác định được View nào cần độ ưu tiên cao hơn ở các trường hợp khác nhau.

    Tuy nhiên nó chỉ đúng khi nội dung của các view không chiếm hết khoảng không còn lại ở super view theo chiều của priority.
    Khi nội dung của các view vượt quá khoảng không có sẵn thì chúng ta cần sử dụng Content compression resistance priority để giải quyết.

    Content compression resistance priority

    Đây là thuộc tính thể hiện độ ưu tiên chống lại việc nén nội dung của View. Có nghĩa là View nào có priority lớn hơn sẽ được ưu tiên hiển thị nội dung của nó để lấp khoảng trống còn lại trong super view trước, sau đó nếu còn khoảng trống thì các view còn lại có priority nhỏ hơn sẽ được lấp vào, nếu hết khoảng trống nó có thể sẽ biến mất nếu không có constraint.priority lớn hơn(Priority của các constraint mặc định là 1000).
    Priority này sẽ hoạt động khi nội dung của các view được lấp đầy hoặc vươti quá size của super view.

    Theo dõi hình dưới đây để hiểu rõ hơn:

    Độ ưu tiên chống nén của UILabel cao hơn Button nên nội dung của button sẽ không hiển thị đầy đủ
    Trong trường hợp này độ ưu tiên chống nén nội dung của UILabel thấp hơn nên Nội dung của button được hiển thị đầy đủ, còn UILabel trong trường hợp này sẽ mất chữ nếu có line = 1. Vì vậy ở đây mình để line = 0 để label tự co dãn

    Tổng kết

    Mình hi vọng bài viết sẽ giúp cho các bạn có thêm 1 chút tự tin khi làm việc với Auto Layout 😀



  • iOS/Auto Layout – Phần 5: Stack View

    iOS/Auto Layout – Phần 5: Stack View

    Lời mở đầu

    Bài viết này mình sẽ giới thiệu với các bạn về một trường hợp đặc biệt trong Auto Layout đó là Stack View. Stack view là một công cụ mạnh mẽ để giúp bạn nhanh chóng cũng như dàng thiết kế giao diện người dùng của bạn.



    1. Stack view

    Stack view cho phéo bạn tận dụng sức mạnh của Auto Layout, tạo giao diện người dung có thể tự động thích ứng với hướng của thiết bị(Device’s orientation), kích thước màn hình, và mọi thay đổi trong không gian có sẵn. Stack view quản lý layout của tất cả các Views trong nó bằng thuộc tính arrangedSubviews. Độ chính xác của layout phụ thuộc vào các thuộc tính của nó như là: Axis, alignment, distribution, spacing và các thuộc tính khác.

    Hình ảnh mô tả các thuộc tính đặc trưng của stack view

    Các thuộc tính của stack view

    Axis: Định nghĩa trục phát triển của stack view. Có 2 kiểu stack view đó là Vertical và horizontal.

    Alignment: Định nghĩa việc căn chỉnh của các View được sắp xếp vuông góc với trục của stack view. Nó có các giá trị như sau:

    UIStackView.Alignment.fill: Làm cho các View bên trong stack view lấp đầy khoảng trống có sẵn trong stack view.

    UIStackView.Alignment.top: Dùng cho horizontal stack view, nó căn chỉnh cạnh trên của các View trong stack view theo cạnh trên của của nó.

    UIStackView.Alignment.leading: Dùng cho vertical stack view. Nó căn chỉnh các cạnh trái của các view trong stack view với cạnh trái của nó.

    UIStackView.Alignment.firstBaseline: Dùng trong horizontal stack view. Nó căn chỉnh các view trong stack view dựa trên đường baseline đầu tiên của chúng.

    UIStackView.Alignment.lastBaseline: Dùng cho horizontal stack view. Nó căn chỉnh các view trong stack view dưa trên đường baseline cuối cùng của chúng.

    UIStackView.Alignment.center: Nó căn giữa các view trong stack view theo chiều của nó.

    UIStackView.Alignment.trailing: Dùng cho vertical stack view, căn các cạnh phải của view trong stack view với cạnh phải của stack view.

    UIStackView.Alignment.bottom: Dùng cho horizontal stack view. Căn chỉnh các view trong stack view với cạnh dưới của nó.

    Distribution: là thuộc tính xác định bố cục của các view được sắp xếp theo chiều của stack view.

    Các thuộc tính của nó như sau:

    UIStackView.Distribution.fill: Nó giúp các view trong stack view lấp đầy không gian có sẵn trong stack view. Khi các view không xếp vừa stack view, nó sẽ thu nhỏ các view dựa trên độ ưu tiên của kháng nén của chúng (Compression resistance priority). Nếu các views được sắp xếp không lấp đầy được stack view, nó sẽ kéo dài các view theo hugging priority của chúng.

    UIStackView.Distribution.fillEqually: Thay đổi kích thước các view bên trong stack view sao cho tất cả các view có cùng kích thước theo chiều của nó và lấp đầy không gian có sẵn theo chiều của stack view. Ví dụ: Horizontal stack view thì các view sẽ bằng nhau về chiều rộng, vertical stack view thì bằng nhau về chiều cao.

    UIStackView.Distribution.fillProportionally: Thay đổi kích thước của các view trong stack view sao cho chúng lấp đầy các không gian có sẵn theo trục của stack view. Các view được thay đổi kích thước theo tỉ lệ dựa trên kích thước nội dung bên trong của chúng dọc theo chiều của stack view.

    UIStackView.Distribution.equalSpacing: Các view trong stack view sẽ được sắp xếp sao cho khoảng cách giữa chúng bằng nhau. Nếu các view được sắp xếp không vừa stack view nó sẽ thu nhỏ các View dưa trên độ ưu tiên kháng nén của chúng(Compression resistance priority).

    UIStackView.Distribution.equalCentering: Làm cho các view strong stack view được sắp xếp sao cho chúng có khoảng cách từ trung tâm bằng nhau theo chiều của stack view. Trong khi duy trì thuộc tính spacing(khoảng cách) giữa các view. Nếu các view được sắp xếp không vừa stack view, nó sẽ thu hẹp khoảng cách cho đến khi đạt khoảng cách tối thiểu được xác định bới thuộc tính khoảng cách của nó. Nếu các view vẫn không phù hợp, stack view sẽ thu nhỏ các view bên trong nó dựa trên thuộc tính Compression resistance priority.

    Spacing: Là thuộc tính định nghĩa khoảng cách các cạnh liền kề của các view trong stack view.

    Để làm việc với stack view hiệu quả các bạn cần hiểu các thuộc tính của nó. Khi bạn thành thạo nó rồi sẽ giúp bạn tiết kiệm khá nhiều thời gian khi auto layout.

    2. Ví dụ đơn giản về Stack View

    Ở ví dụ này mình sử dụng stack view bố trí layout theo hàng dọc để xắp xếp Label, ImageView và Button.

    2.1 Mô tả Views và Constraints

    Sử dụng Interface Builder và kéo vào một vertical stack view. Sau đó các bạn kéo các Label, Image View và Button vào trong stack view đó như hình dưới.

    Lúc này stack view của chúng ta chưa có constraint nên chúng ta cần gán constraint cho stack view như sau:

    1. Stack View.Leading = Superview.LeadingMargin
    2. Stack View.Trailing = Superview.TrailingMargin
    3. Stack View.Top = Top Layout Guide.Bottom + Standard
    4. Bottom Layout Guide.Top = Stack View.Bottom + Standard

    Việc này giúp cho stack view của chúng ta xác định được vị trí và kích thước của nó. Trong trường hợp này stack view của bạn sẽ có kích thước full màn hình.

    2.2 Cài đặt các thuộc tính cho stack view

    Mở tab Attributes Inspector và set các thuộc tính cho stack view như sau:

    StackAxisAlignmentDistributionSpacing
    Stack viewVerticalFillFill8
    1. Axis: Đại diện cho chiều hướng của stack view, Vertical(xếp theo chiều dọc)
    2. Alignment: Đại diện cho việc căn chỉnh, Fill(Điền đầy stack view theo chiều của nó)
    3. Distribution: Đại diện cho phân chia các view trong stack view. Ta gán giá trị Fill cho nó nhằm mục đích làm các view được lấp đầy các khoảng trống của stack view.
    4. Spacing: Giá trị được gán = 8 nhằm tạo khoảng cách giữa các View trong stack view cách đều nhau 1 khoảng = 8 points

    Tiếp đến chúng ta cần set thuộc tính của image view như sau:

    ViewAttributeValue
    Image ViewImageChọn ảnh mà bạn muốn
    Image ViewModeAspect fill

    Cuối cùng chúng ta mở Size inspector lên để gán lại các giá trị Priorities cho Image View:

    NameVertical huggingVertical registance
    Image view249749

    Vì chúng ta sử dụng distribution = Fill nên nó sẽ thu nhỏ các View dựa trên độ ưu tiên kháng nén(Compression registance priority) của chúng. Đó là lí do tại sao chúng ta phải giảm độ ưu tiên theo chiều dọc của Image View.

    Vậy là ta đã có được kết quả giống như trong hình ở trên.
    Để tạo được kết quả như trên mà không dùng stack view chúng ta sẽ phải tạo các constraint cho cả 3 view là label, image view và button. Nhưng với stack view mọi chuyện đã đơn giản hơn nhiều :D.



  • SQLite with FMDB in Swift (P1)

    SQLite with FMDB in Swift (P1)

    Đối với việc làm ứng dụng phần mềm, đa số đều phải sử dụng đến database. Core Data của Apple cung cấp là 1 trong những sự lựa chọn phổ biến đối với ứng dụng trên iphone.
    Theo í kiến cá nhân thì Core Data thường được dùng cho các database được tạo ra trong quá trình sử dụng, còn trong trường hợp nếu bạn có 1 database có sẵn để đưa vào app thì sao? Khi đó thì cần đưa 1 database đã được nhập sẵn vào app. SQlite là 1 lựa chọn tốt Ở bài viết này, mình sẽ giới thiệu cách sử dụng SQLite trong swift bằng thư viện FMDB.

    Nội dung bài viết

    • Giới thiệu về FMDB
    • Tạo đường dẫn cho 1 database
    • Khởi tạo 1 database
    • Tạo kết nối đến database
    • Thực hiện truy vấn

    FMDB Library:

    • FMDB là 1 thư viện hỗ trợ truy vấn Sqlite và quản lí dữ liệu hiệu quả.
    • Giúp bạn xử lí việc open connection/ close connection đến DB.
    • Có thể dùng cho cả swift hoặc obj-C.
    • Dễ dàng và nhanh chóng để tích hợp vào 1 project.
    • Cách sử dụng tương đối đơn giản, cho phép người dùng tự viết câu truy vấn theo ý muốn.

    Bài viết này tập trung vào việc sử dụng SQlite bằng thư viện FMDB, không hướng dẫn về phần cài đặt FMDB. Nếu bạn chưa biết về cách cài đặt thư viện, hãy xem thêm tại:

    https://guides.cocoapods.org/using/using-cocoapods.html

    Trong bài viết này, mình sẽ lấy 1 ví dụ về việc khởi tạo và viết các câu truy vấn đến 1 DB chứa các quyển sách.
    Khởi tạo struct Book:

    struct Book {
        let id: Int
        let name: String
        let author: String
        let price: Double
        
        func toString() {
            print("Book's info: id: \(id), name: \(name), author: \(author), price: \(price)")
        }
    }
    

    Tạo 1 class DatabaseManager để thực hiện việc truy vấn DB.
    Khởi tạo 1 biến database kiểu FMDatabase – Sẽ dùng để truy cập và thực hiện truy vấn đến DB.

    import Foundation
    import FMDB
    
    class DatabaseManager {
        static let shared = DatabaseManager()
        var database: FMDatabase!
    }

    Tạo đường dẫn để lưu DB:

    Nếu chưa có 1 DB có sẵn, thì khi tạo 1 DB cần phải có đường dẫn để lưu DB. Khởi tạo 1 đường dẫn cho 1 database có tên bookDB như sau:

    func getDatabasePath() -> String {
       let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
       let path = directoryPath.first!.appendingPathComponent("musicDB.db")
       print(path)
       return path.description
    }

    Sau khi print được ra đường dẫn, để xem database vừa tạo: Vào Finder, sử dụng tổ hợp phím Cmd + Shift + G, post đường dẫn vừa lấy được vào, Finder sẽ đưa đến database vừa tạo.

    Trong trường hợp đã có 1 DB bookDB có sẵn và được kéo vào trong app, thì lấy đường dẫn như sau:

    func getPathOfExistDB() -> String? {
       if let path = Bundle.main.path(forResource: "bookDB", ofType: ".db") {
           return path
       } 
       return nil
    }

    Khởi tạo 1 database

    func createDatabase() {
        if database == nil {
            if !FileManager.default.fileExists(atPath: getDatabasePath()) {
                database = FMDatabase(path: getDatabasePath())
            }
        }
    }

    Nếu 1 DB chưa tồn tại, thì khởi tạo 1 DB mới tại 1 đường dẫn đơn giản bằng cách khởi tạo 1 object FMDatabase tại 1 đường dẫn.

    Note: Việc check xem đã tồn tại 1 DB tại 1 path cụ thể trước khi khởi tạo rất quan trọng, bởi nếu ta path đó đã có sẵn 1 DB rồi thì DB đó sẽ bị hủy đi để khởi tạo 1 DB mới.

    Tạo kết nối đến Database:

    Note: Khi muốn truy suất đến DB thì phải mở 1 liên kết với DB, khi đã hoàn thành việc truy suất thì phải đóng liên kết lại.

    • Tạo liên kết tới DB:
    database.open()
    • Đóng liên kết tới DB:
    database.close()

    2 hàm trên đều trả về giá trị Bool để biết việc mở/đóng liên kết có thành công hay không.
    Ta có thể kết hợp việc tạo DB cùng với việc mở/đóng liên kết thành 1 hàm để tiện sử dụng:

    func openDatabaseConnectionAtPath(path: String) -> Bool {
        if database == nil {
            if !FileManager.default.fileExists(atPath: path) { // don’t want to create the database file again and destroy the original database.
                database = FMDatabase(path: path)
            }
        }
            
        if database != nil {
            if database.open() {
                print("Open Success")
                return true
            }
        }
        print("Open failed")
        return false
    }

    Tạo 1 bảng trong DB:

    Câu truy vấn tạo bảng:

    Viết câu truy vấn tạo bảng trong FMDB như sau:

    let query = "create table Book (id integer primary key autoincrement not null, name text not null, author text not null, price float not null default 0)"

    Để chạy câu truy vấn:

    database.executeUpdate(query, values: nil)

    Câu lệnh executeUpdate(…) dùng để thực hiện những câu truy vấn tạo ra sự thay đổi đến DB.

    • Thuộc tính values của hàm executeUpdate sẽ được nói ở phần sau, ở đây tạm thời để nil.
    • Hàm thực hiện câu truy vấn tạo bảng:
    func createBookTable() {
        if openDatabaseConnectionAtPath(path: getDatabasePath()) {
            let query = "create table Book id integer primary key autoincrement not null, name text not null, author text not null, price float not null default 0"
            do {
                try database.executeUpdate(query, values: nil)
            } catch let err {
                print("Execute query failed. error: \(err.localizedDescription)")
            }
        }
        database.close()
    }

    Hàm createBookTable ở trên sẽ tạo ra 1 DB nếu DB chưa tồn tại, còn nếu DB đã tồn tại rồi thì sẽ tạo liên kết, thực hiện truy vấn rồi đóng liên kết.
    Thực hiện câu truy vấn createBookTable:

    Kiểm tra kết quả:

    Ở phần tiếp theo, sẽ nói về các câu truy vấn insert, delete,…