Articles

Hogyan alakítsuk át az Xcode bővítményeinket Xcode-bővítményekké

by Khoa Pham

Forrás: Imgur

Az Xcode egy nélkülözhetetlen IDE az iOS és macOS fejlesztők számára. A korai idők óta az egyéni bővítmények készítésének és telepítésének lehetősége óriási termelékenységi lökést adott nekünk. Nem sokkal később az Apple az adatvédelmi aggályok miatt bevezette az Xcode bővítményt.

Elkészítettem néhány Xcode bővítményt és bővítményt, mint például az XcodeWay, az XcodeColorSense, az XcodeColorSense2 és az Xmas. Ez egy kifizetődő tapasztalat volt. Sokat tanultam, és a termelékenység, amit elértem, jelentős volt. Ebben a bejegyzésben végigkövetem, hogyan alakítottam át az Xcode pluginjaimat bővítményekké, és milyen tapasztalataim voltak ezzel kapcsolatban.

Az első Xcode pluginom: XcodeWay

Lusta embert választok egy nehéz munkához. Mert egy lusta ember megtalálja a könnyű módját

Nagyon tetszik a fenti idézet Bill Gates-től. Igyekszem elkerülni az ismétlődő és unalmas feladatokat. Amikor újra azon kapom magam, hogy ugyanazokat a feladatokat végzem, szkripteket és eszközöket írok, hogy automatizáljam azt. Ennek elvégzése időigényes, de a közeljövőben kicsit lustább leszek.

A nyílt forráskódú keretrendszerek és eszközök építése iránti érdeklődés mellett szeretem bővíteni az általam használt IDE-t – leginkább az Xcode-ot.

2014-ben kezdtem el az iOS-fejlesztést. Szerettem volna egy gyors módot arra, hogy közvetlenül az Xcode-ból sok helyre navigálhassak az aktuális projekt kontextusában. Sokszor van, amikor szeretnénk:

  • megnyitni az aktuális projekt mappát a “Finder”-ben, hogy megváltoztassunk néhány fájlt
  • megnyitni a Terminált, hogy futtassunk néhány parancsot
  • megnyitni az aktuális fájlt a GitHubban, hogy gyorsan átadjuk a linket egy munkatársnak
  • vagy megnyitni más mappákat, például témákat, pluginokat, kódrészleteket, eszköznaplókat.

Minden kis idő, amit minden nap megspórolunk, számít.

Azt gondoltam, hogy menő ötlet lenne egy Xcode plugint írni, amivel a fenti dolgokat közvetlenül az Xcode-on belül tudjuk elvégezni. Ahelyett, hogy megvártam volna, hogy mások megcsinálják, felhúztam az ingujjamat, és megírtam az első Xcode pluginomat – XcodeWay- és megosztottam nyílt forrásként.

Az XcodeWay úgy működik, hogy létrehoz egy menüt a Editor alatt, rengeteg lehetőséggel, hogy más helyekre navigáljunk közvetlenül az Xcode-ból. Egyszerűnek tűnik, de volt benne némi kemény munka.

Mi az Xcode plugin?

Az Xcode pluginokat az Xcode hivatalosan nem támogatja, és az Apple sem ajánlja. Nincsenek róluk dokumentumok. A legjobb helyek, ahol megismerhetjük őket, a meglévő pluginok forráskódja és néhány oktatóanyag.

Egy Xcode plugin nem más, mint egy xcplugin típusú csomag, és a ~/Library/Application Support/Developer/Shared/Xcode/Plug-ins . Az Xcode indításkor betölti az ebben a mappában található Xcode bővítményeket. A pluginok ugyanabban a folyamatban futnak, mint az Xcode, így bármit megtehetnek, mint az Xcode. Egy hiba bármelyik pluginban az Xcode összeomlását okozhatja.

Egy Xcode plugin elkészítéséhez hozzunk létre egy macOS Bundle-t egy olyan osztállyal, amely a NSObject-ből bővül, és van egy inicializálója, amely elfogadja a NSBundle -t, például az Xmas-ban:

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

A Info.plist belsejében kell:

  • declare this class as the main entry class for the plugin, and
  • that this bundle has no UI, because we create UI controls and add to the Xcode interface during runtime
<key>NSPrincipalClass</key><string>Xmas</string><key>XCPluginHasUI</key><false/>

Another problem with Xcode plugins is that we have to folyamatos update DVTPluginCompatibilityUUIDs . Ez minden alkalommal változik, amikor az Xcode új verziója megjelenik. Frissítés nélkül az Xcode nem hajlandó betölteni a bővítményt.

Mit tudnak az Xcode bővítmények

Sok fejlesztő azért épít Xcode bővítményeket, mert hiányolják a más IDE-kben, például a Sublime Textben, az AppCode-ban vagy az Atomban található speciális funkciókat.

Mivel az Xcode bővítmények ugyanabban a folyamatban töltődnek be, mint az Xcode, mindent tudnak, amit az Xcode is. Az egyetlen korlátot a képzeletünk jelenti. Az Objective C Runtime-ot kihasználva privát keretrendszereket és függvényeket fedezhetünk fel. Ezután az LLDB és a Symbolic breakpoint tovább használható a futó kód vizsgálatára és viselkedésük megváltoztatására. Használhatjuk a swizzlinget is, hogy megváltoztassuk bármely futó kód végrehajtását. Az Xcode pluginok írása nehéz – sok találgatás, és néha az assembly jó ismerete is szükséges.

A pluginok aranykorában volt egy népszerű plugin-kezelő, amely maga is egy plugin volt, Alcatraz néven. Ez képes volt más pluginokat telepíteni, ami lényegében csak letölti a xcplugin fájlt, és ezt áthelyezi a Plug Ins mappába.

Hogy érzékeltessük, mit tudnak a pluginok, nézzünk meg néhány népszerű plugint.

Xvim

A lista első helyén az Xvim áll, ami Vim billentyűkötéseket ad hozzá közvetlenül az Xcode-on belül. Többnyire az összes olyan billentyűkötést támogatja, amelyet korábban a Terminalban használtunk.

SCXcodeMiniMap

Ha hiányolod a Sublime Textben a MiniMap módot, akkor az SCXcodeMiniMap segítségével hozzáadhatsz egy jobb oldali térképpanelt az Xcode szerkesztőn belül.

FuzzyAutocompletePlugin

A 9-es verzió előtt az Xcode nem rendelkezett megfelelő automatikus kiegészítéssel – csak az előtag alapján működött. Ez volt az a terület, ahol a FuzzyAutocompletePlugin tündökölt. Az Xcode rejtett IDEOpenQuicklyPattern funkcióján alapuló fuzzy automatikus kiegészítést hajt végre.

KSImageNamed-Xcode

A kötegelt kép UIImageView belsejében történő megjelenítéséhez gyakran használjuk a imageNamed módszert. De a képfájl nevének pontos megjegyzése nehézkes. A KSImageNamed-Xcode itt van, hogy segítsen. Egy listát kapunk az automatikusan javasolt képnevekből, amikor elkezdünk gépelni.

ColorSense-for-Xcode

Egy másik viszketés a fejlesztés során a UIColor , amely RGBA színteret használ. Nem kapunk vizuális jelzést az általunk megadott színről, és az ellenőrzés manuális elvégzése időigényes lehet. Szerencsére létezik a ColorSense-for-Xcode, amely mutatja a használt színt, és a színválasztó panel segítségével könnyen kiválaszthatjuk a megfelelő színt.

LinkedConsole

Az AppCode-ban a konzolon belül naplózott fájl egy adott sorára ugorhatunk. Ha ezt a funkciót hiányoljuk az Xcode-ban, akkor használhatjuk a LinkedConsole-t. Ez az Xcode konzolon belül kattintható hivatkozásokat tesz lehetővé, így azonnal az adott fájlra ugorhatunk.

Az Xcode bővítmények mögötti kemény munka

Egy Xcode bővítmény elkészítése nem egyszerű. Nemcsak a macOS programozását kell ismernünk, hanem mélyen el kell merülnünk az Xcode nézethierarchiájában is. Saját keretrendszereket és API-kat kell felfedeznünk ahhoz, hogy beilleszthessük a kívánt funkciót.

Nagyon kevés oktatóanyag létezik a pluginok készítéséről, de szerencsére a legtöbb plugin nyílt forráskódú, így megérthetjük a működésüket. Mivel én már készítettem néhány plugint, tudok néhány technikai részletet mondani róluk.

Az Xcode pluginek általában két privát keretrendszerrel készülnek: DVTKit és IDEKit . A rendszer keretrendszerek a /System/Library/PrivateFrameworks alatt vannak, de az Xcode által kizárólagosan használt keretrendszerek a /Applications/Xcode.app/Contents/ alatt vannak , ott találhatóak a Frameworks , OtherFrameworks és SharedFrameworks.

Van egy class-dump nevű eszköz, ami képes fejléceket generálni az Xcode alkalmazáscsomagból. Az osztálynevekkel és metódusokkal a NSClassFromString meghívásával a névből megkaphatjuk az osztályt.

Swizzling DVTBezelAlertPanel keretrendszer Xmas

A karácsony mindig is különleges érzést adott nekem, ezért úgy döntöttem, hogy elkészítem az Xmas-t, ami az alapértelmezett alert nézet helyett egy véletlenszerű karácsonyi képet mutat. A nézet megjelenítéséhez használt osztály a DVTBezelAlertPanel a DVTKit keretrendszeren belül. A plugin építéséről szóló cikkem itt található.

Az Objective C Runtime-ban létezik egy swizzling nevű technika, amely képes bármely futó osztály és metódus implementációjának és metódusszignatúrájának megváltoztatására és cseréjére.

Itt ahhoz, hogy megváltoztassuk ennek az alert nézetnek a tartalmát, az inicializáló initWithIcon:message:parentWindow:duration:-t kell kicserélnünk a saját metódusunkra. Ezt korán megtesszük a NSApplicationDidFinishLaunchingNotification figyelésével, amely értesítést kap, amikor egy macOS plugin, jelen esetben az Xcode, elindul.

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") }}

Eredetileg szerettem mindent Swiftben csinálni. De a Swiftben trükkös a swizzle init metódus használata, ezért a leggyorsabb, ha ezt Objective C-ben csináljuk. Ezután egyszerűen végigjárjuk a nézethierarchiát, hogy megtaláljuk a NSVisualEffectView-t a NSPanel-ben, hogy frissítsük a képet.

Interakció a DVTSourceTextView-val az Xcode-banColorSense

Többnyire hexa színekkel dolgozom, és szeretnék egy gyors módszert a színek megtekintésére. Ezért építettem az XcodeColorSense-t – támogatja a hex színt, az RGBA-t és a named color-t.

Az ötlet egyszerű. Elemezze a karakterláncot, hogy lássa, ha a felhasználó a UIColor-hoz kapcsolódóan ír be valamit, és jelenítsen meg egy kis overlay nézetet ezzel a színnel háttérként. Az Xcode által használt szövegnézet DVTSourceTextView típusú a DVTKit keretrendszerben. Figyelnünk kell a NSTextViewDidChangeSelectionNotification-re is, amely akkor lép működésbe, amikor bármilyen NSTextView tartalom megváltozik.

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}

Volt egy Matcher architektúra, így felismerhetjük a különböző típusú UIColor konstrukciókat – például 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) }}

Az overlay megjelenítéséhez a NSColorWell-t használjuk, amely jó a háttérrel ellátott nézet megjelenítésére. A pozíciót a firstRectForCharacterRange hívásával és néhány pontkonverzióval határozzuk meg a convertRectFromScreen és convertRect segítségével.

NSTask és IDEWorkspaceWindowController használata az XcodeWayben

Végre az én szeretett XcodeWayem.

Az Xcode-ból különböző helyekre kellett mennem az aktuális projekt kontextusával. Így az XcodeWay-t egy olyan plugin-ként építettem meg, amely sok praktikus menüpontot ad hozzá a Window alatt.

Mivel a plugin ugyanabban az Xcode-folyamatban fut, hozzáfér a főmenühöz NSApp.mainMenu?.itemWithTitle("Window") . Ott tudjuk módosítani a menüt. Az XcodeWay úgy lett kialakítva, hogy a Navigator protokollon keresztül könnyen bővítse a funkciókat.

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

A statikus elérési úttal rendelkező mappák esetében, mint például a Provisioning Profile ~/Library/MobileDevice/Provisioning Profiles vagy a User data Developer/Xcode/UserData , egyszerűen felépíthetjük a URL és meghívhatjuk a NSWorkspace.sharedWorkspace().openURL . A dinamikus mappák esetében, amelyek az aktuális projekttől függően változnak, több munkát kell végezni.

Hogyan nyissuk meg az aktuális projekt mappáját a Finderben? Az aktuális projekt elérési útvonalára vonatkozó információkat a IDEWorkspaceWindowController mappában tartjuk. Ez egy osztály, amely a munkaterület ablakokat kezeli az Xcode-ban. Nézzük meg az EnvironmentManager-t, ahol az objc_getClass segítségével megkapjuk az osztálydefiníciót egy karakterláncból.

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

Végül felhasználhatjuk a valueForKey-t, hogy megkapjuk bármely tulajdonság értékét, amelyről úgy gondoljuk, hogy létezik. Így nem csak a projekt elérési útvonalát kapjuk meg, hanem a nyitó fájl elérési útvonalát is. Így meg tudjuk hívni a activateFileViewerSelectingURLs-t a NSWorkspace-ra, hogy megnyissuk a Findert az adott fájl kiválasztásával. Ez praktikus, mivel a felhasználóknak nem kell megkeresniük az adott fájlt a Finderben.

Néhányszor szeretnénk néhány terminálparancsot végrehajtani az aktuális projektmappán. Ehhez használhatjuk a NSTask parancsot a /usr/bin/open indítópaddal és a argumentumokkal. Az iTerm, ha valószínűleg úgy van beállítva, megnyitja ezt egy új lapon.

Az iOS 7 alkalmazások dokumentumai a iPhone Simulator rögzített helyre kerülnek az Alkalmazástámogatáson belül. De az iOS 8-tól kezdve minden alkalmazás egyedi UUID-vel rendelkezik, és a dokumentummappáikat nehéz megjósolni.

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

Elkészíthetünk egy térképet, és nyomon követést végezhetünk, hogy megtaláljuk az aktuális projekt generált azonosítóját, vagy ellenőrizhetjük az egyes mappákon belüli plist-et, hogy összehasonlítsuk a csomagazonosítót.

A gyors megoldás, amivel előálltam, a legfrissebb frissített mappa keresése volt. Minden alkalommal, amikor létrehozzuk a projektet, vagy változtatásokat végzünk az alkalmazáson belül, a dokumentum mappájuk frissül. Itt használhatjuk a NSFileModificationDate-t, hogy megtaláljuk az aktuális projekt mappáját.

Az Xcode bővítményekkel való munka során sok hack van, de az eredmények kifizetődőek. Minden néhány perc, amit naponta megspórolunk, végül összességében sok időt takarít meg.

Biztonság és szabadság

A nagy hatalommal nagy felelősség is jár. Az a tény, hogy a bővítmények azt csinálhatnak, amit akarnak, riasztóan cseng a biztonsággal kapcsolatban. 2015 végén volt egy rosszindulatú támadás az Xcode módosított, XcodeGhost nevű verziójának terjesztésével, amely rosszindulatú kódot injektál minden olyan alkalmazásba, amelyet az Xcode Ghost segítségével építettek. A rosszindulatú szoftver feltehetően többek között a plugin mechanizmust használja.

Az Appstore-ból letöltött iOS-alkalmazásokhoz hasonlóan az Xcode-hoz hasonló macOS-alkalmazásokat is aláírja az Apple, amikor a Mac Appstore-ból vagy az Apple hivatalos letöltési hivatkozásain keresztül töltjük le őket.

A kódaláírás biztosítja a felhasználókat arról, hogy az alkalmazás ismert forrásból származik, és az alkalmazás nem változott az utolsó aláírás óta. Mielőtt az alkalmazásod integrálhatná az alkalmazásszolgáltatásokat, telepíthető lenne egy eszközre, vagy beküldhető lenne az App Store-ba, az Apple által kiadott tanúsítvánnyal kell aláírni

Az ehhez hasonló potenciális rosszindulatú programok elkerülése érdekében az Apple a WWDC 2016-on bejelentette az Xcode Source Editor Extensiont, amely az egyetlen módja annak, hogy harmadik féltől származó bővítményeket töltsön be az Xcode-ba. Ez azt jelenti, hogy az Xcode 8-tól a bővítményeket nem lehet betölteni.

Source Editor Extension

A bővítmény az ajánlott megközelítés a funkciók biztonságos, korlátozott módon történő hozzáadásához.

Az alkalmazásbővítmények segítségével a felhasználók hozzáférhetnek az alkalmazás funkcióihoz és tartalmához az iOS és a macOS egész területén. Például az alkalmazásod mostantól megjelenhet widgetként a Ma képernyőn, új gombokat adhat hozzá a Művelet lapon, fotószűrőket kínálhat a Fotók alkalmazáson belül, vagy megjeleníthet egy új, rendszerszintű egyéni billentyűzetet.

Előre az Xcode egyetlen kiterjesztése a Forrásszerkesztő, amely lehetővé teszi számunkra egy forrásfájl tartalmának olvasását és módosítását, valamint az aktuális szövegkiválasztás olvasását és módosítását a szerkesztőn belül.

A kiterjesztés egy új cél, és az Xcode-tól eltérő folyamatban fut. Ez annyiban jó, hogy nem tudja megváltoztatni az Xcode-ot azon kívül, hogy megfelel a XCSourceEditorCommand-nak az aktuális dokumentum tartalmának módosításához.

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

Az Xcode 8 rengeteg fejlesztést tartalmaz, például az új kódkiegészítési funkciókat, a Swift kép- és színliterálokat, valamint a snippeket. Ez számos Xcode bővítmény elavulásához vezetett. Néhány nélkülözhetetlen plugin, például az XVim esetében ez egyesek számára elviselhetetlen. Néhány régi plugin funkciót nem lehet elérni a jelenlegi Source Editor Extension rendszerrel.

Hacsak nem mond le az Xcode-ról

Az Xcode 8-tól a pluginekre vonatkozó korlátozás megkerülésére szolgáló megoldás az, hogy a meglévő Xcode aláírást egy lemondásnak nevezett technikával helyettesítjük. A lemondás nagyon egyszerű – csak létre kell hoznunk egy saját aláírású tanúsítványt, és meg kell hívnunk a codesign parancsot. Ezt követően az Xcode-nak képesnek kell lennie a pluginok betöltésére.

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

A lemondott Xcode-dal épített alkalmazásokat azonban nem lehet beküldeni, mivel az aláírás nem felel meg az Xcode hivatalos verziójának. Az egyik megoldás két Xcode használata: egy hivatalos a terjesztéshez és egy lemondott a fejlesztéshez.

Áttérés az Xcode kiterjesztésre

Az Xcode kiterjesztés a helyes út, ezért elkezdtem a bővítményeimet a kiterjesztésbe költöztetni. Az Xmas esetében, mivel módosítja a nézeti hierarchiát, nem válhat kiterjesztéssé.

Color literal az XcodeColorSense2-ben

A színérzékeléshez újraírtam a kiterjesztést a semmiből, és XcodeColorSense2-nek neveztem el. Ez természetesen nem tud overlay-t mutatni az aktuális szerkesztő nézet fölött. Ezért úgy döntöttem, hogy az Xcode 8+-ban található új Color literal-t használom.

A szín egy kis dobozban jelenik meg. Nehéz lehet megkülönböztetni a hasonló színeket, ezért a nevet is feltüntetem. A kód egyszerűen a selections vizsgálatáról és a színdeklaráció megtalálásának elemzéséről szól.

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) }}

A legtöbb funkció a Farge keretrendszerembe van beágyazva, de nem találom a keretrendszer Xcode bővítményen belüli használatának módját.

Mivel a bővítmény funkció csak a Szerkesztő menüben érhető el, testre szabhatunk egy billentyűkötést ennek a menüpontnak a meghívására. Én például a Cmd+Ctrl+S-t választom a színinformációk megjelenítéséhez és elrejtéséhez.

Ez persze nem intuitív az eredeti bővítményhez képest, de jobb, mint a semmi.

Hogyan hibakereshetjük az Xcode-bővítményeket

A bővítmények működése és hibakeresése egyszerű. Használhatjuk az Xcode-ot az Xcode hibakeresésére. Az Xcode hibakeresett verziójának szürke ikonja van.

Hogyan telepíthetjük az Xcode-bővítményeket

A bővítménynek rendelkeznie kell egy hozzá tartozó macOS alkalmazással. Ez lehet a Mac Appstore-ban terjesztett vagy önaláírt. Írtam egy cikket arról, hogyan kell ezt megtenni.

Minden alkalmazáshoz tartozó bővítményt kifejezetten engedélyezni kell a “Rendszerbeállításokon” keresztül.

Az Xcode bővítmény egyelőre csak szerkesztővel működik, tehát meg kell nyitnunk egy forrásfájlt ahhoz, hogy a Editor menü hatása érvényesüljön.

AppleScript az XcodeWayben

Az Xcode bővítményekben a NSWorkspace, NSTask és a private class építés már nem működik. Mivel a FinderGo-ban használtam a Finder Sync Extension-t, gondoltam, kipróbálhatom ugyanezt az AppleScript szkriptelést az Xcode bővítményben.

Az AppleScript az Apple által létrehozott szkriptnyelv. Lehetővé teszi a felhasználók számára, hogy közvetlenül vezéreljék a szkriptelhető Macintosh alkalmazásokat, valamint magának a macOS-nek egyes részeit. Szkripteket – írott utasításkészleteket – hozhat létre ismétlődő feladatok automatizálására, több szkriptelhető alkalmazás funkcióinak kombinálására és összetett munkafolyamatok létrehozására.

Az AppleScript kipróbálásához használhatja a macOS-be épített App Script Editor alkalmazást prototípusfüggvények írására. A függvénydeklaráció on-vel kezdődik és end-vel végződik . A rendszerfüggvényekkel való esetleges konfliktusok elkerülése érdekében általában a my előtagot használom. Itt van, hogyan támaszkodom a System Events-re a home könyvtár megszerzéséhez.

A felhasználói felület szkriptelésének terminológiája a “System Events” szkript szótár “Processes Suite” részében található. Ez a csomag tartalmazza a felhasználói felület legtöbb típusú elemével való interakció terminológiáját, beleértve:

  • ablakok
  • gombok
  • jelölőnégyzetek
  • menük
  • rádiógombok
  • szövegmezők.

A Rendszeres eseményekben a process osztály egy futó alkalmazást reprezentál.

Néhány jó polgár alkalmazás támogatja az AppleScriptet azáltal, hogy egyes funkcióit feltárja, így azokat más alkalmazások is használhatják. Így kapom meg az aktuális dalt a Spotify-ból a Lyrics-ben.

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

Hogy egy adott alkalmazás összes lehetséges parancsát megkapjuk, megnyithatjuk a szótárat a Script Editorban. Ott megtudhatjuk, hogy mely függvények és paraméterek támogatottak.

Ha azt hiszed, hogy az Objective C nehéz, az AppleScript sokkal nehezebb. A szintaxis bőbeszédű és hibalehetőségekkel teli. Referenciaként itt van a teljes szkriptfájl, amely az XcodeWay-t hajtja.

Az adott mappa megnyitásához mondd meg a Finder-nak a POSIX file segítségével. A jobb kódújrafelhasználás érdekében minden funkciót funkcióvá refaktorálok.

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

Az AppleScript futtatásához egy macOS alkalmazáson vagy bővítményen belül egy AppleScript descriptort kell létrehoznunk a megfelelő folyamat sorszámmal és eseményazonosítókkal.

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}

Más feladatok, mint például az aktuális Git-távolság ellenőrzése, egy kicsit trükkösebbek. Sokszor szeretném megosztani a hibakeresés alatt álló fájl linkjét a távoli csapattársammal, hogy tudják, milyen fájlra hivatkozom. Ez a 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

belsejében shell script használatával megvalósítható. quoted és a karakterláncok összekapcsolását használhatjuk a karakterláncok kialakításához. Szerencsére ki tudjuk tenni a Foundation keretrendszert és bizonyos osztályokat. Itt van, hogyan exponálom a NSString-t, hogy kihasználjam az összes létező funkcionalitást. A stringmanipuláció megírása a semmiből a sima AppleScript használatával rengeteg időt vesz igénybe.

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

Ezzel felépíthetjük a többi stringkezelő függvényünket.

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

Egy klassz funkció, amit az XcodeWay támogat, hogy a szimulátorban az aktuális alkalmazás dokumentumkönyvtárába léphetünk. Ez praktikus, amikor egy dokumentumot kell megvizsgálnunk, hogy ellenőrizzük a mentett vagy gyorsítótárazott adatokat. A könyvtár dinamikus, így nehéz felismerni. Rendezhetjük azonban a könyvtárat a legutóbb frissítettek szerint. Az alábbiakban több shell scripts parancsot láncolunk össze, hogy megtaláljuk a mappát.

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

Ez a funkció sokat segített nekem a Galéria fejlesztésénél, hogy ellenőrizzem, hogy a videók és a letöltött képek a megfelelő helyre vannak-e mentve.

A szkriptek közül azonban úgy tűnik, egyik sem működik. A szkriptelés 1993 óta mindig is a macOS része volt. De a Mac Appstore megjelenésével és a biztonsági aggályokkal az AppleScript végül 2012 közepén korlátozva lett. Ekkor került bevezetésre az App Sandbox.

App Sandbox

Az App Sandbox a macOS-ben biztosított hozzáférés-szabályozási technológia, amelyet a kernel szintjén hajtanak végre. Célja a rendszer és a felhasználói adatok károsodásának megfékezése, ha egy alkalmazás veszélybe kerül. A Mac App Store-on keresztül terjesztett alkalmazásoknak el kell fogadniuk az App Sandboxot.

Hogy egy Xcode-bővítményt az Xcode betölthessen, annak szintén támogatnia kell az App Sandboxot.

Az App Sandbox érvényesítésének kezdetén az App Sandbox Temporary Exceptiont használhattuk, hogy ideiglenesen hozzáférést adjunk az alkalmazásunknak az Apple Scripthez.

Ez most már nem lehetséges.

Az AppleScript csak akkor futtatható, ha a ~/Library/Application Scripts mappában található.

Hogyan telepíthetünk egyéni szkripteket

AmacOS alkalmazások vagy bővítmények nem telepíthetnek szkripteket csak úgy maguktól az Application Scripts-be. Szükségük van a felhasználó beleegyezésére.

Egy lehetséges módja ennek, hogy engedélyezzük a Read/Write funkciót, és a NSOpenPanel segítségével megjelenítünk egy párbeszédpanelt, amelyben megkérjük a felhasználót, hogy válassza ki a mappát a szkriptjeink telepítéséhez.

Az XcodeWay esetében úgy döntöttem, hogy egy telepítő shell scriptet biztosítok, hogy a felhasználónak gyors módja legyen a szkriptek telepítésére.

#!/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"

Az AppleScript nagyon erős. Mindez egyértelművé van téve, így a felhasználónak teljes kontrollja van, hogy milyen dolgokat végezhet el.

A szkript, akárcsak egy kiterjesztés, aszinkron módon, egy másik folyamatban történik, az XPC-t használva a folyamatok közötti kommunikációra. Ez növeli a biztonságot, mivel egy szkript nem fér hozzá az alkalmazásunk vagy bővítményünk címtartományához.

Több biztonság a macOS Mojave-ben

Az Apple idén, a WWDC 2018-on mutatta be a macOS Mojave-t, amely rengeteg biztonsági fejlesztésre összpontosít. A Your Apps and the Future of macOS Security című cikkben többet megtudhatunk a macOS-alkalmazásokkal szemben támasztott új biztonsági követelményekről. Ezek egyike az AppleEvents használati leírása.

unable to load info.plist exceptions (egpu overrides)

Az iOS-ben sok jogosultsághoz, például a fotótárhoz, a kamerához és a push értesítésekhez szoktuk megadni a használati leírást. Most az AppleEvents használatának leírását kell deklarálnunk.

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

Az első alkalommal, amikor a bővítményünk megpróbál végrehajtani néhány AppleScript parancsot, a fenti párbeszédpanel jelenik meg, amely a felhasználó beleegyezését kéri. A felhasználó megadhatja vagy megtagadhatja az engedélyt, de az Xcode esetében kérjük, mondjon igent?

A megoldás számunkra az, hogy deklaráljuk a NSAppleEventsUsageDescription-t az alkalmazásunk célpontjában. Csak az app targetben kell deklarálnunk, a extension targetben nem.

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

Hogyan tovább

Huff huff, whew! Köszönöm, hogy követtétek ezt a hosszú utat. A keretrendszerek és eszközök készítése sok időt vesz igénybe, különösen a pluginok és bővítmények – folyamatosan változtatnunk kell, hogy az új operációs rendszerekhez és biztonsági követelményekhez igazítsuk őket. De ez egy kifizetődő folyamat, mivel többet tanultunk, és van néhány eszközünk, amivel megspórolhatjuk a drága időnket.

Hivatkozásképpen itt vannak a bővítményeim, amelyek teljesen nyílt forráskódúak.

  • XcodeWay
  • XcodeColorSense2