Xcode 8 Source Editor Extension

xcode8.png

1. Giới thiệu

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. Xây dựng extension MGConvertCase

2.1. Tạo dự án

Để bắt đầu chúng ta sẽ tạo một ứng dụng Cocoa Application mới.

D8B20DD068ED51C20D908296A2AA0564.png

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.

830F6B2C3D59C51C27E3D69F92E2E748.png

Việc tiếp theo là Enable Development Signing

BA86170C8E2AE6AAE9415DA451BABD40.png

Sau bước trên ta sẽ thêm target cho dự án qua File > New > Target..., chọn Xcode Source Editor Extension.

FECA664FFD98F9DDCF4747C3754EBD91.png

Đặt tên cho target là ConvertCase, các thông số khác để như dưới đây:

A56427C18BD530785D0B0105999F6B70.png

Xcode sẽ hỏi có muốn activate scheme "ConvertCase" hay không và đương nhiên ta sẽ Activate.

1FD8BAB9806196E1E3CB503D452AD208.png

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:

CE4A45EF5991FD7201D11CB846CE2B4F.png

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:

7E562DAABDEC9549FEF610D35D606FE8.png

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

EB2DB094462D248836A567CE36F83A5D.png

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 MGConvertCaseConvertCaseProject Settings chưa.
  • Chọn menu Product > Archive
  • Chọn Export a Developer ID-signed Application

BDC2385719D8CFA62F06667523783339.png

  • 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

FE94E29231B94E2AB13CAC41D502FDE7.png

  • Mở Xcode 8, mở 1 file source code bất kỳ sau đó vào menu Editor để xem thành quả.

EA03E265A1B6F32AF63F7EC576DA48B9.png

  • Để gán shortcut thì chúng ta có thể vào Xcode Settings > Key Bindings:

03E095CF7C37EFE033A0EB33627C3742.png

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.

3. Kết luận

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