Řetězcové literály v rychlém
Být schopen vyjádřit základní hodnoty, jako jsou řetězce a celá čísla, pomocí inline literály je základním prvkem ve většině programovacích jazyků. Nicméně, zatímco mnoho jiných jazyků má podporu pro konkrétní literály pečené do jejich kompilátoru, Swift zaujímá mnohem dynamičtější přístup-pomocí vlastního typového systému definuje, jak by se s různými literály mělo zacházet, prostřednictvím protokolů.
Tento týden, pojďme se zaměřit na řetězcové literály zejména tím, že se podívat na mnoho různých způsobů, které mohou být použity, a jak jsme se — přes Swift je vysoce protocol-oriented design — jsou schopni přizpůsobit způsob literály jsou interpretovány, což nám umožňuje dělat některé opravdu zajímavé věci.
Základní Vývojka: Připojit se zdarma online rychlokurz pro iOS vývojáře, kteří chtějí, aby se stal černý pás senior vývojáře — to je: dosáhněte odborné úrovně praktických dovedností a staňte se součástí nejlépe placených vývojářů na světě.
základy
Stejně jako v mnoha jiných jazycích, Rychlé řetězce jsou vyjádřeny prostřednictvím literály uzavřeny v uvozovkách a mohou obsahovat speciální sekvence (jako řádky), uvozena znaky, a interpolované hodnoty:
let string = "\(user.name) says \"Hi!\"\nWould you like to reply?"// John says "Hi!"// Would you like to reply?
Zatímco funkce používané výše již nám poskytují velkou flexibilitu, a je velmi pravděpodobné, dost pro drtivou většinu případů použití, tam jsou situace, ve kterých výkonnější způsoby, jak vyjádřit literály mohou přijít vhod. Podívejme se na některé z nich, počínaje tím, kdy potřebujeme definovat řetězec obsahující více řádků textu.
Víceřádkové literály
i když všechny standardní řetězec literál může být rozdělen do více řádků pomocí \n
, to není vždy praktické — zejména pokud chceme definovat větší kus textu jako inline doslovný.
naštěstí, od Swift 4, jsme také schopni definovat víceřádkové řetězcové literály pomocí tří uvozovek místo jedné. Zde například používáme tuto schopnost k výstupu textu nápovědy pro skript Swift v případě, že uživatel při vyvolání na příkazovém řádku nepředal žádné argumenty:
// We're comparing against 1 here, since the first argument passed// to any command line tool is the current path of execution.guard CommandLine.arguments.count > 1 else { print(""" To use this script, pass the following: - A string to process - The maximum length of the returned string """) // Exit the program with a non-zero code to indicate failure. exit(1)}
výše využíváme skutečnosti, že víceřádkové řetězcové literály zachovávají odsazení svého textu vzhledem k ukončovací sadě uvozovek ve spodní části. Umožňují nám také mnohem více volně používat unescaped uvozovek v nich, protože oni jsou definovány tři uvozovky, takže hranice doslovný mnohem méně pravděpodobné, aby se stal nejednoznačné.
Obě dvě výše uvedené charakteristiky, aby víceřádkové literály skvělý nástroj pro definování inline HTML — například v nějaké formě webové stránky generace nástroj, nebo při vykreslování části obsahu aplikace pomocí webových názory jako tento:
extension Article { var html: String { // If we want to break a multiline literal into separate // lines without causing an *actual* line break, then we // can add a trailing '\' to one of our lines. let twitterLink = """ <a href="https://twitter.com/\(author.twitterHandle)">\ @\(author.twitterHandle)</a> """ return """ <article> <h1>\(title)</h1> <div class="author"> <p>\(author.name)</p> \(twitterLink) </div> <div class="body">\(body)</div> </article> """ }}
výše uvedená technika může také být opravdu užitečné při definování řetězce založené na testovací data. Řekněme například, že nastavení naší aplikace musí být exportovatelné jako XML a že chceme napsat test, který tuto funkci ověří. Spíše než definovat XML, které chceme ověřit, proti v samostatném souboru — můžeme použít víceřádkový řetězec doslovný vložené do našeho testu:
class SettingsTests: XCTestCase { func testXMLConversion() { let settings = Settings( messageLimit: 7, enableSync: true, signature: "Sent from my Swift app" ) XCTAssertEqual(settings.xml, """ <?xml version="1.0" encoding="UTF-8"?> <settings> <messagelimit>7</messagelimit> <enablesync>1</enablesync> <signature>Sent from my Swift app</signature> </settings> """) }}
ve prospěch definování testovacích dat inline, jako my výše, je, že to se stává mnohem jednodušší, aby se rychle rozpoznat případné chyby při psaní testu — od testovací kód a očekávané výstupy jsou umístěny hned vedle sebe. Pokud však některá testovací data přesáhnou několik řádků na délku nebo pokud je třeba použít stejná data na více místech, stále se vyplatí přesunout je do vlastního souboru.
Raw řetězce
Nový v Swift 5, raw řetězců nám umožňují vypnout všechny dynamické řetězcový literál funkce (jako interpolace, a interpretace speciálních znaků, například \n
), ve prospěch jednoduše léčit doslovný, jak syrové posloupnost znaků. Surové řetězce jsou definovány obklopením doslovného řetězce se znaky libry (nebo „hashtagy“, jak jim děti říkají):
let rawString = #"Press "Continue" to close this dialog."#
Stejně jako, jak jsme výše použili víceřádkové doslovný definovat testovací data, surový řetězec literály jsou zvláště užitečné, když chceme inline řetězce, které musí obsahovat speciální znaky — například uvozovky nebo zpětná lomítka. Tady je další test-související příklad, ve kterém budeme používat raw string literal definovat řetězec JSON encode User
příklad:
class UserTests: XCTestCase { func testDecoding() throws { let json = #"{"id": 37, "name": "John"}"# let data = Data(json.utf8) let user = try data.decoded() as User XCTAssertEqual(user.id, 37) XCTAssertEqual(user.name, "John") }}
Nad používáme typ inference založené na dekódování API z „Type inference-poháněl serializace v Swift“.
Zatímco syrové řetězce zakázat funkce, jako je řetězec interpolace ve výchozím nastavení, tam je způsob, jak to změnit tím, že přidá další kila podepsat hned po interpolace přední lomítko — jako tento:
extension URL { func html(withTitle title: String) -> String { return #"<a href="\#(absoluteString)">\#(title)</a>"# }}
Konečně, syrové řetězce jsou také zvláště užitečné při interpretaci řetězec pomocí zvláštní syntaxe, zejména v případě, že syntaxe spoléhá na postavy, které by normálně třeba utekl do řetězcový literál — jako regulární výrazy. Tím, že definuje regulární výrazy pomocí syrové řetězce, není úniku, je potřeba, což nám výrazy, které jsou stejně čitelné, jako že si:
// This expression matches all words that begin with either an// uppercase letter within the A-Z range, or with a number.let regex = try NSRegularExpression( pattern: #"(()|(\d))\w+"#)
I s výše vylepšení, to je sporné, jak snadné přečíst (a ladit) regulární výrazy jsou — zejména při použití v kontextu vysoce typově bezpečný jazyk jako Swift. To bude s největší pravděpodobností přijít do dané vývojka předchozí zkušenosti s regulárními výrazy, zda nebo ne oni jim přednost před uplatněním více vlastní řetězec analýze algoritmů, přímo v Swift.
Vyjádření hodnot pomocí řetězcové literály
Zatímco všechny řetězcové literály jsou obrátil do String
hodnoty ve výchozím nastavení, můžeme také použít je, aby vyjádřil vlastní hodnoty, stejně. Jak jsme se podíval na v „Type-safe identifikátory v Swift“, a dodal, string literal podporu jednoho z našich typů můžete nechat nám dosáhnout zvýšené bezpečnost typ, bez obětování pohodlí pomocí literály.
například, řekněme, že jsme definovali Searchable
protokol se chovat jako API pro vyhledávání jakékoliv databáze nebo podkladové úložiště, že naše aplikace používá — a že jsme pomocí Query
enum na modelu různé způsoby, jak takové vyhledávání provádět:
protocol Searchable { associatedtype Element func search(for query: Query) -> }enum Query { case matching(String) case notMatching(String) case matchingAny()}
výše uvedený přístup nám dává hodně síly a flexibility, jak budeme provádět každý hledat, ale nejčastější případ použití je stále pravděpodobné, že bude nejjednodušší one — hledá prvky odpovídající daným řetězcem — a to by bylo opravdu hezké, kdyby jsme byli schopni udělat, že pomocí řetězcový literál.
dobrá zpráva je, že můžeme dělat to stalo, zatímco ještě drží nad API zcela neporušené, tím Query
přizpůsobit ExpressibleByStringLiteral
:
extension Query: ExpressibleByStringLiteral { init(stringLiteral value: String) { self = .matching(value) }}
tímto způsobem jsme nyní zdarma, aby provést odpovídající vyhledávání, aniž byste museli vytvořit Query
hodnoty ručně — vše, co musíme udělat, je projít řetězcový literál, jako kdyby API voláme vlastně přijal String
přímo. Zde tuto schopnost používáme k implementaci testu, který ověří, že typ UserStorage
správně implementuje svou vyhledávací funkci:
class UserStorageTests: XCTestCase { func testSearch() { let storage = UserStorage.inMemory let user = User(id: 3, name: "Amanda") storage.insert(user) let matches = storage.search(for: "anda") XCTAssertEqual(matches, ) }}
Vlastní řetězec doslovný výrazy mohou v mnoha situacích, dejte nám vyhnout se nutnosti vybrat mezi typem bezpečnost a pohodlí při práci s řetězci-podle typů, jako jsou dotazy a identifikátory. To může být skvělý nástroj k použití za účelem dosažení API design, který škáluje dobře, od nejjednodušší případ použití, celou cestu na pokrytí okraje případy a nabízí větší výkon a přizpůsobitelnost, když je potřeba.
vlastní interpolace
jedna věc, kterou mají všechny „příchutě“ Swift string literals společné, je jejich podpora interpolace hodnot. I když jsme vždy byli schopni přizpůsobit, jak je daný typ interpolován podle CustomStringConvertible
– Swift 5 zavádí nové způsoby implementace vlastních API přímo na vrcholu interpolačního motoru řetězce.
jako příklad Řekněme, že chceme uložit daný řetězec popřípadě použitím předpony a přípony. V ideálním případě bychom chtěli tyto hodnoty jednoduše interpolovat a vytvořit tak konečný řetězec, jako je tento:
func save(_ text: String, prefix: String?, suffix: String?) { let text = "\(prefix)\(text)\(suffix)" textStorage.store(text)}
Nicméně, protože oba prefix
suffix
jsou volitelné, pouze pomocí jejich popis nebude produkovat výsledek hledáme — a kompilátor bude i nám varováním:
String interpolation produces a debug description for an optional value
Zatímco jsme vždy máte možnost rozbalení každý z těchto dvou volitelných doplňků před interpolací nich, pojďme se podívat na to, jak bychom mohli dělat obě tyto věci najednou pomocí vlastní interpolace. Začneme tím, že rozšiřuje String.StringInterpolation
appendInterpolation
přetížení, který přijímá všechny volitelné hodnoty:
extension String.StringInterpolation { mutating func appendInterpolation<T>(unwrapping optional: T?) { let string = optional.map { "\($0)" } ?? "" appendLiteral(string) }}
nad unwrapping:
parametr label je důležité, protože to je to, co budeme používat, aby se říct, že kompilátor použít konkrétní metoda interpolace — jako tento:
func save(_ text: String, prefix: String?, suffix: String?) { let text = "\(unwrapping: prefix)\(text)\(unwrapping: suffix)" textStorage.store(text)}
i když je to jen syntaktický cukr, výše vypadá opravdu čistý! To však sotva poškrábe povrch toho, co mohou vlastní metody interpolace řetězců udělat. Mohou být jak obecné, tak ne generické, přijímat libovolný počet argumentů, používat výchozí hodnoty a téměř cokoli jiného, co mohou „normální“ metody udělat.
Zde je další příklad, ve kterém jsme umožňují naše metody pro převod URL na odkaz HTML před a také být použity v souvislosti řetězec interpolace:
extension String.StringInterpolation { mutating func appendInterpolation(linkTo url: URL, _ title: String) { let string = url.html(withTitle: title) appendLiteral(string) }}
S výše v místě, nyní můžeme snadno vytvářet odkazy HTML z adresy URL, jako je tento:
webView.loadHTMLString( "If you're not redirected, \(linkTo: url, "tap here").", baseURL: nil)
super věc o vlastní řetězec interpolace je to, jak kompilátor bere každý z našich appendInterpolation
metody a převádí je do odpovídající interpolace Api — to nám dává úplnou kontrolu nad tím, co stránka bude vypadat, například tím, že odstraní vnější parametr štítky, jako jsme to udělali pro title
výše.
budeme pokračovat v hledání dalších způsobů použití interpolace vlastních řetězců, například s přiřazenými řetězci a jinými druhy textových metadat, v nadcházejících článcích.
Podpora Swift tím, Sundell kontrolou tohoto sponzor:
Základní Vývojka: Připojit se zdarma online rychlokurz pro iOS vývojáře, kteří chtějí, aby se stal černý pás senior vývojáře — to je: dosažení expertní úrovně praktických dovedností a stát se součástí nejvyšší placených vývojáři ve světě.
Závěr
Zatímco některé Swift pokročilejší string literal schopnosti jsou jen opravdu užitečné ve velmi specifických situacích, jako jsou ty v tomto článku, je to hezké mít je k dispozici, když je potřeba — zejména proto, že je možné zcela se jim vyhnout a používat pouze řetězce "the old-fashioned way"
.
String literals je další oblast, ve které Swift protokolově orientovaný design skutečně svítí. Delegováním moc, jak literály jsou interpretovány a zpracovány pro implementátory protokolů, spíše než hard-kódování těchto chování v kompilátor sám, my jako vývojáři třetích stran jsou schopni silně přizpůsobit způsob literály jsou zpracovány — zatímco ještě drží výchozí nastavení tak jednoduché, jak jen mohou být.