Articles

Jak převést zásuvné moduly Xcode na rozšíření Xcode

od Khoa Pham

Zdroj: Xcode je nepostradatelné IDE pro vývojáře iOS a macOS. Možnost vytvářet a instalovat vlastní zásuvné moduly nám už od prvních dnů přinesla obrovský nárůst produktivity. Netrvalo dlouho a Apple kvůli obavám o soukromí zavedl rozšíření Xcode.

Sám jsem vytvořil několik pluginů a rozšíření Xcode, například XcodeWay, XcodeColorSense, XcodeColorSense2 a Xmas. Byla to obohacující zkušenost. Hodně jsem se naučil a produktivita, kterou jsem získal, byla značná. V tomto příspěvku vás provedu tím, jak jsem své zásuvné moduly Xcode převedl na rozšíření, a zkušenostmi, které jsem při tom získal.

Můj první zásuvný modul Xcode: XcodeWay

Na těžkou práci jsem si vybral lenocha. Protože líný člověk si najde snadný způsob, jak ji udělat

Velmi se mi líbí výše uvedený citát Billa Gatese. Snažím se vyhýbat opakujícím se a nudným úkolům. Kdykoli zjistím, že dělám znovu stejné úkoly, píšu skripty a nástroje, které to automatizují. Dělat to zabere nějaký čas, ale v blízké budoucnosti budu o něco línější.

Kromě zájmu o budování open source frameworků a nástrojů rád rozšiřuji IDE, které používám – většinou Xcode.

V roce 2014 jsem poprvé začal vyvíjet iOS. Chtěl jsem rychlý způsob, jak přejít na mnoho míst přímo z Xcode s kontextem aktuálního projektu. Mnohokrát chceme:

  • otevřít aktuální složku projektu ve „Finderu“, abychom změnili některé soubory
  • otevřít Terminál, abychom spustili některé příkazy
  • otevřít aktuální soubor v GitHubu, abychom rychle předali odkaz kolegovi z práce
  • nebo otevřít jiné složky, jako jsou témata, pluginy, úryvky kódu, logy zařízení.

Každý kousek času, který každý den ušetříme, se počítá.

Přemýšlel jsem, že by byl skvělý nápad napsat zásuvný modul Xcode, který bychom mohli dělat všechny výše uvedené věci přímo v Xcode. Místo abych čekal, až to udělají ostatní, vytáhl jsem rukáv a napsal svůj první plugin pro Xcode – XcodeWay- a sdílel ho jako open source.

XcodeWay funguje tak, že pod Editor vytvoří nabídku se spoustou možností, jak přejít na jiná místa přímo z Xcode. Vypadá to jednoduše, ale bylo potřeba vynaložit určité úsilí.

Co jsou zásuvné moduly Xcode?

Zásuvné moduly Xcode nejsou oficiálně podporovány Xcode ani doporučovány společností Apple. Neexistují o nich žádné dokumenty. Nejlépe se o nich můžeme dozvědět ze zdrojových kódů existujících zásuvných modulů a z několika výukových programů.

Zásuvný modul Xcode je pouze svazek typu xcplugin a je umístěn na ~/Library/Application Support/Developer/Shared/Xcode/Plug-ins . Při spuštění Xcode se načtou všechny zásuvné moduly Xcode přítomné v této složce. Zásuvné moduly jsou spouštěny ve stejném procesu jako Xcode, takže by mohly dělat cokoli jako Xcode. Chyba v některém zásuvném modulu může způsobit pád Xcode.

Pro vytvoření zásuvného modulu Xcode vytvořte macOS Bundle s jednou třídou, která se rozšiřuje z NSObject , a mějte inicializátor, který přijímá NSBundle , například v Xmas:

class Xmas: NSObject {
 var bundle: NSBundle
 init(bundle: NSBundle) { self.bundle = bundle super.init() }}

Uvnitř Info.plist musíme:

  • prohlásit tuto třídu za hlavní vstupní třídu zásuvného modulu a
  • že tento svazek nemá uživatelské rozhraní, protože ovládací prvky uživatelského rozhraní vytváříme a přidáváme do rozhraní Xcode za běhu
<key>NSPrincipalClass</key><string>Xmas</string><key>XCPluginHasUI</key><false/>

Dalším problémem zásuvných modulů Xcode je, že musíme průběžně aktualizovat DVTPluginCompatibilityUUIDs . To se mění vždy, když vyjde nová verze Xcode. Bez aktualizace Xcode odmítne zásuvný modul načíst.

Co zásuvné moduly Xcode umí

Mnoho vývojářů vytváří zásuvné moduly Xcode, protože jim chybí specifické funkce, které jsou v jiných IDE, jako je Sublime Text, AppCode nebo Atom.

Protože se zásuvné moduly Xcode načítají ve stejném procesu jako Xcode, umí vše, co umí Xcode. Jediným omezením je naše představivost. Můžeme využít Objective C Runtime k objevování soukromých rámců a funkcí. Pak lze dále využít LLDB a Symbolic breakpoint ke kontrole běžícího kódu a změně jejich chování. Můžeme také použít swizzling ke změně implementace libovolného běžícího kódu. Psaní zásuvných modulů Xcode je náročné – je třeba spousta dohadů a někdy i dobrá znalost assembleru.

Ve zlaté éře zásuvných modulů existoval populární správce zásuvných modulů, který sám byl zásuvným modulem, s názvem Alcatraz. Ten uměl instalovat další zásuvné moduly, které v podstatě jen stáhly soubor xcplugin a ten přesunuly do složky Plug Ins.

Abychom si udělali představu o tom, co zásuvné moduly umí, podívejme se na některé populární zásuvné moduly.

Xvim

První na seznamu je Xvim, který přidává vazby kláves Vim přímo v Xcode. Podporuje většinou všechny vazby kláves, které jsme měli v Terminálu.

SCXcodeMiniMap

Pokud vám v Sublime Textu chybí režim MiniMap, můžete pomocí SCXcodeMiniMap přidat pravý panel mapy uvnitř editoru Xcode.

FuzzyAutocompletePlugin

Před verzí 9 neměl Xcode správné automatické dokončování – bylo založeno pouze na předponě. V tom zazářil plugin FuzzyAutocompletePlugin. Provádí fuzzy automatické doplňování na základě skryté funkce IDEOpenQuicklyPattern v Xcode.

KSImageNamed-Xcode

Pro zobrazení obrázku svazku uvnitř UIImageView často používáme metodu imageNamed. Pamatovat si přesně název souboru s obrázkem je však obtížné. KSImageNamed-Xcode je tu proto, aby vám pomohl. Když začnete psát, zobrazí se seznam automaticky navržených názvů obrázků.

ColorSense-for-Xcode

Dalším svědectvím při vývoji je práce s UIColor , která používá barevný prostor RGBA. Nedostaneme vizuální indikátor barvy, kterou zadáváme, a ruční provádění kontroly může být časově náročné. Naštěstí existuje aplikace ColorSense-for-Xcode, která zobrazuje použitou barvu, a panel pro výběr barvy, pomocí něhož snadno vybereme správnou barvu.

Konzola

V aplikaci AppCode můžeme přejít na konkrétní řádek v souboru, který je zaznamenán uvnitř konzoly. Pokud vám tato funkce v Xcode chybí, můžete použít LinkedConsole. Ta umožňuje klikatelné odkazy uvnitř konzoly Xcode, takže můžeme okamžitě přejít na daný soubor.

Těžká práce za pluginy Xcode

Vytvořit plugin Xcode není snadné. Nejenže musíme znát programování pro macOS, ale musíme se také ponořit hluboko do hierarchie zobrazení Xcode. Musíme prozkoumat soukromé rámce a rozhraní API, abychom do nich mohli vložit požadovanou funkci.

Existuje jen velmi málo návodů, jak vytvářet zásuvné moduly, ale naštěstí je většina zásuvných modulů open source, takže můžeme pochopit, jak fungují. Protože jsem několik zásuvných modulů vytvořil, mohu o nich uvést několik technických podrobností.

Zásuvné moduly Xcode se obvykle dělají pomocí dvou soukromých rámců: DVTKit a IDEKit . Systémové frameworky jsou na /System/Library/PrivateFrameworks, ale frameworky, které používá výhradně Xcode, jsou pod /Applications/Xcode.app/Contents/ , tam najdete Frameworks , OtherFrameworks a SharedFrameworks.

Existuje nástroj class-dump, který umí generovat hlavičky ze svazku aplikace Xcode. Pomocí názvů tříd a metod můžete zavolat NSClassFromString a získat třídu z názvu.

Swizzling DVTBezelAlertPanel framework v Xmas

Vánoce ve mně vždy vyvolávaly zvláštní pocit, a tak jsem se rozhodl vytvořit Xmas, který místo výchozího zobrazení upozornění zobrazuje náhodný vánoční obrázek. Třída použitá k vykreslení tohoto zobrazení je DVTBezelAlertPanel uvnitř frameworku DVTKit. Můj článek o sestavení tohoto pluginu je zde.

V prostředí Objective C Runtime existuje technika zvaná swizzling, která dokáže měnit a přepínat implementaci a signatury metod libovolných spuštěných tříd a metod.

Zde, abychom změnili obsah tohoto výstražného zobrazení, musíme vyměnit inicializátor initWithIcon:message:parentWindow:duration: za naši vlastní metodu. To uděláme hned na začátku nasloucháním NSApplicationDidFinishLaunchingNotification, který je upozorněn, když se spustí zásuvný modul systému macOS, v tomto případě Xcode.

class func swizzleMethods() { guard let originalClass = NSClassFromString("DVTBezelAlertPanel") as? NSObject.Type else { return }
do { try originalClass.jr_swizzleMethod("initWithIcon:message:parentWindow:duration:", withMethod: "xmas_initWithIcon:message:parentWindow:duration:") } catch { Swift.print("Swizzling failed") }}

Zpočátku se mi líbilo dělat všechno ve Swiftu. Ale je složité použít metodu swizzle init ve Swiftu, takže nejrychlejší je udělat to v Objective C. Pak jednoduše projdeme hierarchii zobrazení a najdeme NSVisualEffectView uvnitř NSPanel, abychom aktualizovali obrázek.

Interakce s DVTSourceTextView v XcodeColorSense

Pracuji většinou s hexadecimálními barvami a chci rychlý způsob, jak zobrazit barvu. Proto jsem vytvořil XcodeColorSense – podporuje hexadecimální barvy, RGBA a pojmenované barvy.

Námět je jednoduchý. Analyzovat řetězec a zjistit, zda uživatel píše něco, co souvisí s UIColor, a zobrazit malé překryvné zobrazení s touto barvou jako pozadím. Textové zobrazení, které používá Xcode, je typu DVTSourceTextView v rámci DVTKit. Potřebujeme také poslouchat NSTextViewDidChangeSelectionNotification, který se spustí vždy, když se změní nějaký obsah NSTextView.

func listenNotification() { NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(handleSelectionChange(_:)), name: NSTextViewDidChangeSelectionNotification, object: nil)}
func handleSelectionChange(note: NSNotification) { guard let DVTSourceTextView = NSClassFromString("DVTSourceTextView") as? NSObject.Type, object = note.object where object.isKindOfClass(DVTSourceTextView.self), let textView = object as? NSTextView else { return }
self.textView = textView}

Měl jsem architekturu Matcher, takže můžeme detekovat různé druhy konstrukcí UIColor – například HexMatcher .

public struct HexMatcher: Matcher {
func check(line: String, selectedText: String) -> (color: NSColor, range: NSRange)? { let pattern1 = "\"#?{6}\"" let pattern2 = "0x{6}"
let ranges = .flatMap { return Regex.check(line, pattern: ) }
guard let range = ranges.first else { return nil }
let text = (line as NSString).substringWithRange(range).replace("0x", with: "").replace("\"", with: "") let color = NSColor.hex(text)
return (color: color, range: range) }}

K vykreslení překryvu použijeme NSColorWell, který je vhodný pro zobrazení zobrazení pohledu s pozadím. Pozici určíme voláním firstRectForCharacterRange a některými převody bodů pomocí convertRectFromScreen a convertRect .

Použití NSTask a IDEWorkspaceWindowController v XcodeWay

Nakonec můj milovaný XcodeWay.

Zjistil jsem, že potřebuji přejít na různá místa z Xcode s kontextem aktuálního projektu. Proto jsem vytvořil XcodeWay jako zásuvný modul, který přidává spoustu praktických možností nabídky pod Window.

Protože zásuvný modul běží ve stejném procesu Xcode, má přístup k hlavní nabídce NSApp.mainMenu?.itemWithTitle("Window") . Tam můžeme nabídku změnit. XcodeWay je navržen tak, aby bylo možné snadno rozšiřovat funkce prostřednictvím protokolu Navigator.

@objc protocol Navigator: NSObjectProtocol { func navigate() var title: String { get }}

Pro složky se statickou cestou, jako je Provisioning Profile ~/Library/MobileDevice/Provisioning Profiles nebo User data Developer/Xcode/UserData , můžeme pouze zkonstruovat URL a zavolat NSWorkspace.sharedWorkspace().openURL . Pro dynamické složky, které se mění v závislosti na aktuálním projektu, je třeba udělat více práce.

Jak otevřeme složku pro aktuální projekt ve Finderu? Informace o cestě k aktuálnímu projektu jsou uloženy uvnitř IDEWorkspaceWindowController . Jedná se o třídu, která spravuje okna pracovního prostoru v Xcode. Podívejte se na EnvironmentManager, kde pomocí objc_getClass získáme definici třídy z řetězce.

self.IDEWorkspaceWindowControllerClass = objc_getClass("IDEWorkspaceWindowController");
NSArray *workspaceWindowControllers = ;
id workSpace = nil;
for (id controller in workspaceWindowControllers) { if ( isEqual:]) { workSpace = ; }}
NSString * path = valueForKey:@"_pathString"];

Nakonec můžeme využít valueForKey k získání hodnoty libovolné vlastnosti, o které si myslíme, že existuje. Tímto způsobem získáme nejen cestu k projektu, ale také cestu k otevíracímu souboru. Můžeme tedy zavolat activateFileViewerSelectingURLs na NSWorkspace a otevřít Finder s vybraným souborem. To je praktické, protože uživatelé nemusí hledat tento soubor ve Finderu.

Mnohdy chceme spustit některé příkazy Terminálu v aktuální složce projektu. Abychom toho dosáhli, můžeme použít NSTask se spouštěcím blokem /usr/bin/open a argumenty . iTerm, pokud je pravděpodobně nakonfigurován, to otevře v nové kartě.

Dokumenty pro aplikace iOS 7 jsou umístěny v pevném umístění iPhone Simulator uvnitř Podpory aplikací. Od iOS 8 má však každá aplikace unikátní UUID a jejich složky s dokumenty jsou těžko předvídatelné.

~/Library/Developer/CoreSimulator/Devices/1A2FF360-B0A6-8127-95F3-68A6AB0BCC78/data/Container/Data/Application/

Můžeme vytvořit mapu a provést sledování, abychom našli vygenerované ID pro aktuální projekt, nebo zkontrolovat plist uvnitř každé složky a porovnat identifikátor svazku.

Rychlé řešení, které mě napadlo, bylo vyhledat nejnovější aktualizovanou složku. Při každém sestavení projektu nebo provedení změn uvnitř aplikace se aktualizuje jejich složka s dokumenty. Právě tam můžeme využít NSFileModificationDate k vyhledání složky aktuálního projektu.

Při práci se zásuvnými moduly Xcode existuje mnoho hacků, ale výsledky se vyplatí. Každých pár minut, které každý den ušetříme, nakonec celkově ušetří spoustu času.

Bezpečí a svoboda

S velkou mocí přichází i velká odpovědnost. Skutečnost, že si zásuvné moduly mohou dělat, co chtějí, zvoní hranou bezpečnosti. Koncem roku 2015 došlo k malwarovému útoku šířením upravené verze Xcode nazvané XcodeGhost, která do všech aplikací vytvořených pomocí Xcode Ghost vloží škodlivý kód. Předpokládá se, že malware využívá mimo jiné mechanismus zásuvných modulů.

Stejně jako aplikace pro iOS, které stahujeme z Appstore, jsou i aplikace pro macOS, jako je Xcode, podepsány společností Apple, když je stahujeme z Mac Appstore nebo prostřednictvím oficiálních odkazů ke stažení od společnosti Apple.

Podepsání kódu aplikace zajišťuje uživatelům, že pochází ze známého zdroje a aplikace nebyla od posledního podepsání upravena. Předtím, než může vaše aplikace integrovat služby aplikace, být nainstalována na zařízení nebo odeslána do obchodu App Store, musí být podepsána certifikátem vydaným společností Apple

Aby se předešlo potenciálnímu malwaru, jako je tento, na konferenci WWDC 2016 společnost Apple oznámila rozšíření Xcode Source Editor Extension jako jediný způsob, jak do Xcode nahrát rozšíření třetích stran. To znamená, že od verze Xcode 8 nelze zásuvné moduly načítat.

Rozšíření editoru zdrojů

Rozšíření je doporučený přístup k bezpečnému přidávání funkcí omezenými způsoby.

Rozšíření aplikací poskytují uživatelům přístup k funkcím a obsahu vaší aplikace v celém systému iOS a macOS. Vaše aplikace se nyní například může zobrazit jako widget na obrazovce Dnes, přidat nová tlačítka v listu akcí, nabídnout fotografické filtry v aplikaci Fotky nebo zobrazit novou vlastní klávesnici pro celý systém.

Prozatím jediným rozšířením v Xcode je Source Editor, který nám umožňuje číst a upravovat obsah zdrojového souboru a také číst a upravovat aktuální výběr textu v editoru.

Rozšíření je nový cíl a běží v jiném procesu než Xcode. To je dobré v tom, že nemůže měnit Xcode jiným způsobem než v souladu s XCSourceEditorCommand upravovat aktuální obsah dokumentu.

protocol XCSourceEditorCommand {
 func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -&gt; Void)}

Xcode 8 má spoustu vylepšení, například nové funkce pro dokončování kódu, obrázkové a barevné literály Swift a fragmenty. To vedlo ke zrušení mnoha zásuvných modulů Xcode. U některých nepostradatelných zásuvných modulů, jako je XVim, je to pro některé lidi neúnosné. Některé staré funkce zásuvných modulů nelze dosáhnout pomocí současného systému rozšíření editoru zdrojů.

Pokud nerezignujete na Xcode

Obejít omezení z Xcode 8 pro zásuvné moduly lze tak, že stávající podpis Xcode nahradíte technikou zvanou resign. Rezignace je velmi snadná – stačí vytvořit certifikát s vlastním podpisem a zavolat příkaz codesign. Poté by měl být Xcode schopen načítat zásuvné moduly.

codesign -f -s MySelfSignedCertificate /Applications/Xcode.app

Není však možné odesílat aplikace vytvořené pomocí rezignovaného Xcode, protože podpis neodpovídá oficiální verzi Xcode. Jednou z možností je používat dva Xcode: jeden oficiální pro distribuci a druhý rezignovaný pro vývoj.

Přechod na rozšíření Xcode

Rozšíření Xcode je správná cesta, proto jsem začal přesouvat své pluginy do rozšíření. Pro Xmas, protože mění hierarchii zobrazení, se rozšířením stát nemůže.

Barevný doslov v XcodeColorSense2

Pro barevný smysl jsem rozšíření přepsal od začátku a nazval ho XcodeColorSense2. To samozřejmě nemůže zobrazit překryv nad aktuálním zobrazením editoru. Proto jsem se rozhodl využít nový Color literal, který se nachází v Xcode 8+.

Barva se zobrazuje v malém rámečku. Může být těžké rozlišit podobné barvy, proto uvádím i název. Kód je jednoduše o kontrole selections a parsování pro nalezení deklarace barvy.

func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void { guard let selection = invocation.buffer.selections.firstObject as? XCSourceTextRange else { completionHandler(nil) return }
let lineNumber = selection.start.line
guard lineNumber < invocation.buffer.lines.count, let line = invocation.buffer.lines as? String else { completionHandler(nil) return }
guard let hex = findHex(string: line) else { completionHandler(nil) return }
let newLine = process(line: line, hex: hex)
invocation.buffer.lines.replaceObject(at: lineNumber, with: newLine)
completionHandler(nil) }}

Většina funkcí je zabudována uvnitř mého frameworku Farge, ale nemohu najít způsob, jak framework použít uvnitř rozšíření Xcode.

Protože je funkce rozšíření přístupná pouze přes nabídku Editor, můžeme si přizpůsobit vazbu na klávesu, která tuto položku nabídky vyvolá. Já například zvolím Cmd+Ctrl+S pro zobrazení a skrytí informací o barvách.

V porovnání s původním doplňkem to samozřejmě není intuitivní, ale je to lepší než nic.

Jak ladit rozšíření Xcode

Práce s rozšířeními a jejich ladění je jednoduché. K ladění můžeme použít Xcode. Odladěná verze Xcode má šedou ikonu.

Jak nainstalovat rozšíření Xcode

Rozšíření musí mít doprovodnou aplikaci pro macOS. Ta může být distribuována do Mac Appstore nebo podepsaná vlastním podpisem. Napsal jsem článek, jak to udělat.

Všechna rozšíření pro aplikaci musí být explicitně povolena prostřednictvím „Předvoleb systému“.

Rozšíření Xcode zatím funguje pouze s editorem, takže musíme otevřít zdrojový soubor, aby se nabídka Editor projevila.

AppleScript v XcodeWay

V rozšířeních Xcode už nefungují NSWorkspace, NSTask a konstrukce soukromých tříd. Protože jsem použil rozšíření Finder Sync ve FinderGo, napadlo mě, že bych mohl zkusit stejné skriptování AppleScriptem pro rozšíření Xcode.

AppleScript je skriptovací jazyk vytvořený společností Apple. Umožňuje uživatelům přímo ovládat skriptovatelné aplikace Macintosh a také části samotného systému macOS. Můžete vytvářet skripty – sady písemných pokynů – k automatizaci opakujících se úloh, kombinovat funkce z více skriptovatelných aplikací a vytvářet složité pracovní postupy.

Chcete-li si AppleScript vyzkoušet, můžete k psaní prototypových funkcí použít editor skriptů aplikace zabudovaný v systému macOS. Deklarace funkce začíná znakem on a končí znakem end . Abych se vyhnul případným konfliktům se systémovými funkcemi, používám obvykle jako předponu my. Zde se spoléhám na to, že systémové události získají domovský adresář.

Skriptovací terminologii uživatelského rozhraní naleznete ve slovníku skriptů „Processes Suite“ v části „System Events“. Tato sada obsahuje terminologii pro interakci s většinou typů prvků uživatelského rozhraní, včetně:

  • oken
  • tlačítek
  • zaškrtávacích políček
  • menu
  • radiových tlačítek
  • textových polí.

V systémových událostech představuje třída process spuštěnou aplikaci.

Mnoho dobrých občanských aplikací podporuje AppleScript vystavením některých svých funkcí, takže je mohou využívat jiné aplikace. Takto získám aktuální skladbu ze Spotify v aplikaci Lyrics.

tell application "Spotify" set trackId to id of current track as string set trackName to name of current track as string set artworkUrl to artwork url of current track as string set artistName to artist of current track as string set albumName to album of current track as string return trackId & "---" & trackName & "---" & artworkUrl & "---" & artistName & "---" & albumNameend tell

Chceme-li získat všechny možné příkazy určité aplikace, můžeme otevřít slovník v Editoru skriptů. Tam se dozvíme, které funkce a parametry jsou podporovány.

Pokud si myslíte, že Objective C je těžké, AppleScript je mnohem těžší. Syntaxe je mnohomluvná a náchylná k chybám. Pro vaši informaci uvádíme celý soubor skriptu, který pohání XcodeWay.

Chcete-li otevřít určitou složku, řekněte to Finder pomocí POSIX file. Kvůli lepšímu opakovanému použití kódu refaktoruji každou funkci do funkce.

on myOpenFolder(myPath)tell application "Finder"activateopen myPath as POSIX fileend tellend myOpenFolder

Chceme-li spustit AppleScript uvnitř aplikace nebo rozšíření systému MacOS, musíme zkonstruovat deskriptor AppleScriptu se správným pořadovým číslem procesu a identifikátory událostí.

func eventDescriptior(functionName: String) -> NSAppleEventDescriptor { var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess)) let target = NSAppleEventDescriptor( descriptorType: typeProcessSerialNumber, bytes: &psn, length: MemoryLayout<ProcessSerialNumber>.size )
let event = NSAppleEventDescriptor( eventClass: UInt32(kASAppleScriptSuite), eventID: UInt32(kASSubroutineEvent), targetDescriptor: target, returnID: Int16(kAutoGenerateReturnID), transactionID: Int32(kAnyTransactionID) )
let function = NSAppleEventDescriptor(string: functionName) event.setParam(function, forKeyword: AEKeyword(keyASSubroutineName))
return event}

Další úlohy, například kontrola aktuálního vzdáleného serveru Git, jsou trochu složitější. Mnohokrát chci sdílet odkaz na soubor, který ladím, se vzdáleným kolegou z týmu, aby věděl, na jaký soubor odkazuji. To lze provést pomocí shell script uvnitř AppleScript .

on myGitHubURL()set myPath to myProjectPath()set myConsoleOutput to (do shell script "cd " & quoted form of myPath & "; git remote -v")set myRemote to myGetRemote(myConsoleOutput)set myUrl to (do shell script "cd " & quoted form of myPath & "; git config --get remote." & quoted form of myRemote & ".url")set myUrlWithOutDotGit to myRemoveSubString(myUrl, ".git")end myGitHubURL

K vytvoření řetězců můžeme použít quoted a spojování řetězců. Naštěstí můžeme vystavit rámec Foundation a některé třídy. Zde uvádím, jak vystavím NSString, abych využil všechny existující funkce. Psaní manipulace s řetězci od začátku pomocí obyčejného AppleScriptu zabere spoustu času.

use scripting additionsuse framework "Foundation"property NSString : a reference to current application's NSString

Pomocí toho můžeme vytvořit naše další funkce pro práci s řetězci.

on myRemoveLastPath(myPath)set myString to NSString's stringWithString:myPathset removedLastPathString to myString's stringByDeletingLastPathComponentremovedLastPathString as textend myRemoveLastPath

Jednou ze skvělých funkcí, které XcodeWay podporuje, je možnost přejít do adresáře dokumentů aktuální aplikace v simulátoru. To se hodí, když potřebujeme zkontrolovat dokument a zkontrolovat uložená data nebo data v mezipaměti. Adresář je dynamický, takže je těžké ho zjistit. Můžeme však adresář seřadit podle poslední aktualizace. Níže je uvedeno, jak zřetězíme několik příkazů shell scripts pro vyhledání adresáře.

on myOpenDocument()set command1 to "cd ~/Library/Developer/CoreSimulator/Devices/;"set command2 to "cd `ls -t | head -n 1`/data/Containers/Data/Application;"set command3 to "cd `ls -t | head -n 1`/Documents;"set command4 to "open ."do shell script command1 & command2 & command3 & command4end myOpenDocument

Tato funkce mi velmi pomohla při vývoji Galerie pro kontrolu, zda jsou videa a stažené obrázky uloženy na správném místě.

Jednak se zdá, že žádný ze skriptů nefunguje. Skriptování bylo vždy součástí systému macOS od roku 1993. Ale s příchodem Mac Appstore a obavami o bezpečnost byl AppleScript v polovině roku 2012 nakonec omezen. Tehdy se prosadil App Sandbox.

App Sandbox

App Sandbox je technologie řízení přístupu poskytovaná v systému macOS a vynucovaná na úrovni jádra. Je navržena tak, aby omezila poškození systému a uživatelských dat, pokud dojde ke kompromitaci aplikace. Aplikace distribuované prostřednictvím obchodu Mac App Store musí App Sandbox přijmout.

Aby mohlo být rozšíření Xcode načteno aplikací Xcode, musí také podporovat App Sandbox.

Na začátku vynucování App Sandboxu jsme mohli použít dočasnou výjimku App Sandboxu, abychom dočasně udělili naší aplikaci přístup k Apple Scriptu.

To nyní není možné.

Jediný způsob, jak AppleScript spustit, je, že se nachází uvnitř složky ~/Library/Application Scripts.

Jak instalovat vlastní skripty

Aplikace nebo rozšíření systému MacOS nemohou samy instalovat skripty do složky Application Scripts. Potřebují k tomu souhlas uživatele.

Jedním z možných způsobů je povolit Read/Write a pomocí NSOpenPanel zobrazit dialogové okno, které uživatele požádá o výběr složky pro instalaci našich skriptů.

Pro XcodeWay jsem se rozhodl poskytnout instalační shellový skript, aby měl uživatel rychlý způsob instalace skriptů.

#!/bin/bash
set -euo pipefail
DOWNLOAD_URL=https://raw.githubusercontent.com/onmyway133/XcodeWay/master/XcodeWayExtensions/Script/XcodeWayScript.scptSCRIPT_DIR="${HOME}/Library/Application Scripts/com.fantageek.XcodeWayApp.XcodeWayExtensions"
mkdir -p "${SCRIPT_DIR}"curl $DOWNLOAD_URL -o "${SCRIPT_DIR}/XcodeWayScript.scpt"

AppleScript je velmi mocný. Vše je explicitní, takže uživatel má plnou kontrolu nad tím, které věci lze provádět.

Stejně jako rozšíření se skript provádí asynchronně v jiném procesu pomocí XPC pro komunikaci mezi procesy. To zvyšuje bezpečnost, protože skript nemá přístup do adresního prostoru k naší aplikaci nebo rozšíření.

Více zabezpečení v macOS Mojave

Na letošní konferenci WWDC 2018 představil Apple macOS Mojave, který se zaměřuje na spoustu vylepšení zabezpečení. V článku Vaše aplikace a budoucnost zabezpečení macOS se můžeme dozvědět více o nových požadavcích na zabezpečení aplikací pro macOS. Jedním z nich je popis použití pro AppleEvents.

neumožňuje načíst výjimky info.plist (egpu overrides)

V systému iOS jsme zvyklí deklarovat popis použití pro mnoho oprávnění, jako je knihovna fotografií, fotoaparát a push oznámení. Nyní musíme deklarovat popis použití pro AppleEvents.

Zdroj: https://www.felix-schwarz.org/blog/2018/08/new-apple-event-apis-in-macos-mojave

Při prvním pokusu našeho rozšíření o spuštění některých příkazů jazyka AppleScript se zobrazí výše uvedené dialogové okno s žádostí o souhlas uživatele. Uživatel může souhlas udělit nebo odepřít, ale pro Xcode prosím řekněte ano ?

Oprava pro nás spočívá v deklaraci NSAppleEventsUsageDescription v cíli naší aplikace. Musíme deklarovat pouze v cíli aplikace, nikoli v cíli rozšíření.

<key>NSAppleEventsUsageDescription</key><string>Use AppleScript to open folders</string>

Kam dál

Huff huff, whew! Díky za sledování tak dlouhé cesty. Tvorba frameworků a nástrojů zabere spoustu času, zejména zásuvných modulů a rozšíření – musíme je neustále měnit, abychom je přizpůsobili novým operačním systémům a bezpečnostním požadavkům. Ale je to obohacující proces, protože jsme se naučili více a máme některé nástroje, které nám šetří drahocenný čas.

Pro vaši informaci, zde jsou moje rozšíření, která jsou plně open source.

  • XcodeWay
  • XcodeColorSense2

.