Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A collection of Swift tips & tricks that I've shared on Twitter

License

NotificationsYou must be signed in to change notification settings

JohnSundell/SwiftTips

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 

Repository files navigation

⚠️This list is no longer being updated. For my latest Swift tips, checkout the"Tips" section on Swift by Sundell.

Swift tips & tricks ⚡️

One of the things I really love about Swift is how I keep finding interesting ways to use it in various situations, and when I do - I usually share them onTwitter. Here's a collection of all the tips & tricks that I've shared so far. Each entry has a link to the original tweet, if you want to respond with some feedback or question, which is always super welcome! 🚀

Also make sure to check out all of my other Swift content:

Table of contents

#102 Making async tests faster and more stable
#101 Adding support for Apple Pencil double-taps
#100 Combining values with functions
#99 Dependency injection using functions
#98 Using a custom exception handler
#97 Using type aliases to give semantic meaning to primitives
#96 Specializing protocols using constraints
#95 Unwrapping an optional or throwing an error
#94 Testing code that uses static APIs
#93 Matching multiple enum cases with associated values
#92 Multiline string literals
#91 Reducing sequences
#90 Avoiding manual Codable implementations
#89 Using feature flags instead of feature branches
#88 Lightweight data hierarchies using tuples
#87 The rule of threes
#86 Useful Codable extensions
#85 Using shared UserDefaults suites
#84 Custom UIView backing layers
#83 Auto-Equatable enums with associated values
#82 Defaults for associated types
#81 Creating a dedicated identifier type
#80 Assigning optional tuple members to variables
#79 Struct convenience initializers
#78 Usages of throwing functions
#77 Nested generic types
#76 Equatable & Hashable structures
#75 Conditional conformances
#74 Generic type aliases
#73 Parsing command line arguments using UserDefaults
#72 Using the & operator
#71 Capturing multiple values in mocks
#70 Reducing the need for mocks
#69 Using "then" as an external parameter label for closures
#68 Combining lazily evaluated sequences with the builder pattern
#67 Faster & more stable UI tests
#66 Accessing the clipboard from a Swift script
#65 Using tuples for view state
#64 Throwing tests and LocalizedError
#63 The difference between static and class properties
#62 Creating extensions with static factory methods
#61 Child view controller auto-resizing
#60 Using zip
#59 Defining custom option sets
#58 Using the where clause with associated types
#57 Using first class functions when iterating over a dictionary
#56 Calling instance methods as static functions
#55 Dropping suffixes from method names to support multiple arguments
#54 Constraining protocols to classes to ensure mutability
#53 String-based enums in string interpolation
#52 Expressively comparing a value with a list of candidates
#51 UIView bounds and transforms
#50 UIKit default arguments
#49 Avoiding Massive View Controllers
#48 Extending optionals
#47 Using where with for-loops
#46 Variable shadowing
#45 Using dot syntax for static properties and initializers
#44 Calling functions as closures with a tuple as parameters
#43 Enabling static dependency injection
#42 Type inference for lazy properties in Swift 4
#41 Converting Swift errors to NSError
#40 Making UIImage macOS compatible
#39 Internally mutable protocol-oriented APIs
#38 Switching on a set
#37 Adding the current locale to cache keys
#36 Setting up tests to avoid retain cycles with weak references
#35 Expressively matching a value against a list of candidates
#34 Organizing code using extensions
#33 Using map to transform an optional into a Result type
#32 Assigning to self in struct initializers
#31 Recursively calling closures as inline functions
#30 Passing self to required Objective-C dependencies
#29 Making weak or lazy properties readonly
#28 Defining static URLs using string literals
#27 Manipulating points, sizes and frames using math operators
#26 Using closure types in generic constraints
#25 Using associated enum values to avoid state-specific optionals
#24 Using enums for async result types
#23 Working on async code in a playground
#22 Overriding self with a weak reference
#21 Using DispatchWorkItem
#20 Combining a sequence of functions
#19 Chaining optionals with map() and flatMap()
#18 Using self-executing closures for lazy properties
#17 Speeding up Swift package tests
#16 Avoiding mocking UserDefaults
#15 Using variadic parameters
#14 Referring to enum cases with associated values as closures
#13 Using the === operator to compare objects by instance
#12 Calling initializers with dot syntax and passing them as closures
#11 Structuring UI tests as extensions on XCUIApplication
#10 Avoiding default cases in switch statements
#9 Using the guard statement in many different scopes
#8 Passing functions & operators as closures
#7 Using #function for UserDefaults key consistency
#6 Using a name already taken by the standard library
#5 Using Wrap to implement Equatable
#4 Using typealiases to reduce the length of method signatures
#3 Referencing either external or internal parameter name when writing docs
#2 Using auto closures
#1 Namespacing with nested types

🚀 Here are some quick tips to make async tests faster & more stable:

  • 😴 Avoid sleep() - use expectations instead
  • ⏱ Use generous timeouts to avoid flakiness on CI
  • 🧐 Put all assertions at the end of each test, not inside closures
// BEFORE:classMentionDetectorTests:XCTestCase{func testDetectingMention(){letdetector=MentionDetector()letstring="This test was written by @johnsundell."        detector.detectMentions(in: string){ mentionsinXCTAssertEqual(mentions,["johnsundell"])}sleep(2)}}// AFTER:classMentionDetectorTests:XCTestCase{func testDetectingMention(){letdetector=MentionDetector()letstring="This test was written by @johnsundell."varmentions:[String]?letexpectation=self.expectation(description: #function)        detector.detectMentions(in: string){            mentions= $0            expectation.fulfill()}waitForExpectations(timeout:10)XCTAssertEqual(mentions,["johnsundell"])}}

For more on async testing, check out"Unit testing asynchronous Swift code".

✍️ Adding support for the new Apple Pencil double-tap feature is super easy! All you have to do is to create aUIPencilInteraction, add it to a view, and implement one delegate method. Hopefully all pencil-compatible apps will soon adopt this.

letinteraction=UIPencilInteraction()interaction.delegate=selfview.addInteraction(interaction)extensionViewController:UIPencilInteractionDelegate{func pencilInteractionDidTap(_ interaction:UIPencilInteraction){        // Handle pencil double-tap}}

For more on using this and other iPad Pro features, check out"Building iPad Pro features in Swift".

😎 Here's a cool function that combines a value with a function to return a closure that captures that value, so that it can be called without any arguments. Super useful when working with closure-based APIs and we want to use some of our properties without having to captureself.

func combine<A, B>(_ value:A, with closure:@escaping(A)->B)->()->B{return{closure(value)}}// BEFORE:classProductViewController:UIViewController{overridefunc viewDidLoad(){        super.viewDidLoad()        buyButton.handler={[weak self]inguardlet self=selfelse{return}self.productManager.startCheckout(for:self.product)}}}// AFTER:classProductViewController:UIViewController{overridefunc viewDidLoad(){        super.viewDidLoad()        buyButton.handler=combine(product, with: productManager.startCheckout)}}

💉 When I'm only using a single function from a dependency, I love to inject that function as a closure, instead of having to create a protocol and inject the whole object. Makes dependency injection & testing super simple.

finalclassArticleLoader{typealiasNetworking=(Endpoint)->Future<Data>privateletnetworking:Networkinginit(networking:@escapingNetworking=URLSession.shared.load){self.networking= networking}func loadLatest()->Future<[Article]>{returnnetworking(.latestArticles).decode()}}

For more on this technique, check out"Simple Swift dependency injection with functions".

💥 It's cool that you can easily assign a closure as a customNSException handler. This is super useful when building things in Playgrounds - since you can't use breakpoints - so instead of justsignal SIGABRT, you'll get the full exception description if something goes wrong.

NSSetUncaughtExceptionHandler{ exceptioninprint(exception)}

❤️ I love that in Swift, we can use the type system to make our code so much more self-documenting - one way of doing so is to use type aliases to give the primitive types that we use a more semantic meaning.

extensionList.Item{    // Using type aliases, we can give semantic meaning to the    // primitive types that we use, without having to introduce    // wrapper types.typealiasIndex=Int}extensionList{enumMutation{        // Our enum cases now become a lot more self-documenting,        // without having to add additional parameter labels to        // explain them.case add(Item,Item.Index)case update(Item,Item.Index)case remove(Item.Index)}}

For more on self-documenting code, check out"Writing self-documenting Swift code".

🤯 A little late night prototyping session reveals that protocol constraints can not only be applied to extensions - they can also be added to protocol definitions!

This is awesome, since it lets us easily define specialized protocols based on more generic ones.

protocolComponent{associatedtypeContainerfunc add(to container:Container)}// Protocols that inherit from other protocols can include// constraints to further specialize them.protocolViewComponent:Componentwhere Container==UIView{associatedtypeView:UIViewvarview:View{get}}extensionViewComponent{func add(to container:UIView){        container.addSubview(view)}}

For more on specializing protocols, check out"Specializing protocols in Swift".

📦 Here's a super handy extension on Swift'sOptional type, which gives us a really nice API for easily unwrapping an optional, or throwing an error in case the value turned out to benil:

extensionOptional{func orThrow(_ errorExpression:@autoclosure()->Error)throws->Wrapped{switchself{case.some(let value):return valuecase.none:throwerrorExpression()}}}letfile=tryloadFile(at: path).orThrow(MissingFileError())

For more ways that optionals can be extended, check out"Extending optionals in Swift".

👩‍🔬 Testing code that uses static APIs can be really tricky, but there's a way that it can often be done - using Swift's first class function capabilities!

Instead of accessing that static API directly, we can inject the function we want to use, which enables us to mock it!

// BEFOREclassFriendsLoader{func loadFriends(then handler:@escaping(Result<[Friend]>)->Void){Networking.loadData(from:.friends){ resultin...}}}// AFTERclassFriendsLoader{typealiasHandler<T>=(Result<T>)->VoidtypealiasDataLoadingFunction=(Endpoint,@escapingHandler<Data>)->Voidfunc loadFriends(using dataLoading:DataLoadingFunction=Networking.loadData,                     then handler:@escapingHandler<[Friend]>){dataLoading(.friends){ resultin...}}}// MOCKING IN TESTSletdataLoading:FriendsLoader.DataLoadingFunction={ _, handlerinhandler(.success(mockData))}friendsLoader.loadFriends(using: dataLoading){ resultin...}

🐾 Swift's pattern matching capabilities are so powerful! Two enum cases with associated values can even be matched and handled by the same switch case - which is super useful when handling state changes with similar data.

enumDownloadState{case inProgress(progress:Double)case paused(progress:Double)case cancelledcase finished(Data)}func downloadStateDidChange(to state:DownloadState){switch state{case.inProgress(let progress),.paused(let progress):updateProgressView(with: progress)case.cancelled:showCancelledMessage()case.finished(let data):process(data)}}

🅰 One really nice benefit of Swift multiline string literals - even for single lines of text - is that they don't require quotes to be escaped. Perfect when working with things like HTML, or creating a custom description for an object.

lethtml= highlighter.highlight("Array<String>")XCTAssertEqual(html,"""<span class="type">Array</span>&lt;<span class="type">String</span>&gt;""")

💎 While it's very common in functional programming, thereduce function might be a bit of a hidden gem in Swift. It provides a super useful way to transform a sequence into a single value.

extensionSequencewhere Element:Equatable{func numberOfOccurrences(of target:Element)->Int{returnreduce(0){ result, elementinguard element== targetelse{return result}return result+1}}}

You can read more about transforming collections in"Transforming collections in Swift".

📦 When I use Codable in Swift, I want to avoid manual implementations as much as possible, even when there's a mismatch between my code structure and the JSON I'm decoding.

One way that can often be achieved is to use private data containers combined with computed properties.

structUser:Codable{letname:Stringletage:IntvarhomeTown:String{return originPlace.name}privateletoriginPlace:Place}privateextensionUser{structPlace:Codable{letname:String}}extensionUser{structContainer:Codable{letuser:User}}

🚢 Instead of using feature branches, I merge almost all of my code directly into master - and then I use feature flags to conditionally enable features when they're ready. That way I can avoid merge conflicts and keep shipping!

extensionListViewController{func addSearchIfNeeded(){        // Rather than having to keep maintaining a separate        // feature branch for a new feature, we can use a flag        // to conditionally turn it on.guardFeatureFlags.searchEnabledelse{return}letresultsVC=SearchResultsViewController()letsearchVC=UISearchController(            searchResultsController: resultsVC)        searchVC.searchResultsUpdater= resultsVC        navigationItem.searchController= searchVC}}

You can read more about feature flags in"Feature flags in Swift".

💾 Here I'm using tuples to create a lightweight hierarchy for my data, giving me a nice structure without having to introduce any additional types.

structCodeSegment{vartokens:(        previous:String?,        current:String)vardelimiters:(        previous:Character?        next: Character?)}handle(segment.tokens.current)

You can read more about tuples in"Using tuples as lightweight types in Swift"

3️⃣ Whenever I have 3 properties or local variables that share the same prefix, I usually try to extract them into their own method or type. That way I can avoid massive types & methods, and also increase readability, without falling into a "premature optimization" trap.

Before

publicfunc generate()throws{letcontentFolder=try folder.subfolder(named:"content")letarticleFolder=try contentFolder.subfolder(named:"posts")letarticleProcessor=ContentProcessor(folder: articleFolder)letarticles=try articleProcessor.process()...}

After

publicfunc generate()throws{letcontentFolder=try folder.subfolder(named:"content")letarticles=tryprocessArticles(in: contentFolder)...}privatefunc processArticles(in folder:Folder)throws->[ContentItem]{letfolder=try folder.subfolder(named:"posts")letprocessor=ContentProcessor(folder: folder)returntry processor.process()}

👨‍🔧 Here's two extensions that I always add to theEncodable &Decodable protocols, which for me really make the Codable API nicer to use. By using type inference for decoding, a lot of boilerplate can be removed when the compiler is already able to infer the resulting type.

extensionEncodable{func encoded()throws->Data{returntryJSONEncoder().encode(self)}}extensionData{func decoded<T:Decodable>()throws->T{returntryJSONDecoder().decode(T.self, from:self)}}letdata=try user.encoded()// By using a generic type in the decoded() method, the// compiler can often infer the type we want to decode// from the current context.tryuserDidLogin(data.decoded())// And if not, we can always supply the type, still making// the call site read very nicely.letotherUser=try data.decoded()asUser

📦UserDefaults is a lot more powerful than what it first might seem like. Not only can it store more complex values (like dates & dictionaries) and parse command line arguments - it also enables easy sharing of settings & lightweight data between apps in the same App Group.

letsharedDefaults=UserDefaults(suiteName:"my-app-group")!letuseDarkMode= sharedDefaults.bool(forKey:"dark-mode")// This value is put into the shared suite.sharedDefaults.set(true, forKey:"dark-mode")// If you want to treat the shared settings as read-only (and add// local overrides on top of them), you can simply add the shared// suite to the standard UserDefaults.letcombinedDefaults=UserDefaults.standardcombinedDefaults.addSuite(named:"my-app-group")// This value is a local override, not added to the shared suite.combinedDefaults.set(true, forKey:"app-specific-override")

🎨 By overridinglayerClass you can tell UIKit whatCALayer class to use for aUIView's backing layer. That way you can reduce the amount of layers, and don't have to do any manual layout.

finalclassGradientView:UIView{overrideclassvarlayerClass:AnyClass{returnCAGradientLayer.self}varcolors:(start:UIColor, end:UIColor)?{        didSet{updateLayer()}}privatefunc updateLayer(){letlayer=self.layeras!CAGradientLayer        layer.colors= colors.map{[$0.start.cgColor, $0.end.cgColor]}}}

✅ That the compiler now automatically synthesizes Equatable conformances is such a huge upgrade for Swift! And the cool thing is that it works for all kinds of types - even for enums with associated values! Especially useful when using enums for verification in unit tests.

structArticle:Equatable{lettitle:Stringlettext:String}structUser:Equatable{letname:Stringletage:Int}extensionNavigator{enumDestination:Equatable{case profile(User)case article(Article)}}func testNavigatingToArticle(){letarticle=Article(title:"Title", text:"Text")    controller.select(article)XCTAssertEqual(navigator.destinations,[.article(article)])}

🤝 Associated types can have defaults in Swift - which is super useful for types that are not easily inferred (for example when they're not used for a specific instance method or property).

protocolIdentifiable{associatedtypeRawIdentifier:Codable=Stringvarid:Identifier<Self>{get}}structUser:Identifiable{letid:Identifier<User>letname:String}structGroup:Identifiable{typealiasRawIdentifier=Intletid:Identifier<Group>letname:String}

🆔 If you want to avoid using plain strings as identifiers (which can increase both type safety & readability), it's really easy to create a custom Identifier type that feels just like a native Swift type, thanks to protocols!

More on this topic in"Type-safe identifiers in Swift".

structIdentifier:Hashable{letstring:String}extensionIdentifier:ExpressibleByStringLiteral{init(stringLiteral value:String){        string= value}}extensionIdentifier:CustomStringConvertible{vardescription:String{return string}}extensionIdentifier:Codable{init(from decoder:Decoder)throws{letcontainer=try decoder.singleValueContainer()        string=try container.decode(String.self)}func encode(to encoder:Encoder)throws{varcontainer= encoder.singleValueContainer()try container.encode(string)}}structArticle:Codable{letid:Identifierlettitle:String}letarticle=Article(id:"my-article", title:"Hello world!")

🙌 A really cool thing about using tuples to model the internal state of a Swift type, is that you can unwrap an optional tuple's members directly into local variables.

Very useful in order to group multiple optional values together for easy unwrapping & handling.

classImageTransformer{privatevarqueue=[(image: UIImage, transform: Transform)]()privatefunc processNext(){        // When unwrapping an optional tuple, you can assign the members        // directly to local variables.guardlet(image, transform)= queue.firstelse{return}letcontext=Context()        context.draw(image)        context.apply(transform)...}}

❤️ I love to structure my code using extensions in Swift. One big benefit of doing so when it comes to struct initializers, is that defining a convenience initializer doesn't remove the default one the compiler generates - best of both worlds!

structArticle{letdate:Datevartitle:Stringvartext:Stringvarcomments:[Comment]}extensionArticle{init(title:String, text:String){self.init(date:Date(), title: title, text: text, comments:[])}}letarticleA=Article(title:"Best Cupcake Recipe", text:"...")letarticleB=Article(    date:Date(),    title:"Best Cupcake Recipe",    text:"...",    comments:[Comment(user: currentUser, text:"Yep, can confirm!")])

🏈 A big benefit of using throwing functions for synchronous Swift APIs is that the caller can decide whether they want to treat the return value as optional (try?) or required (try).

func loadFile(named name:String)throws->File{guardlet url=urlForFile(named: name)else{throwFile.Error.missing}do{letdata=tryData(contentsOf: url)returnFile(url: url, data: data)}catch{throwFile.Error.invalidData(error)}}letrequiredFile=tryloadFile(named:"AppConfig.json")letoptionalFile=try?loadFile(named:"UserSettings.json")

🐝 Types that are nested in generics automatically inherit their parent's generic types - which is super useful when defining accessory types (for things like states or outcomes).

structTask<Input, Output>{typealiasClosure=(Input)throws->Outputletclosure:Closure}extensionTask{enumResult{case success(Output)case failure(Error)}}

🤖 Now that the Swift compiler automatically synthesizes Equatable & Hashable conformances for value types, it's easier than ever to setup model structures with nested types that are allEquatable/Hashable!

typealiasValue=Hashable&CodablestructUser:Value{varname:Stringvarage:IntvarlastLoginDate:Date?varsettings:Settings}extensionUser{structSettings:Value{varitemsPerPage:Intvartheme:Theme}}extensionUser.Settings{enumTheme:String,Value{case lightcase dark}}

You can read more about using nested types in Swift here.

🎉 Swift 4.1 is here! One of the key features it brings is conditional conformances, which lets you have a type only conform to a protocol under certain constraints.

protocolUnboxTransformable{associatedtypeRawValuestaticfunc transform(_ value:RawValue)throws->Self?}extensionArray:UnboxTransformablewhere Element:UnboxTransformable{typealiasRawValue=[Element.RawValue]staticfunc transform(_ value:RawValue)throws->[Element]?{returntry value.compactMap(Element.transform)}}

I also havean article with lots of more info on conditional conformances here. Paul Hudson also has a great overview ofall Swift 4.1 features here.

🕵️‍♀️ A cool thing about Swift type aliases is that they can be generic! Combine that with tuples and you can easily define simple generic types.

typealiasPair<T>=(T,T)extensionGame{func calculateScore(for players:Pair<Player>)->Int{...}}

You can read more about using tuples as lightweight typeshere.

☑️ A really cool "hidden" feature of UserDefaults is that it contains any arguments that were passed to the app at launch!

Super useful both in Swift command line tools & scripts, but also to temporarily override a value when debugging iOS apps.

letdefaults=UserDefaults.standardletquery= defaults.string(forKey:"query")letresultCount= defaults.integer(forKey:"results")

👏 Swift's& operator is awesome! Not only can you use it to compose protocols, you can compose other types too! Very useful if you want to hide concrete types & implementation details.

protocolLoadableFromURL{func load(from url:URL)}classContentViewController:UIViewController,LoadableFromURL{func load(from url:URL){...}}classViewControllerFactory{func makeContentViewController()->UIViewController&LoadableFromURL{returnContentViewController()}}

🤗 When capturing values in mocks, using an array (instead of just a single value) makes it easy to verify that only a certain number of values were passed.

Perfect for protecting against "over-calling" something.

classUserManagerTests:XCTestCase{func testObserversCalledWhenUserFirstLogsIn(){letmanager=UserManager()letobserver=ObserverMock()        manager.addObserver(observer)        // First login, observers should be notifiedletuser=User(id:123, name:"John")        manager.userDidLogin(user)XCTAssertEqual(observer.users,[user])        // If the same user logs in again, observers shouldn't be notified        manager.userDidLogin(user)XCTAssertEqual(observer.users,[user])}}privateextensionUserManagerTests{classObserverMock:UserManagerObserver{private(set)varusers=[User]()func userDidChange(to user:User){            users.append(user)}}}

👋 When writing tests, you don't always need to create mocks - you can create stubs using real instances of things like errors, URLs & UserDefaults.

Here's how to do that for some common tasks/object types in Swift:

// Create errors using NSError (#function can be used to reference the name of the test)leterror=NSError(domain: #function, code:1, userInfo:nil)// Create non-optional URLs using file pathsleturl=URL(fileURLWithPath:"Some/URL")// Reference the test bundle using Bundle(for:)letbundle=Bundle(for:type(of:self))// Create an explicit UserDefaults object (instead of having to use a mock)letuserDefaults=UserDefaults(suiteName: #function)// Create queues to control/await concurrent operationsletqueue=DispatchQueue(label: #function)

For when you actually do need mocking, check out"Mocking in Swift".

⏱ I've started using "then" as an external parameter label for completion handlers. Makes the call site read really nicely (Because I do ❤️ conversational API design) regardless of whether trailing closure syntax is used or not.

protocolDataLoader{    // Adding type aliases to protocols can be a great way to    // reduce verbosity for parameter types.typealiasHandler=(Result<Data>)->VoidassociatedtypeEndpointfunc loadData(from endpoint:Endpoint, then handler:@escapingHandler)}loader.loadData(from:.messages){ resultin...}loader.loadData(from:.messages, then:{ resultin...})

😴 Combining lazily evaluated sequences with builder pattern-like properties can lead to some pretty sweet APIs for configurable sequences in Swift.

Also useful for queries & other things you "build up" and then execute.

// Extension adding builder pattern-like properties that return// a new sequence value with the given configuration appliedextensionFileSequence{varrecursive:FileSequence{varsequence=self        sequence.isRecursive=truereturn sequence}varincludingHidden:FileSequence{varsequence=self        sequence.includeHidden=truereturn sequence}}// BEFOREletfiles= folder.makeFileSequence(recursive:true, includeHidden:true)// AFTERletfiles= folder.files.recursive.includingHidden

Want an intro to lazy sequences? Check out"Swift sequences: The art of being lazy".

My top 3 tips for faster & more stable UI tests:

📱 Reset the app's state at the beginning of every test.

🆔 Use accessibility identifiers instead of UI strings.

⏱ Use expectations instead of waiting time.

func testOpeningArticle(){    // Launch the app with an argument that tells it to reset its stateletapp=XCUIApplication()    app.launchArguments.append("--uitesting")    app.launch()        // Check that the app is displaying an activity indicatorletactivityIndicator= app.activityIndicator.elementXCTAssertTrue(activityIndicator.exists)        // Wait for the loading indicator to disappear = content is readyexpectation(for:NSPredicate(format:"exists == 0"),                evaluatedWith: activityIndicator)                    // Use a generous timeout in case the network is slowwaitForExpectations(timeout:10)        // Tap the cell for the first article    app.tables.cells["Article.0"].tap()        // Assert that a label with the accessibility identifier "Article.Title" existsletlabel= app.staticTexts["Article.Title"]XCTAssertTrue(label.exists)}

📋 It's super easy to access the contents of the clipboard from a Swift script. A big benefit of Swift scripting is being able to use Cocoa's powerful APIs for Mac apps.

import Cocoaletclipboard=NSPasteboard.general.string(forType:.string)

🎯 Using Swift tuples for view state can be a super nice way to group multiple properties together and render them reactively using the layout system.

By using a tuple we don't have to either introduce a new type or make our view model-aware.

classTextView:UIView{varstate:(title:String?, text:String?){        // By telling UIKit that our view needs layout and binding our        // state in layoutSubviews, we can react to state changes without        // doing unnecessary layout work.        didSet{setNeedsLayout()}}privatelettitleLabel=UILabel()privatelettextLabel=UILabel()overridefunc layoutSubviews(){        super.layoutSubviews()        titleLabel.text= state.title        textLabel.text= state.text...}}

⚾️ Swift tests can throw, which is super useful in order to avoid complicated logic or force unwrapping. By making errors conform toLocalizedError, you can also get a nice error message in Xcode if there's a failure.

classImageCacheTests:XCTestCase{func testCachingAndLoadingImage()throws{letbundle=Bundle(for:type(of:self))letcache=ImageCache(bundle: bundle)                // Bonus tip: You can easily load images from your test        // bundle using this UIImage initializerletimage=tryrequire(UIImage(named:"sample", in: bundle, compatibleWith:nil))try cache.cache(image, forKey:"key")letcachedImage=try cache.image(forKey:"key")XCTAssertEqual(image, cachedImage)}}enumImageCacheError{case emptyKeycase dataConversionFailed}// When using throwing tests, making your errors conform to// LocalizedError will render a much nicer error message in// Xcode (per default only the error code is shown).extensionImageCacheError:LocalizedError{varerrorDescription:String?{switchself{case.emptyKey:return"An empty key was given"case.dataConversionFailed:return"Failed to convert the given image to Data"}}}

For more information, and the implementation of therequire method used above, check out"Avoiding force unwrapping in Swift unit tests".

✍️ Unlikestatic properties,class properties can be overridden by subclasses (however, they can't be stored, only computed).

classTableViewCell:UITableViewCell{classvarpreferredHeight:CGFloat{return60}}classTallTableViewCell:TableViewCell{overrideclassvarpreferredHeight:CGFloat{return100}}

👨‍🎨 Creating extensions with static factory methods can be a great alternative to subclassing in Swift, especially for things like setting up UIViews, CALayers or other kinds of styling.

It also lets you remove a lot of styling & setup from your view controllers.

extensionUILabel{staticfunc makeForTitle()->UILabel{letlabel=UILabel()        label.font=.boldSystemFont(ofSize:24)        label.textColor=.darkGray        label.adjustsFontSizeToFitWidth=true        label.minimumScaleFactor=0.75return label}staticfunc makeForText()->UILabel{letlabel=UILabel()        label.font=.systemFont(ofSize:16)        label.textColor=.black        label.numberOfLines=0return label}}classArticleViewController:UIViewController{    lazyvartitleLabel=UILabel.makeForTitle()    lazyvartextLabel=UILabel.makeForText()}

🧒 An awesome thing about child view controllers is that they're automatically resized to match their parent, making them a super nice solution for things like loading & error views.

classListViewController:UIViewController{func loadItems(){letloadingViewController=LoadingViewController()add(loadingViewController)        dataLoader.loadItems{[weak self] resultin            loadingViewController.remove()self?.handle(result)}}}

For more about child view controller (including theadd andremove methods used above), check out"Using child view controllers as plugins in Swift".

🤐 Using the zip function in Swift you can easily combine two sequences. Super useful when using two sequences to do some work, since zip takes care of all the bounds-checking.

func render(titles:[String]){for(label, text)inzip(titleLabels, titles){print(text)        label.text= text}}

🎛 The awesome thing about option sets in Swift is that they can automatically either be passed as a single member or as a set. Even cooler is that you can easily define your own option sets as well, perfect for options and other non-exclusive values.

// Option sets are awesome, because you can easily pass them// both using dot syntax and array literal syntax, like when// using the UIView animation API:UIView.animate(withDuration:0.3,               delay:0,               options:.allowUserInteraction,               animations: animations)UIView.animate(withDuration:0.3,               delay:0,               options:[.allowUserInteraction,.layoutSubviews],               animations: animations)// The cool thing is that you can easily define your own option// sets as well, by defining a struct that has an Int rawValue,// that will be used as a bit mask.extensionCache{structOptions:OptionSet{staticletsaveToDisk=Options(rawValue:1)staticletclearOnMemoryWarning=Options(rawValue:1 <<1)staticletclearDaily=Options(rawValue:1 <<2)letrawValue:Int}}// We can now use Cache.Options just like UIViewAnimationOptions:Cache(options:.saveToDisk)Cache(options:[.saveToDisk,.clearDaily])

🙌 Using thewhere clause when designing protocol-oriented APIs in Swift can let your implementations (or others' if it's open source) have a lot more freedom, especially when it comes to collections.

See"Using generic type constraints in Swift 4" for more info.

publicprotocolPathFinderMap{associatedtypeNode    // Using the 'where' clause for associated types, we can    // ensure that a type meets certain requirements (in this    // case that it's a sequence with Node elements).associatedtypeNodeSequence:Sequencewhere NodeSequence.Element==Node    // Instead of using a concrete type (like [Node]) here, we    // give implementors of this protocol more freedom while    // still meeting our requirements. For example, one    // implementation might use Set<Node>.func neighbors(of node:Node)->NodeSequence}

👨‍🍳 Combine first class functions in Swift with the fact that Dictionary elements are (Key, Value) tuples and you can build yourself some pretty awesome functional chains when iterating over a Dictionary.

func makeActor(at coordinate:Coordinate, for building:Building)->Actor{letactor=Actor()actor.position= coordinate.pointactor.animation= building.animationreturnactor}func render(_ buildings:[Coordinate:Building]){    buildings.map(makeActor).forEach(add)}

😎 In Swift, you can call any instance method as a static function and it will return a closure representing that method. This is how running tests using SPM on Linux works.

More about this topic in my blog post"First class functions in Swift".

// This produces a '() -> Void' closure which is a reference to the// given view's 'removeFromSuperview' method.letclosure=UIView.removeFromSuperview(view)// We can now call it just like we would any other closure, and it// will run 'view.removeFromSuperview()'closure()// This is how running tests using the Swift Package Manager on Linux// works, you return your test functions as closures:extensionUserManagerTests{staticvarallTests=[("testLoggingIn", testLoggingIn),("testLoggingOut", testLoggingOut),("testUserPermissions", testUserPermissions)]}

👏 One really nice benefit of dropping suffixes from method names (and just using verbs, when possible) is that it becomes super easy to support both single and multiple arguments, and it works really well semantically.

extensionUIView{func add(_ subviews:UIView...){        subviews.forEach(addSubview)}}view.add(button)view.add(label)// By dropping the "Subview" suffix from the method name, both// single and multiple arguments work really well semantically.view.add(button, label)

👽 Using theAnyObject (orclass) constraint on protocols is not only useful when defining delegates (or other weak references), but also when you always want instances to be mutable without copying.

// By constraining a protocol with 'AnyObject' it can only be adopted// by classes, which means all instances will always be mutable, and// that it's the original instance (not a copy) that will be mutated.protocolDataContainer:AnyObject{vardata:Data?{getset}}classUserSettingsManager{privatevarsettings:SettingsprivateletdataContainer:DataContainer    // Since DataContainer is a protocol, we an easily mock it in    // tests if we use dependency injectioninit(settings:Settings, dataContainer:DataContainer){self.settings= settingsself.dataContainer= dataContainer}func saveSettings()throws{letdata=try settings.serialize()        // We can now assign properties on an instance of our protocol        // because the compiler knows it's always going to be a class        dataContainer.data= data}}

🍣 Even if you define a custom raw value for a string-based enum in Swift, the full case name will be used in string interpolation.

Super useful when using separate raw values for JSON, while still wanting to use the full case name in other contexts.

extensionBuilding{    // This enum has custom raw values that are used when decoding    // a value, for example from JSON.enumKind:String{case castle="C"case town="T"case barracks="B"case goldMine="G"case camp="CA"case blacksmith="BL"}varanimation:Animation{returnAnimation(            // When used in string interpolation, the full case name is still used.            // For 'castle' this will be 'buildings/castle'.            name:"buildings/\(kind)",            frameCount: frameCount,            frameDuration: frameDuration)}}

👨‍🔬 Continuing to experiment with expressive ways of comparing a value with a list of candidates in Swift. Adding an extension on Equatable is probably my favorite approach so far.

extensionEquatable{func isAny(of candidates:Self...)->Bool{return candidates.contains(self)}}letisHorizontal= direction.isAny(of:.left,.right)

Seetip #35 for my previous experiment.

📐 A really interesting side-effect of aUIView'sbounds being its rect within its own coordinate system is that transforms don't affect it at all. That's why it's usually a better fit thanframe when doing layout calculations of subviews.

letview=UIView()view.frame.size=CGSize(width:100, height:100)view.transform=CGAffineTransform(scaleX:2, y:2)print(view.frame) // (-50.0, -50.0, 200.0, 200.0)print(view.bounds) // (0.0, 0.0, 100.0, 100.0)

👏 It's awesome that many UIKit APIs with completion handlers and other optional parameters import into Swift with default arguments (even though they are written in Objective-C). Getting rid of all those nil arguments is so nice!

// BEFORE: All parameters are specified, just like in Objective-CviewController.present(modalViewController, animated:true, completion:nil)modalViewController.dismiss(animated:true, completion:nil)viewController.transition(from: loadingViewController,                          to: contentViewController,                          duration:0.3,                          options:[],                          animations: animations,                          completion:nil)// AFTER: Since many UIKit APIs with completion handlers and other// optional parameters import into Swift with default arguments,// we can make our calls shorterviewController.present(modalViewController, animated:true)modalViewController.dismiss(animated:true)viewController.transition(from: loadingViewController,                          to: contentViewController,                          duration:0.3,                          animations: animations)

✂️ Avoiding Massive View Controllers is all about finding the right levels of abstraction and splitting things up.

My personal rule of thumb is that as soon as I have 3 methods or properties that have the same prefix, I break them out into their own type.

// BEFOREclassLoginViewController:UIViewController{private lazyvarsignUpLabel=UILabel()private lazyvarsignUpImageView=UIImageView()private lazyvarsignUpButton=UIButton()}// AFTERclassLoginViewController:UIViewController{private lazyvarsignUpView=SignUpView()}classSignUpView:UIView{private lazyvarlabel=UILabel()private lazyvarimageView=UIImageView()private lazyvarbutton=UIButton()}

❤️ I love the fact that optionals are enums in Swift - it makes it so easy to extend them with convenience APIs for certain types. Especially useful when doing things like data validation on optional values.

func validateTextFields()->Bool{guard !usernameTextField.text.isNilOrEmptyelse{returnfalse}...returntrue}// Since all optionals are actual enum values in Swift, we can easily// extend them for certain types, to add our own convenience APIsextensionOptionalwhere Wrapped==String{varisNilOrEmpty:Bool{switchself{caselet string?:return string.isEmptycasenil:returntrue}}}// Since strings are now Collections in Swift 4, you can even// add this property to all optional collections:extensionOptionalwhere Wrapped:Collection{varisNilOrEmpty:Bool{switchself{caselet collection?:return collection.isEmptycasenil:returntrue}}}

🗺 Using thewhere keyword can be a super nice way to quickly apply a filter in afor-loop in Swift. You can of course usemap,filter andforEach, orguard, but for simple loops I think this is very expressive and nice.

func archiveMarkedPosts(){forpostin postswhere post.isMarked{archive(post)}}func healAllies(){forplayerin playerswhere player.isAllied(to: currentPlayer){        player.heal()}}

👻 Variable shadowing can be super useful in Swift, especially when you want to create a local copy of a parameter value in order to use it as state within a closure.

init(repeatMode:RepeatMode, closure:@escaping()->UpdateOutcome){    // Shadow the argument with a local, mutable copyvarrepeatMode= repeatModeself.closure={        // With shadowing, there's no risk of accidentially        // referring to the immutable versionswitch repeatMode{case.forever:breakcase.times(let count):guard count>0else{return.finished}                        // We can now capture the mutable version and use            // it for state in a closure            repeatMode=.times(count-1)}returnclosure()}}

✒️ Dot syntax is one of my favorite features of Swift. What's really cool is that it's not only for enums, any static method or property can be used with dot syntax - even initializers! Perfect for convenience APIs and default parameters.

publicenumRepeatMode{case times(Int)case forever}publicextensionRepeatMode{staticvarnever:RepeatMode{return.times(0)}staticvaronce:RepeatMode{return.times(1)}}view.perform(animation, repeated:.once)// To make default parameters more compact, you can even use init with dot syntaxclassImageLoader{init(cache:Cache=.init(), decoder:ImageDecoder=.init()){...}}

🚀 One really cool aspect of Swift having first class functions is that you can pass any function (or even initializer) as a closure, and even call it with a tuple containing its parameters!

// This function lets us treat any "normal" function or method as// a closure and run it with a tuple that contains its parametersfunc call<Input, Output>(_ function:(Input)->Output, with input:Input)->Output{returnfunction(input)}classViewFactory{func makeHeaderView()->HeaderView{        // We can now pass an initializer as a closure, and a tuple        // containing its parametersreturncall(HeaderView.init, with:loadTextStyles())}privatefunc loadTextStyles()->(font:UIFont, color:UIColor){return(theme.font, theme.textColor)}}classHeaderView{init(font:UIFont, textColor:UIColor){...}}

💉 If you've been struggling to test code that uses static APIs, here's a technique you can use to enable static dependency injection without having to modify any call sites:

// Before: Almost impossible to test due to the use of singletonsclassAnalytics{staticfunc log(_ event:Event){Database.shared.save(event)letdictionary= event.serialize()NetworkManager.shared.post(dictionary, to: eventURL)}}// After: Much easier to test, since we can inject mocks as argumentsclassAnalytics{staticfunc log(_ event:Event,                    database:Database=.shared,                    networkManager:NetworkManager=.shared){        database.save(event)letdictionary= event.serialize()        networkManager.post(dictionary, to: eventURL)}}

🎉 In Swift 4, type inference works for lazy properties and you don't need to explicitly refer toself!

// Swift 3classPurchaseView:UIView{private lazyvarbuyButton:UIButton=self.makeBuyButton()privatefunc makeBuyButton()->UIButton{letbutton=UIButton()        button.setTitle("Buy", for:.normal)        button.setTitleColor(.blue, for:.normal)return button}}// Swift 4classPurchaseView:UIView{private lazyvarbuyButton=makeBuyButton()privatefunc makeBuyButton()->UIButton{letbutton=UIButton()        button.setTitle("Buy", for:.normal)        button.setTitleColor(.blue, for:.normal)return button}}

😎 You can turn any SwiftError into anNSError, which is super useful when pattern matching with a code 👍. Also, switching on optionals is pretty cool!

lettask= urlSession.dataTask(with: url){ data, _, errorinswitch error{case.some(leterror asNSError)where error.code== NSURLErrorNotConnectedToInternet:        presenter.showOfflineView()case.some(let error):        presenter.showGenericErrorView()case.none:        presenter.renderContent(from: data)}}task.resume()

Also make sure to check outKostas Kremizas' tip about how you can pattern match directly against a member ofURLError.

🖥 Here's an easy way to make iOS model code that usesUIImage macOS compatible - like me andGui Rambo discussed on theSwift by Sundell Podcast.

// Either put this in a separate file that you only include in your macOS target or wrap the code in #if os(macOS) / #endifimport Cocoa// Step 1: Typealias UIImage to NSImagetypealiasUIImage=NSImage// Step 2: You might want to add these APIs that UIImage has but NSImage doesn't.extensionNSImage{varcgImage:CGImage?{varproposedRect=CGRect(origin:.zero, size: size)returncgImage(forProposedRect:&proposedRect,                       context:nil,                       hints:nil)}convenienceinit?(named name:String){self.init(named:Name(name))}}// Step 3: Profit - you can now make your model code that uses UIImage cross-platform!structUser{letname:StringletprofileImage:UIImage}

🤖 You can easily define a protocol-oriented API that can only be mutated internally, by using an internal protocol that extends a public one.

// Declare a public protocol that acts as your immutable APIpublicprotocolModelHolder{associatedtypeModelvarmodel:Model{get}}// Declare an extended, internal protocol that provides a mutable APIinternalprotocolMutableModelHolder:ModelHolder{varmodel:Model{getset}}// You can now implement the requirements using 'public internal(set)'publicclassUserHolder:MutableModelHolder{publicinternal(set)varmodel:Userinternalinit(model:User){self.model= model}}

🎛 You can switch on a set using array literals as cases in Swift! Can be really useful to avoid manyif/else if statements.

classRoadTile:Tile{varconnectedDirections=Set<Direction>()func render(){switch connectedDirections{case[.up,.down]:            image=UIImage(named:"road-vertical")case[.left,.right]:            image=UIImage(named:"road-horizontal")default:            image=UIImage(named:"road")}}}

🌍 When caching localized content in an app, it's a good idea to add the current locale to all keys, to prevent bugs when switching languages.

func cache(_ content:Content, forKey key:String)throws{letdata=trywrap(content)asDataletkey=localize(key: key)try storage.store(data, forKey: key)}func loadCachedContent(forKey key:String)->Content?{letkey=localize(key: key)letdata= storage.loadData(forKey: key)return data.flatMap{try?unbox(data: $0)}}privatefunc localize(key:String)->String{return key+"-"+ Bundle.main.preferredLocalizations[0]}

🚳 Here's an easy way to setup a test to avoid accidental retain cycles with object relationships (like weak delegates & observers) in Swift:

func testDelegateNotRetained(){    // Assign the delegate (weak) and also retain it using a local varvardelegate:Delegate?=DelegateMock()    controller.delegate= delegateXCTAssertNotNil(controller.delegate)        // Release the local var, which should also release the weak reference    delegate=nilXCTAssertNil(controller.delegate)}

👨‍🔬 Playing around with an expressive way to check if a value matches any of a list of candidates in Swift:

// Instead of multiple conditions like this:if string=="One" || string=="Two" || string=="Three"{}// You can now do:if string==any(of:"One","Two","Three"){}

You can find agist with the implementation here.

👪 APIs in a Swift extension automatically inherit its access control level, making it a neat way to organize public, internal & private APIs.

publicextensionAnimation{init(textureNamed textureName:String){        frames=[Texture(name: textureName)]}init(texturesNamed textureNames:[String], frameDuration:TimeInterval=1){        frames= textureNames.map(Texture.init)self.frameDuration= frameDuration}init(image:Image){        frames=[Texture(image: image)]}}internalextensionAnimation{func loadFrameImages()->[Image]{return frames.map{ $0.loadImageIfNeeded()}}}

🗺 Usingmap you can transform an optional value into an optionalResult type by simply passing in the enum case.

enumResult<Value>{case value(Value)case error(Error)}classPromise<Value>{privatevarresult:Result<Value>?init(value:Value?=nil){        result= value.map(Result.value)}}

👌 It's so nice that you can assign directly toself instruct initializers in Swift. Very useful when adding conformance to protocols.

extensionBool:AnswerConvertible{publicinit(input:String)throws{switch input.lowercased(){        case"y","yes","👍":self=truedefault:self=false      }}}

☎️ Defining Swift closures as inline functions enables you to recursively call them, which is super useful in things like custom sequences.

classDatabase{func records(matching query:Query)->AnySequence<Record>{varrecordIterator=loadRecords().makeIterator()func iterate()->Record?{guardlet nextRecord= recordIterator.next()else{returnnil}guard nextRecord.matches(query)else{                // Since the closure is an inline function, it can be recursively called,                // in this case in order to advance to the next item.returniterate()}return nextRecord}                // AnySequence/AnyIterator are part of the standard library and provide an easy way        // to define custom sequences using closures.returnAnySequence{AnyIterator(iterate)}}}

Rob Napier points out that using the above might cause crashes if used on a large databaset, since Swift has no guaranteedTail Call Optimization (TCO).

Slava Pestov also points out that another benefit of inline functions vs closures is that they can have their own generic parameter list.

🏖 Using lazy properties in Swift, you can passself to required Objective-C dependencies without having to use force-unwrapped optionals.

classDataLoader:NSObject{    lazyvarurlSession:URLSession=self.makeURLSession()privatefunc makeURLSession()->URLSession{returnURLSession(configuration:.default, delegate:self, delegateQueue:.main)}}classRenderer{    lazyvardisplayLink:CADisplayLink=self.makeDisplayLink()privatefunc makeDisplayLink()->CADisplayLink{returnCADisplayLink(target:self, selector: #selector(screenDidRefresh))}}

👓 If you have a property in Swift that needs to beweak orlazy, you can still make it readonly by usingprivate(set).

classNode{private(set) weakvarparent:Node?private(set) lazyvarchildren=[Node]()func add(child:Node){        children.append(child)        child.parent=self}}

🌏 Tired of usingURL(string: "url")! for static URLs? MakeURL conform toExpressibleByStringLiteral and you can now simply use"url" instead.

extensionURL:ExpressibleByStringLiteral{    // By using 'StaticString' we disable string interpolation, for safetypublicinit(stringLiteral value:StaticString){self=URL(string:"\(value)").require(hint:"Invalid URL string literal:\(value)")}}// We can now define URLs using static string literals 🎉leturl:URL="https://www.swiftbysundell.com"lettask=URLSession.shared.dataTask(with:"https://www.swiftbysundell.com")// In Swift 3 or earlier, you also have to implement 2 additional initializersextensionURL{publicinit(extendedGraphemeClusterLiteral value:StaticString){self.init(stringLiteral: value)}publicinit(unicodeScalarLiteral value:StaticString){self.init(stringLiteral: value)}}

To find the extension that adds therequire() method onOptional that I use above, check outRequire.

✚ I'm always careful with operator overloading, but for manipulating things like sizes, points & frames I find them super useful.

extensionCGSize{staticfunc*(lhs:CGSize, rhs:CGFloat)->CGSize{returnCGSize(width: lhs.width* rhs, height: lhs.height* rhs)}}button.frame.size= image.size*2

If you like the above idea, check outCGOperators, which contains math operator overloads for all Core Graphics' vector types.

🔗 You can use closure types in generic constraints in Swift. Enables nice APIs for handling sequences of closures.

extensionSequencewhere Element==()->Void{func callAll(){forEach{$0()}}}extensionSequencewhere Element==()->String{func joinedResults(separator:String)->String{returnmap{$0()}.joined(separator: separator)}}callbacks.callAll()letnames= nameProviders.joinedResults(separator:",")

(If you're using Swift 3, you have to changeElement toIterator.Element)

🎉 Using associated enum values is a super nice way to encapsulate mutually exclusive state info (and avoiding state-specific optionals).

// BEFORE: Lots of state-specific, optional propertiesclassPlayer{varisWaitingForMatchMaking:BoolvarinvitingUser:User?varnumberOfLives:IntvarplayerDefeatedBy:Player?varroundDefeatedIn:Int?}// AFTER: All state-specific information is encapsulated in enum casesclassPlayer{enumState{case waitingForMatchMakingcase waitingForInviteResponse(from:User)case active(numberOfLives:Int)case defeated(by:Player, roundNumber:Int)}varstate:State}

👍 I really like using enums for all async result types, even boolean ones. Self-documenting, and makes the call site a lot nicer to read too!

protocolPushNotificationService{    // Beforefunc enablePushNotifications(completionHandler:@escaping(Bool)->Void)        // Afterfunc enablePushNotifications(completionHandler:@escaping(PushNotificationStatus)->Void)}enumPushNotificationStatus{case enabledcase disabled}service.enablePushNotifications{ statusinif status==.enabled{        enableNotificationsButton.removeFromSuperview()}}

🏃 Want to work on your async code in a Swift Playground? Just setneedsIndefiniteExecution to true to keep it running:

import PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution=trueDispatchQueue.main.asyncAfter(deadline:.now()+3){letgreeting="Hello after 3 seconds"print(greeting)}

To stop the playground from executing, simply callPlaygroundPage.current.finishExecution().

💦 Avoid memory leaks when accidentially refering toself in closures by overriding it locally with a weak reference:

Swift >= 4.2

dataLoader.loadData(from: url){[weak self] result in    guardlet self=selfelse{return}self.cache(result)...

Swift < 4.2

dataLoader.loadData(from: url){[weak self] result in    guardlet `self`=selfelse{return}self.cache(result)...

Note that the reason the above currently works isbecause of a compiler bug (which I hope gets turned into a properly supported feature soon).

🕓 Using dispatch work items you can easily cancel a delayed asynchronous GCD task if you no longer need it:

letworkItem=DispatchWorkItem{    // Your async code goes in here}// Execute the work item after 1 secondDispatchQueue.main.asyncAfter(deadline:.now()+1, execute: workItem)// You can cancel the work item if you no longer need itworkItem.cancel()

➕ While working on a new Swift developer tool (to be open sourced soon 😉), I came up with a pretty neat way of organizing its sequence of operations, by combining their functions into a closure:

internalfunc+<A, B, C>(lhs:@escaping(A)throws->B,                         rhs:@escaping(B)throws->C)->(A)throws->C{return{tryrhs(lhs($0))}}publicfunc run()throws{try(determineTarget+ build+ analyze+ output)()}

If you're familiar with the functional programming world, you might know the above technique as thepipe operator (thanks toAlexey Demedreckiy for pointing this out!)

🗺 Usingmap() andflatMap() on optionals you can chain multiple operations without having to use lengthyif lets orguards:

// BEFOREguardlet string=argument(at:1)else{return}guardlet url=URL(string: string)else{return}handle(url)// AFTERargument(at:1).flatMap(URL.init).map(handle)

🚀 Using self-executing closures is a great way to encapsulate lazy property initialization:

classStoreViewController:UIViewController{private lazyvarcollectionView:UICollectionView={letlayout=UICollectionViewFlowLayout()letview=UICollectionView(frame:self.view.bounds, collectionViewLayout: layout)        view.delegate=self        view.dataSource=selfreturn view}()overridefunc viewDidLoad(){        super.viewDidLoad()        view.addSubview(collectionView)}}

⚡️ You can speed up your Swift package tests using the--parallel flag. ForMarathon, the tests execute 3 times faster that way!

swift test --parallel

🛠 Struggling with mockingUserDefaults in a test? The good news is: you don't need mocking - just create a real instance:

classLoginTests:XCTestCase{privatevaruserDefaults:UserDefaults!privatevarmanager:LoginManager!overridefunc setUp(){        super.setup()                userDefaults=UserDefaults(suiteName: #file)        userDefaults.removePersistentDomain(forName: #file)                manager=LoginManager(userDefaults: userDefaults)}}

👍 Using variadic parameters in Swift, you can create some really nice APIs that take a list of objects without having to use an array:

extensionCanvas{func add(_ shapes:Shape...){        shapes.forEach(add)}}letcircle=Circle(center:CGPoint(x:5, y:5), radius:5)letlineA=Line(start:.zero, end:CGPoint(x:10, y:10))letlineB=Line(start:CGPoint(x:0, y:10), end:CGPoint(x:10, y:0))letcanvas=Canvas()canvas.add(circle, lineA, lineB)canvas.render()

😮 Just like you can refer to a Swift function as a closure, you can do the same thing with enum cases with associated values:

enumUnboxPath{case key(String)case keyPath(String)}structUserSchema{staticletname=key("name")staticletage=key("age")staticletposts=key("posts")privatestaticletkey=UnboxPath.key}

📈 The=== operator lets you check if two objects are the same instance. Very useful when verifying that an array contains an instance in a test:

protocolInstanceEquatable:class,Equatable{}extensionInstanceEquatable{staticfunc==(lhs:Self, rhs:Self)->Bool{return lhs=== rhs}}extensionEnemy:InstanceEquatable{}func testDestroyingEnemy(){    player.attack(enemy)XCTAssertTrue(player.destroyedEnemies.contains(enemy))}

😎 Cool thing about Swift initializers: you can call them using dot syntax and pass them as closures! Perfect for mocking dates in tests.

classLogger{privateletstorage:LogStorageprivateletdateProvider:()->Dateinit(storage:LogStorage=.init(), dateProvider:@escaping()->Date=Date.init){self.storage= storageself.dateProvider= dateProvider}func log(event:Event){        storage.store(event: event, date:dateProvider())}}

📱 Most of my UI testing logic is now categories onXCUIApplication. Makes the test cases really easy to read:

func testLoggingInAndOut(){XCTAssertFalse(app.userIsLoggedIn)        app.launch()    app.login()XCTAssertTrue(app.userIsLoggedIn)        app.logout()XCTAssertFalse(app.userIsLoggedIn)}func testDisplayingCategories(){XCTAssertFalse(app.isDisplayingCategories)        app.launch()    app.login()    app.goToCategories()XCTAssertTrue(app.isDisplayingCategories)}

🙂 It’s a good idea to avoid “default” cases when switching on Swift enums - it’ll “force you” to update your logic when a new case is added:

enumState{case loggedIncase loggedOutcase onboarding}func handle(_ state:State){switch state{case.loggedIn:showMainUI()case.loggedOut:showLoginUI()    // Compiler error: Switch must be exhaustive}}

💂 It's really cool that you can use Swift's 'guard' statement to exit out of pretty much any scope, not only return from functions:

// You can use the 'guard' statement to...forstringin strings{    // ...continue an iterationguardshouldProcess(string)else{continue}        // ...or break itguard !shouldBreak(for: string)else{break}        // ...or returnguard !shouldReturn(for: string)else{return}        // ..or throw an errorguard string.isValidelse{throwStringError.invalid(string)}        // ...or exit the programguard !shouldExit(for: string)else{exit(1)}}

❤️ Love how you can pass functions & operators as closures in Swift. For example, it makes the syntax for sorting arrays really nice!

letarray=[3,9,1,4,6,2]letsorted= array.sorted(by:<)

🗝 Here's a neat little trick I use to get UserDefault key consistency in Swift (#function expands to the property name in getters/setters). Just remember to write a good suite of tests that'll guard you against bugs when changing property names.

extensionUserDefaults{varonboardingCompleted:Bool{get{returnbool(forKey: #function)}set{set(newValue, forKey: #function)}}}

📛 Want to use a name already taken by the standard library for a nested type? No problem - just useSwift. to disambiguate:

extensionCommand{enumError:Swift.Error{case missingcase invalid(String)}}

📦 Playing around with usingWrap to implementEquatable for any type, primarily for testing:

protocolAutoEquatable:Equatable{}extensionAutoEquatable{staticfunc==(lhs:Self, rhs:Self)->Bool{letlhsData=try!wrap(lhs)asDataletrhsData=try!wrap(rhs)asDatareturn lhsData== rhsData}}

📏 One thing that I find really useful in Swift is to use typealiases to reduce the length of method signatures in generic types:

publicclassPathFinder<Object:PathFinderObject>{publictypealiasMap=Object.MappublictypealiasNode=Map.NodepublictypealiasPath=PathFinderPath<Object>publicstaticfunc possiblePaths(for object:Object, at rootNode:Node, on map:Map)->Path.Sequence{return.init(object: object, rootNode: rootNode, map: map)}}

📖 You can reference either the external or internal parameter label when writing Swift docs - and they get parsed the same:

// EITHER:classFoo{    /**    *   - parameter string: A string    */func bar(with string:String){}}// OR:classFoo{    /**    *   - parameter with: A string    */func bar(with string:String){}}

👍 Finding more and more uses for auto closures in Swift. Can enable some pretty nice APIs:

extensionDictionary{mutatingfunc value(for key:Key, orAdd valueClosure:@autoclosure()->Value)->Value{iflet value=self[key]{return value}letvalue=valueClosure()self[key]= valuereturn value}}

🚀 I’ve started to become a really big fan of nested types in Swift. Love the additional namespacing it gives you!

publicstructMap{publicstructModel{publicletsize:Sizepubliclettheme:Themepublicvarterrain:[Position:Terrain.Model]publicvarunits:[Position:Unit.Model]publicvarbuildings:[Position:Building.Model]}publicenumDirection{case upcase rightcase downcase left}publicstructPosition{publicvarx:Intpublicvary:Int}publicenumSize:String{case small="S"case medium="M"case large="L"case extraLarge="XL"}}

About

A collection of Swift tips & tricks that I've shared on Twitter

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp