Chuyển đổi giữa nhiều môi trường trong app iOS

Overview

Trong quá trình phát triển một phần mềm, chúng ta thường xuyên gặp các vấn đề liên quan đến việc một app cần làm việc trên nhiều môi trường. Mỗi môi trường có một thông số khác nhau như: server hostname (URL), Bundle ID, và API Key của bên thứ 3.

Thông thường, cách mà chúng ta làm đó là thay các thông số đó một cách thủ công (manually). Nghĩa là, tự thay đổi các URL của server, API key, app bundle ID bằng tay (copy/paste). Và lặp đi lặp lại bước này mỗi khi muốn chuyển đổi giữa các môi trường. Điều này vừa tiêu tốn thời gian (time – consuming), vừa dễ phát sinh lỗi (error – prone), và điều quan trọng là nó lặp đi lặp lại (repetitive). Rất may Xcode hỗ trợ chúng ta một cách rất hữu dụng để có thể dễ dàng chuyển đổi giữa cách môi trường, thay vì phải làm bằng cách thủ công. Trong tutorial này sẽ trình bày các bước để cài đặt và sử dụng Xcode Scheme, Build Configuration vào Build Setting để quản lý các bản build khác nhau.

Getting Started

Trước tiên chúng ta tạo một project mới, điền đầy đủ thông tin vào các trường. Nếu đang làm việc trên một project nào đó thì có thể bỏ qua bước này.

Cài đặt Build Configuration

Tìm đến project Info, trong phần Configuration, click vào dấu (+) và chọn duplicate the “Debug” configuration để tạo mới một cấu hình. Đổi tên cấu hình mới thành “Debug Staging” để phân biệt với cấu hình Debug đã có. Tương tự chúng ta tạo thêm các build configuration: Release Staging, Release Appstore bằng cách nhấn dấu (+) và Duplicate the “Release” Configuration. Kết quả sẽ như sau: Kết thúc bước này chúng ta đã có được 5 build configuration khác nhau là: Debug, Debug Staging, Release, Release Staging, Release Appstore. Ở bước sau, chúng ta sẽ tiến hành cài đặt Build Setting ứng với từng build configuration đã tạo.

Cài đặt User-defined Build Settings

Thông thường chúng ta sẽ tạo một cài đặt User-Defined cho các thuộc tính như Bundle ID, tên ứng dụng, Facebook ID; vì trên các bản build khác nhau đôi khi chúng cần thiết phải khác nhau. Đê làm như vậy, trong tab Build Setting của project, chúng ta nhấn vào dấu (+) và chọn Add User-Defined Setting. Ở đây mình tạo hai thuộc tính User-Defined là: BUNDLE_IDFACEBOOK_APP_ID. Tiến hành thay đổi các giá trị cho từng build configuration ta được kết quả như sau: Các giá trị định nghĩa user-defined vừa được tạo ở trên chúng ta sẽ sử dụng ở trong file Info.plist ở bước tiếp theo sau đây.

Cài đặt file Info.plist

Chắc hẳn có đôi lần khi mở file Info.plist, mỗi khi nhìn vào các kí tự kiểu (PRODUCTNAME),(PRODUCT_NAME)*, *(PRODUCT_ID), … chúng ta vẫn thắc mắc nó là gì, ở đâu ra, thì ngay bây giờ chúng ta đã có câu trả lời hoàn hảo dành cho câu hỏi này. Tất các các giá trị mà ta vừa định nghĩa trong phần *User-Defined *ở bước trên sẽ được sử dụng trong file Info.plist, bằng cách thêm chúng vào trong phần value của các key cần thiết. Ví dụ, nếu chúng ta đã định nghĩa BUNDLE_ID trong phần User-Defined setting, việc cần làm bây giờ là chỉnh sửa giá trị của key Bundle identifier trong file Info.plist thành (BUNDLE_ID)*. Tương tự với key *FacebookAppID* sẽ có value là *(FACEBBOOK_APP_ID). Kết quả sẽ như sau:

Tạo Build Scheme tương ứng với mỗi Build Setting

Mỗi bản build cần một Scheme (kế hoạch), chúng ta sẽ tạo 3 Scheme sau: DemoAppStaging, DemoAppProduction, DemoAppStore, lần lượt dành cho bản build thử, bản build Production, và bản build lên AppStore.

Click chuột vào phần Set the active scheme nằm ở phía trên bên trái của Xcode và chọn Manager Scheme, như hình sau: Sau đó tạo một Scheme mới có tên là DemoAppStaging Làm tương tự đối với DemoAppProductionDemoAppAppStore. Tick chọn mục “Shared” để đồng bộ các Scheme này lên Git.

Edit Scheme

Sau khi tạo mới các Scheme cần thiết, ta tiến hành chỉnh sửa chúng phù hợp với nhu cầu sử dụng của từng Scheme. Hãy đảm bảo rằng từng thuộc tính của build configuration đều đã được chỉnh sửa, chúng ta chắc chắn không muốn khi build cho tester lại ra một kết quả, còn khi archive cho khách hàng lại ra một kết quả khác. 😄 Hãy chắc chắn rằng tất cả các phần Build, Run, Test, Archive đều cùng một Build Configuration. Bây giờ, ta có thể chuyển đổi giữa các scheme khác nhau một cách dễ dàng bằng cách chọn chúng trong list các scheme đã được xây dựng bên trên.

Định nghĩa Prepocessor Macro

Để dễ dàng chuyển đổi giữa các Base URL của server thật hoặc server test, chúng ta có thể thêm các Preprocessor Macro (Macro tiền xử lý) để có thể xác định được môi trường đích của bản build. Ví dụ: STAGING=1 dành cho các bản build thử. RELEASE=1 dành cho các bản build trên server thật. Để thêm một macro, trong mục Build Setting, tiến hành tìm kiếm với từ khóa “Preprocessor Macros”, sau đó thêm STAGING=1 vào Debug stagingRelease staging; thêm RELEASE=1 vào Release, Release AppStore. Cuối cùng, chúng ta chỉ cần định nghĩa Base URL như sau trong file Constant hoặc file PrefixHeader như sau: Tạo một Alert hiển thị hai BaseURL này, Build và Run project lần lượt trên 2 Scheme khác nhau là DemoAppStagingDemoAppProduction ta được hai kết quả như sau: Tận hưởng thành quả thôi.

Where go from here?

Ở tutorial trên chúng ta đã biết làm thế nào để: • Tạo các build configuration. • Tạo build scheme ứng với từng build configuration. • Tạo các key User-defined ứng với từng build configuration để add vào file Info.plist. • Define các macro riêng cho từng build configuration.

Câu hỏi đặt ra là chúng ta còn có thể làm gì được nữa không? Thì đây sẽ là một bonus nho nhỏ: Vâng, khi debug câu lệnh kinh điển chúng ta thường sử dụng đó là NSLog(), tuy nhiên trên bản build để release thì việc in ra các dòng log đôi khi không cần thiết. Cách đơn giản nhất để làm được điều đó là chúng ta sẽ define một macro dùng để in ra các dòng log, và macro này sẽ chỉ thực hiện trên bản debug. Như sau:

#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#ifdef DEBUG
#   define ULog(fmt, ...)  { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"%s\n [Line %d] ", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__]  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; }
#else
#   define ULog(...)
#endif

Sử dụng tương tự như NSLog, và build lần lượt trên các Scheme khác nhau để thấy sự khác biệt.

Thank for your attension.

All Rights Reserved