Xcode 8 Source Editor Extension
Tháng 7/2016 vừa qua, Xcode 8 được giới thiệu cùng với Swift 2.3 và 3.0. Bên cạnh việc bắt buộc người dùng phải convert dự án sang Swift 2.3 và 3.0 gây phiền toái rất nhiều cho lập trình viên, Xcode 8 còn nâng cao tính bảo mật bằng cách không cho cài đặt các plugin ngoài (hẳn các bạn còn nhớ ...
Tháng 7/2016 vừa qua, Xcode 8 được giới thiệu cùng với Swift 2.3 và 3.0. Bên cạnh việc bắt buộc người dùng phải convert dự án sang Swift 2.3 và 3.0 gây phiền toái rất nhiều cho lập trình viên, Xcode 8 còn nâng cao tính bảo mật bằng cách không cho cài đặt các plugin ngoài (hẳn các bạn còn nhớ vụ XcodeGhost, 1 bản Xcode được các bạn láng giềng mod để tự động thêm trojan vào app mà vẫn qua được kiểm duyệt và up được lên App Store). Việc này đã sinh ra làn sóng phản đối của lập trình viên, những người đang sử dụng plugin thông qua Alcatraz.
Tất nhiên là Apple vẫn mở một con đường cho lập trình viên bằng cách giới thiệu "plugin" chính chủ của mình, gọi là Source Editor Extension (SEE).
SEE cho phép người dùng bổ sung thêm các tính năng vào trình soạn thảo code của Xcode, và đây cũng là điểm hạn chế của nó khi không thể can thiệp sâu vào Xcode như các plugin của Xcode 7 trở xuống đã làm được. Tuy nhiêm điểm mạnh của SEE là nó cung cấp các phương thức dễ dàng để lập trình viên có thể tự mình tạo ra các extension, đồng thời cung cấp khả năng để đưa các extension đó lên App Store để chia sẻ miễn phí hoặc bán kiếm lời.
Dưới đây tôi sẽ hướng dẫn các bạn tạo một extension cho Xcode để giúp cho việc chuyển đổi kiểu của text sang Upper case và Lower case.
2.1. Tạo dự án
Để bắt đầu chúng ta sẽ tạo một ứng dụng Cocoa Application mới.
Tên dự án sẽ là MGConvertCase (hoặc tên khác tùy ý). Có 1 điểm cần chú ý là chúng ta phải chọn Team để có thể test được extension.
Việc tiếp theo là Enable Development Signing
Sau bước trên ta sẽ thêm target cho dự án qua File > New > Target..., chọn Xcode Source Editor Extension.
Đặt tên cho target là ConvertCase, các thông số khác để như dưới đây:
Xcode sẽ hỏi có muốn activate scheme "ConvertCase" hay không và đương nhiên ta sẽ Activate.
2.2. Cấu hình dự án
Xong bước tạo dự án, để cấu hình dự án chúng ta cần phải sửa nội dung file Info.plist trong thư mục ConvertCase như sau:
Trong đó:
- Bundle name: Tên của extension sẽ hiện ở menu Editor của Xcode
- XCSourceEditorCommandDefinitions: một mảng chứa các command của extension:
- XCSourceEditorCommandIdentifier: định danh của command, cái này phải duy nhất
- XCSourceEditorCommandName: tên của command, sẽ hiện ở menu của Xcode
Để kiểm tra xem phần cấu hình có đúng không ta sẽ chạy thử (Command + R), chọn Xcode ở màn hình Choose an app to run:
Tại phiên bản Xcode Test có màu xám, chúng ta mở 1 project bất kỳ (trong demo tôi mở 1 Playground) và kiểm tra menu Editor > Convert Case. Nếu kết quả như hình dưới thì có nghĩa là chúng ta đã cấu hình đúng. Xin chúc mừng, bạn đã tạo được extension đầu tiên của mình!
Nếu không thì có thể bạn phải chạy dòng lệnh sau ở Terminal và khởi động lại Mac:
sudo /usr/libexec/xpccachectl
Dĩ nhiên là chọn UPPER CASE hay lower case không có tác dụng gì vì ta chưa viết code cho nó.
2.3. Viết code
Để viết code chúng ta sẽ mở file SourceEditorCommand.swift. Trong đó chúng ta quan tâm tới hàm perform(with:completionHandler:)
func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void { // Implement your command here, invoking the completion handler when done. Pass it nil on success, and an NSError on failure. completionHandler(nil) }
Thông qua biến invocation chúng ta có thể truy cập các thông tin của source code. Sau khi thực hiện các thay đổi, chúng ta sẽ gọi completionHandler, truyền vào tham số nil nếu thành công và NSError nếu có lỗi xảy ra.
class SourceEditorCommand: NSObject, XCSourceEditorCommand { func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void { guard let textRange = invocation.buffer.selections.firstObject as? XCSourceTextRange, invocation.buffer.lines.count > 0 else { completionHandler(nil) return } guard let bundleIdentifier = Bundle.main.bundleIdentifier else { completionHandler(nil) return } let upperCaseIdentifier = bundleIdentifier + ".UpperCase" let lowerCaseIdentifier = bundleIdentifier + ".LowerCase" // Switch all different commands id based which defined in Info.plist switch invocation.commandIdentifier { case upperCaseIdentifier: for lineIndex in textRange.start.line...textRange.end.line { let line = invocation.buffer.lines[lineIndex] as! NSString invocation.buffer.lines[lineIndex] = line.uppercased } case lowerCaseIdentifier: for lineIndex in textRange.start.line...textRange.end.line { let line = invocation.buffer.lines[lineIndex] as! NSString invocation.buffer.lines[lineIndex] = line.lowercased } default: break } completionHandler(nil) } }
Mục đích của đoạn code trên là tìm tất cả các dòng đang được select trong source code editor của Xcode và chuyển đổi thành Upper Case hoặc Lower Case tùy theo người dùng chọn command nào.
Ta có thể Command+R để xem kết quả.
2.4. Đóng gói
- Kiểm tra lại xem bạn đã signing cho MGConvertCase và ConvertCase ở Project Settings chưa.
- Chọn menu Product > Archive
- Chọn Export a Developer ID-signed Application
- Kéo file MGConvertCase.app vừa tạo được vào thư mục Applications
- Chạy MGConvertCase.app và thoát app.
- Đi tới System Preferences -> Extensions -> Xcode Source Editor và kích hoạt extension
- Mở Xcode 8, mở 1 file source code bất kỳ sau đó vào menu Editor để xem thành quả.
- Để gán shortcut thì chúng ta có thể vào Xcode Settings > Key Bindings:
2.5. App Store
Nếu muốn chia sẻ extension của mình thì các bạn hoàn toàn có thể submit lên App Store. Các bước submit giống với submit 1 ứng dụng Mac bình thường.
Như vậy chúng ta đã tìm hiểu xong về Xcode 8 Source Editor Extension. Chúc các bạn thành công trong việc tạo các extension riêng để phục vụ cho công việc của mình.
Source code