Github repository addressWithout further ado, first look at the final effect:Image size limit, not clear can be clickedTo view the original image

As you can see, this plugin consists of two modules, one is the setup page which we use with the Mac APP (no Mac development foundation is required), and the other is the Xcode Source Editor plug-in. We use SWIFT to develop these two modules.

Step 1: Start the project and create a Mac APP

Then name the projectDKJSONExtensionThe development language for Swift uses storyboard. At this point, we have created the main project and run it as a blank window. We first set the function of the page, in the development of Xcode Source editer plug-in. So first let’s go to the storyboard and change the title of the main application windowThen drag out the Settings page in the ViewController, and for layout PURPOSES I use two StackViews to arrange the 3 options horizontallyEvent handling is done after the setup screen, which displays and modifies the current model types and naming conventions. Here we useUserDefaultsStoring data in order to interact with controls in our storyboard we’re going to use two common ways to get controls here the first one is to use@IBOutletQuote, Chapter 2 through setuptagTake a subviewWe’re going to implement radio buttons that need to be handled by themselves in the button click event. Do an event handler for the button in the same row and store the current value

@IBAction func modeTypeChange(_ sender: NSButton) {
        [codeableBtn,handyJSONBtn,swiftyJSONBtn].forEach {
            $0?.state = sender.title = = $0?.title ? .on : .off
        }
        UserDefaults.modelType = sender.title 
    }

    @IBAction func modeNameStyleChange(_ sender: NSButton) {
        for i in 0..<3 {
            let btn = self.view.viewWithTag(100 + i) as? NSButton
            btn?.state = sender.title = = btn?.title ?.on:.off
        }
        UserDefaults.modelStyle = sender.title
    }
Copy the code

So we’ve done our event handling and data storage. Here we’re storing data that needs to be shared with extension to do this we’re using the APP Group for userDefault access. If you are usingUserDefaults.standardWe will not be able to get the accessed value in Extension. The steps are as follows: Go to the Developer Center to create an APP GroupSelect APPgroup to fill in the name and identifier created to open the duplicate identifier as shownAdd the APPgroup to the project, and the identifier just copiedAt this point, we can access the data by group, which will be shared by the same group of applications. Let’s add an extension to userDefault. Note that userDefault is created using groupID


extension UserDefaults{
    static var grouped:UserDefaults{
        return UserDefaults(suiteName: "group.com.dkjone.DKJsonExtension") ?? standard
    }
    /// Model type
    static var  modelType: String {
        get { return grouped.string(forKey: #function) ?? "HandyJSON" }
        set { grouped.setValue(newValue, forKey: #function) }
    }
    // Naming rules
    static var modelStyle: String {
        get { return grouped.string(forKey: #function) ?? "Hump method" }
        set { grouped.setValue(newValue, forKey: #function) }
    }
}
Copy the code

Finally, when the Settings page loads, the currently stored Settings data is displayed

 override func viewDidLoad(a) {
        super.viewDidLoad()
        [codeableBtn,handyJSONBtn,swiftyJSONBtn].forEach {
            $0?.state = UserDefaults.modelType = = $0?.title ? .on : .off
        }
        for i in 0..<3 {
            let btn = self.view.viewWithTag(100 + i) as? NSButton
            btn?.state = UserDefaults.modelStyle = = btn?.title ?.on:.off
        }
    }
Copy the code

Here we have completed all the functions of setting up the application. Next we implement the Source Editor Extension section. In this section we create the extension as shown in the figure below:After the creation of our project more than the following files

-- JSONExtension
---- SourceEditorExtension.swift
---- SourceEditorCommand.swift
---- Info.plist
Copy the code

These files are the key to writing Extension. Our project target will also have an additional option. When we run it with the following option, a gray Xcode will appear, allowing us to debug. Opening the file with gray Xcode will show the name of the plug-in we added in the Editor menu.If you get an error

dyld: Library not loaded: @rpath/XcodeKit.framework/Versions/A/XcodeKit
  Referenced from: 
Copy the code

Note library connection problems, find the library link to delete after the figure can be addedPlease go to xcoed->preferencces-> Account, delete the previous account, and log in to the account again

"Your session has expired. Please log in."
Copy the code

Gray Xcode appears if it runs successfully To open the file or project in grey xcode, at the bottom of the Editor menu will be our new plug-in. Before that, let’s change the name of the plug-in menu, either in the info.plist or in the code. We can change the return value in the code directly. Modified The priority of the returned value is higher than that of info.plistSourceEditorExtension.swiftThe file contains the following contents

class SourceEditorExtension: NSObject.XCSourceEditorExtension {
    var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: Any]] {
        return [[.nameKey: "Convert JSON file to model", .identifierKey: "paraseFile", .classNameKey: SourceEditorCommand.className()],
                [.nameKey: "Convert JSON from clipboard to model", .identifierKey: "parasePasteboard", .classNameKey: SourceEditorCommand.className()]]
    }
}
Copy the code

What to look out for:

NameKey: specifies the name of the menu. IdentifierKey: specifies the menu identifier used in the processing class to determine which menu was clicked. To handle the className of this menu, we get it directly using className(). If you’re writing a string, prefix the className with the module name.

Here we run directly after the following effect

Finally, we need to deal with how to generate code: open the processing class

class SourceEditorCommand: NSObject.XCSourceEditorCommand {
// The method that is called when a menu is clicked
 func perform(with invocation: XCSourceEditorCommandInvocation.completionHandler: @escaping (Error?). ->Void){}}Copy the code

The invocation is the context in which the current menu ID and the current document type, document content, line number, etc. If you need to modify the file, simply modify the invocation.buffer.lines. This is a mutable array of type Sting. You can add to it or you can replace a line. In order to get the configuration saved by the main application, we also need to configure the same APP group for this extension, as above. Use the same userdefault to retrieve the configuration stored in the main program.

Here specific parse JSON logic is not carefully explained, interested can view the complete source code github or code cloud

The final step is to release the plug-in we made. Select the main project and then xcode-> Product -> Archive. Then, as with IOS apps, you can choose to sign the developer or submit to the APP Store or for internal testing. The exported content is shown as follows:If you want to make the DMG file in the image, open itDisk utility-> File -> New image -> Create by Folder -> Select the folder where the plug-in is located. To use it, drag the APP file (in the circle above) into the Application folder and open it. Make sure you open it once and get thereSystem Preferences->extensionTo check the plug-inThen we can use the extension normally