Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon

How-To Tutorials - iOS Programming

64 Articles
article-image-understanding-data-structures-in-swift
Avi Tsadok
22 Oct 2024
10 min read
Save for later

Understanding Data Structures in Swift

Avi Tsadok
22 Oct 2024
10 min read
This article is an excerpt from the book, The Ultimate iOS Interview Playbook, by Avi Tsadok. The iOS Interview Guide is an essential book for iOS developers who want to maximize their skills and prepare for the competitive world of interviews on their way to getting their dream job. The book covers all the crucial aspects, from writing a resume to reviewing interview questions, and passing the architecture interview successfully.Introduction In iOS development, data structures are fundamental tools for managing and organizing data. Whether you are preparing for a technical interview or building robust iOS applications, mastering data structures like arrays, dictionaries, and sets is essential. This tutorial will guide you through the essential data structures in Swift, explaining their importance and providing practical examples of how to use them effectively. By the end of this tutorial, you will have a solid understanding of how to work with these data structures, making your code more efficient, modular, and reusable. Prerequisites Before diving into the tutorial, make sure you have the following prerequisites: Familiarity with Swift Programming Language: A basic understanding of Swift syntax, including variables, functions, and control flow, is essential for this tutorial. Xcode Installed: Ensure you have Xcode installed on your Mac. You can download it from the Mac App Store if you haven’t done so already. Basic Understanding of Object-Oriented Programming: Knowing concepts such as classes and objects will help you better understand the examples provided in this tutorial. Step-by-Step Instructions 1. Learning the Importance of Data Structures Data structures play a crucial role in iOS development. They allow you to store, manage, and manipulate data efficiently, which is especially important in performance-sensitive applications. Whether you're handling user data, managing app state, or working with APIs, choosing the right data structure can significantly impact your app's performance and scalability. Swift provides several built-in data structures, including arrays, dictionaries, and sets. Each of these data structures offers unique advantages and is suitable for different use cases. Understanding when and how to use each of them is a key skill for any iOS developer. 2. Working with Arrays Arrays are one of the most commonly used data structures in Swift. They allow you to store ordered collections of elements, making them ideal for tasks that require sequential access to data. Declaring and Initializing an Array To declare and initialize an array in Swift, you can use the following syntax: var numbers: [Int] = [1, 2, 3, 4, 5] This creates an array of integers containing the values 1 through 5. Arrays in Swift are type-safe, meaning you can only store elements of the specified type (in this case, Int). Removing Duplicates from an Array A common task in programming is to remove duplicate elements from an array. Swift makes this easy by converting the array into a Set, which automatically removes duplicates, and then converting it back into an array: let arrayWithDuplicates = [1, 2, 3, 3, 4, 5, 5] let arrayWithNoDuplicates = Array(Set(arrayWithDuplicates)) This approach is efficient and concise, leveraging the unique properties of sets to remove duplicates. Iterating Over an Array Arrays provide several methods for iterating over their elements. The most common approach is to use a for-in loop: for number in numbers {    print(number) } This loop prints each element in the array to the console. You can also use methods like map, filter, and reduce for more advanced operations on arrays. 3. Implementing a Queue Using an Array A queue is a data structure that follows the First-In-First-Out (FIFO) principle, where the first element added is the first one to be removed. Queues are commonly used in scenarios like task scheduling, breadth-first search algorithms, and managing requests in networking. In Swift, you can implement a basic queue using an array. Here’s an example: struct Queue<Element> {    private var array: [Element] = []    var isEmpty: Bool {        return array.isEmpty    }    var count: Int {        return array.count    }    mutating func enqueue(_ element: Element) {        array.append(element)    }    mutating func dequeue() -> Element? {        return array.isEmpty ? nil : array.removeFirst()    } } In this implementation: The enqueue method adds an element to the end of the array. The dequeue method removes and returns the first element in the array. Queues are useful in many scenarios, such as managing tasks in a multi-threaded environment or implementing a breadth-first search algorithm. 4. Dictionaries in Swift Dictionaries are another powerful data structure in Swift. They store data in key-value pairs, allowing you to quickly look up values based on their associated keys. Dictionaries are ideal for tasks where you need fast access to data based on a unique identifier. Declaring and Initializing a Dictionary Here’s how you can declare and initialize a dictionary in Swift: var userAges: [String: Int] = ["Alice": 25, "Bob": 30] In this example, the keys are strings representing user names, and the values are integers representing their ages. Accessing and Modifying Dictionary Values You can access and modify values in a dictionary using the key: if let age = userAges["Alice"] {    print("Alice is \(age) years old.") } userAges["Alice"] = 26 This code snippet retrieves Alice's age and updates it to 26. Dictionaries are highly efficient for lookups, making them a valuable tool when working with large datasets. Adding and Removing Key-Value Pairs To add a new key-value pair to a dictionary, simply assign a value to a new key: userAges["Charlie"] = 22 To remove a key-value pair, use the removeValue(forKey:) method: userAges.removeValue(forKey: "Bob") 5. Exploring Sets Sets in Swift are similar to arrays, but with one key difference: they do not allow duplicate elements. Sets are unordered collections of unique elements, making them ideal for tasks like checking membership, ensuring uniqueness, and performing set operations (e.g., union, intersection). Declaring and Initializing a Set Here’s how you can declare and initialize a set in Swift:  let uniqueNumbers: Set = [1, 2, 3, 4, 5] Unlike arrays, sets do not maintain the order of elements. However, they are more efficient for operations like checking if an element exists. Performing Set Operations Swift sets support various operations that are common in set theory, such as union, intersection, and subtraction: let evenNumbers: Set = [2, 4, 6, 8] let oddNumbers: Set = [1, 3, 5, 7] let union = evenNumbers.union(oddNumbers)  // All unique elements from both sets let intersection = evenNumbers.intersection([4, 5, 6])  // Elements common to both sets let difference = evenNumbers.subtracting([4, 6])  // Elements in evenNumbers but not in the other set 6. Understanding the Codable Protocol The Codable protocol in Swift simplifies encoding and decoding data, making it easier to work with JSON and other data formats. This is especially useful when interacting with web APIs or saving data to disk. Defining a Codable Struct Here’s an example of a struct that conforms to the Codable protocol: struct Person: Codable {    var name: String    var age: Int    var address: String } With Codable, you can easily encode and decode instances of Person using JSONEncoder and JSONDecoder: let person = Person(name: "Alice", age: 25, address: "123 Main St") let jsonData = try JSONEncoder().encode(person) let decodedPerson = try JSONDecoder().decode(Person.self, from: jsonData) Output and Explanation For each code snippet, you should test and verify that the output matches the expected results. For example, when implementing the queue structure, enqueue and dequeue elements to ensure the correct order of processing. Similarly, when working with dictionaries, confirm that you can retrieve, add, and remove key-value pairs correctly. Conclusion This tutorial has covered fundamental data structures in Swift, including arrays, dictionaries, and sets, and their practical applications in iOS development. Understanding these data structures will make you a better Swift developer and prepare you for technical interviews and real-world projects. Author BioAvi Tsadok, seasoned iOS developer with a 13-year career, has proven his expertise leading projects for notable companies like Any.do, a top productivity app, and currently at Melio Payments, where he steers the mobile team. Known for his ability to simplify complex tech concepts, Avi has written four books and published 40+ tutorials and articles that enlighten and empower aspiring iOS developers. His voice resonates beyond the page, as he's a recognized public speaker and has conducted numerous interviews with fellow iOS professionals, furthering the field's discourse and development.
Read more
  • 0
  • 0
  • 525

article-image-understanding-the-foundation-of-protocol-oriented-design
Expert Network
30 Jun 2021
7 min read
Save for later

Understanding the Foundation of Protocol-oriented Design

Expert Network
30 Jun 2021
7 min read
When Apple announced Swift 2 at the World Wide Developers Conference (WWDC) in 2016, they also declared that Swift was the world’s first protocol-oriented programming (POP) language. From its name, we might assume that POP is all about protocol; however, that would be a wrong assumption. POP is about so much more than just protocol; it is actually a new way of not only writing applications but also thinking about programming. This article is an excerpt from the book Mastering Swift, 6th Edition by Jon Hoffman. In this article, we will discuss a protocol-oriented design and how we can use protocols and protocol extensions to replace superclasses. We will look at how to define animal types for a video game in a protocol-oriented way. Requirements When we develop applications, we usually have a set of requirements that we need to develop against. With that in mind, let’s define the requirements for the animal types that we will be creating in this article: We will have three categories of animals: land, sea, and air. Animals may be members of multiple categories. For example, an alligator can be a member of both the land and sea categories. Animals may attack and/or move when they are on a tile that matches the categories they are in. Animals will start off with a certain number of hit points, and if those hit points reach 0 or less, then they will be considered dead. POP Design We will start off by looking at how we would design the animal types needed and the relationships between them. Figure 1 shows our protocol-oriented design: Figure 1: Protocol-oriented design In this design, we use three techniques: protocol inheritance, protocol composition, and protocol extensions. Protocol inheritance Protocol inheritance is where one protocol can inherit the requirements from one or more additional protocols. We can also inherit requirements from multiple protocols, whereas a class in Swift can have only one superclass. Protocol inheritance is extremely powerful because we can define several smaller protocols and mix/match them to create larger protocols. You will want to be careful not to create protocols that are too granular because they will become hard to maintain and manage. Protocol composition Protocol composition allows types to conform to more than one protocol. With protocol-oriented design, we are encouraged to create multiple smaller protocols with very specific requirements. Let’s look at how protocol composition works. Protocol inheritance and composition are really powerful features but can also cause problems if used wrongly. Protocol composition and inheritance may not seem that powerful on their own; however, when we combine them with protocol extensions, we have a very powerful programming paradigm. Let’s look at how powerful this paradigm is. Protocol-oriented design — putting it all together We will begin by writing the Animal superclass as a protocol: protocol Animal { var hitPoints: Int { get set } } In the Animal protocol, the only item that we are defining is the hitPoints property. If we were putting in all the requirements for an animal in a video game, this protocol would contain all the requirements that would be common to every animal. We only need to add the hitPoints property to this protocol. Next, we need to add an Animal protocol extension, which will contain the functionality that is common for all types that conform to the protocol. Our Animal protocol extension would contain the following code: extension Animal { mutating func takeHit(amount: Int) { hitPoints -= amount } func hitPointsRemaining() -> Int { return hitPoints } func isAlive() -> Bool { return hitPoints > 0 ? true : false } } The Animal protocol extension contains the same takeHit(), hitPointsRemaining(), and isAlive() methods. Any type that conforms to the Animal protocol will automatically inherit these three methods. Now let’s define our LandAnimal, SeaAnimal, and AirAnimal protocols. These protocols will define the requirements for the land, sea, and air animals respectively: protocol LandAnimal: Animal { var landAttack: Bool { get } var landMovement: Bool { get } func doLandAttack() func doLandMovement() } protocol SeaAnimal: Animal { var seaAttack: Bool { get } var seaMovement: Bool { get } func doSeaAttack() func doSeaMovement() } protocol AirAnimal: Animal { var airAttack: Bool { get } var airMovement: Bool { get } func doAirAttack() func doAirMovement() } These three protocols only contain the functionality needed for their particular type of animal. Each of these protocols only contains four lines of code. This makes our protocol design much easier to read and manage. The protocol design is also much safer because the functionalities for the various animal types are isolated in their own protocols rather than being embedded in a giant superclass. We are also able to avoid the use of flags to define the animal category and, instead, define the category of the animal by the protocols it conforms to. In a full design, we would probably need to add some protocol extensions for each of the animal types, but we do not need them for our example here. Now, let’s look at how we would create our Lion and Alligator types using protocol-oriented design: struct Lion: LandAnimal { var hitPoints = 20 let landAttack = true let landMovement = true func doLandAttack() { print(“Lion Attack”) } func doLandMovement() { print(“Lion Move”) } } struct Alligator: LandAnimal, SeaAnimal { var hitPoints = 35 let landAttack = true let landMovement = true let seaAttack = true let seaMovement = true func doLandAttack() { print(“Alligator Land Attack”) } func doLandMovement() { print(“Alligator Land Move”) } func doSeaAttack() { print(“Alligator Sea Attack”) } func doSeaMovement() { print(“Alligator Sea Move”) } } Notice that we specify that the Lion type conforms to the LandAnimal protocol, while the Alligator type conforms to both the LandAnimal and SeaAnimal protocols. As we saw previously, having a single type that conforms to multiple protocols is called protocol composition and is what allows us to use smaller protocols, rather than one giant monolithic superclass. Both the Lion and Alligator types originate from the Animal protocol; therefore, they will inherit the functionality added with the Animal protocol extension. If our animal type protocols also had extensions, then they would also inherit the function added by those extensions. With protocol inheritance, composition, and extensions, our concrete types contain only the functionality needed by the particular animal types that they conform to. Since the Lion and Alligator types originate from the Animal protocol, we can use polymorphism. Let’s look at how this works: var animals = [Animal]() animals.append(Alligator()) animals.append(Alligator()) animals.append(Lion()) for (index, animal) in animals.enumerated() { if let _ = animal as? AirAnimal { print(“Animal at \(index) is Air”) } if let _ = animal as? LandAnimal { print(“Animal at \(index) is Land”) } if let _ = animal as? SeaAnimal { print(“Animal at \(index) is Sea”) } } In this example, we create an array that will contain Animal types named animals. We then create two instances of the Alligator type and one instance of the Lion type that are added to the animals array. Finally, we use a for-in loop to loop through the array and print out the animal type based on the protocol that the instance conforms to. Upgrade your knowledge and become an expert in the latest version of the Swift programming language with Mastering Swift 5.3, 6th Edition by Jon Hoffman. About Jon Hoffman has over 25 years of experience in the field of information technology. He has worked in the areas of system administration, network administration, network security, application development, and architecture. Currently, Jon works as an Enterprise Software Manager for Syn-Tech Systems.
Read more
  • 0
  • 0
  • 6198

article-image-your-first-swift-program
Packt
20 Feb 2018
4 min read
Save for later

Your First Swift Program

Packt
20 Feb 2018
4 min read
 In this article, by Keith Moon author of the book Swift 4 Programming Cookbook, we will learn how to write your first swift program. (For more resources related to this topic, see here.) Your first Swift program In this first recipe will be get up and running with Swift using a Swift Playground, and run our first piece of Swift code. Getting ready To run our first Swift program, we first need to download and install our IDE. During the beta of Apple's Xcode 9, it is available as a direct download from Apple's developer website at http://developer.apple.com/download, access to this beta will require a free Apple developer account. Once the beta has ended and Xcode 9 is publically available, it will also be available from the Mac App Store. By obtaining it from the Mac App Store, you will automatically be informed of updates, so this is the preferred route, once Xcode 9 is out of beta. Xcode from the Mac App Store Open up the Mac App Store, either from the dock or via Spotlight: Search for xcode: Click Install: Xcode is a large download (over 4 GB). So, depending on your internet connection, this could take a while! Progress can be monitored from Launchpad: Xcode as a direct download Go to the Apple Developer download page at http://developer.apple.com/download  Click the Download button to download Xcode within a .xip file.  Double click on the downloaded file to unpack the Xcode application. Drag the Xcode application into your Applications folder How to do it... With Xcode downloaded, let create our first Swift playground: Launch Xcode from the icon in your dock. From the welcome screen, choose Get started with a playground. From the template chooser, select the blank template from the iOS tab: Choose a name for your playground and a location to save it: Xcode Playgrounds can be based on one of three different Apple platforms, iOS, tvOS and macOS (the operating system formerly known as OSX). Playgrounds provide full access to the frameworks available to either iOS, tvOS or macOS, depending on which you choose. An iOS playground will be assumed for the entirety of this chapter, chiefly because this is the platform of choice of the author. Where recipes do have UI components, the iOS platform will be used until otherwise stated. You are now presented with a view that looks like this: Let's replace the word playground with Swift!. Press the blue play button in the bottom left-hand corner of the window to execute the code in the playground: Congratulations! You have just run some Swift code. On the right-hand side of the window, you will see the output of each line of code in the playground. We can see our line of code has output "Hello, Swift!": There's more... If you put your cursor over the output on the left-hand side, you will see two buttons, one that looks like an eye, another that is a circle: Click on the eye button and you get a Quick Look box of the output. This isn't that useful for just a string, but can be useful for more visual output like colors and views. Click on the square button, and a box will be added in-line, under your code, showing the output of the code. This can be really useful if you want to see how the output changes as you change the code. Summary In this article, we learnt how to run your first swift program. Resources for Article: Further resources on this subject: Your First Swift App [article] Exploring Swift [article] Functions in Swift [article]
Read more
  • 0
  • 0
  • 4327

article-image-integrating-messages-app
Packt
06 Apr 2017
17 min read
Save for later

Integrating with Messages App

Packt
06 Apr 2017
17 min read
In this article by Hossam Ghareeb, the author of the book, iOS Programming Cookbook, we will cover the recipe Integrating iMessage app with iMessage app. (For more resources related to this topic, see here.) Integrating iMessage app with iMessage app Using iMessage apps will let users use your apps seamlessly from iMessage without having to leave the iMessage. Your app can share content in the conversation, make payment, or do any specific job that seems important or is appropriate to do within a Messages app. Getting ready Similar to the Stickers app we created earlier, you need Xcode 8.0 or later version to create an iMessage app extension and you can test it easily in the iOS simulator. The app that we are going to build is a Google drive picker app. It will be used from an iMessage extension to send a file to your friends just from Google Drive. Before starting, ensure that you follow the instructions in Google Drive API for iOS from https://developers.google.com/drive/ios/quickstart to get a client key to be used in our app. Installing the SDK in Xcode will be done via CocoaPods. To get more information about CocoaPods and how to use it to manage dependencies, visit https://cocoapods.org/ . How to do it… We Open Xcode and create a new iMessage app, as shown, and name itFiles Picker:   Now, let's install Google Drive SDK in iOS using CocoaPods. Open terminal and navigate to the directory that contains your Xcode project by running this command: cd path_to_directory Run the following command to create a Pod file to write your dependencies: Pod init It will create a Pod file for you. Open it via TextEdit and edit it to be like this: use_frameworks! target 'PDFPicker' do end target 'MessagesExtension' do pod 'GoogleAPIClient/Drive', '~> 1.0.2' pod 'GTMOAuth2', '~> 1.1.0' end Then, close the Xcode app completely and run the pod install command to install the SDK for you. A new workspace will be created. Open it instead of the Xcode project itself. Prepare the client key from the Google drive app you created as we mentioned in the Getting ready section, because we are going to use it in the Xcode project. Open MessagesViewController.swift and add the following import statements: import GoogleAPIClient import GTMOAuth2 Add the following private variables just below the class declaration and embed your client key in the kClientID constant, as shown: private let kKeychainItemName = "Drive API" private let kClientID = "Client_Key_Goes_HERE" private let scopes = [kGTLAuthScopeDrive] private let service = GTLServiceDrive() Add the following code in your class to request authentication to Google drive if it's not authenticated and load file info: override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychain(forName: kKeychainItemName, clientID: kClientID, clientSecret: nil) { service.authorizer = auth } } // When the view appears, ensure that the Drive API service is authorized // and perform API calls override func viewDidAppear(_ animated: Bool) { if let authorizer = service.authorizer, canAuth = authorizer.canAuthorize where canAuth { fetchFiles() } else { present(createAuthController(), animated: true, completion: nil) } } // Construct a query to get names and IDs of 10 files using the Google Drive API func fetchFiles() { print("Getting files...") if let query = GTLQueryDrive.queryForFilesList(){ query.fields = "nextPageToken, files(id, name, webViewLink, webContentLink, fileExtension)" service.executeQuery(query, delegate: self, didFinish: #selector(MessagesViewController.displayResultWithTicket(ticket:finishedWit hObject:error:))) } } // Parse results and display func displayResultWithTicket(ticket : GTLServiceTicket, finishedWithObject response : GTLDriveFileList, if let error = error { showAlert(title: "Error", message: error.localizedDescription) return } var filesString = "" let files = response.files as! [GTLDriveFile] if !files.isEmpty{ filesString += "Files:n" for file in files{ filesString += "(file.name) ((file.identifier) ((file.webViewLink) ((file.webContentLink))n" } } else { filesString = "No files found." } print(filesString) } // Creates the auth controller for authorizing access to Drive API private func createAuthController() -> GTMOAuth2ViewControllerTouch { let scopeString = scopes.joined(separator: " ") return GTMOAuth2ViewControllerTouch( scope: scopeString, clientID: kClientID, clientSecret: nil, keychainItemName: kKeychainItemName, delegate: self, finishedSelector: #selector(MessagesViewController.viewController(vc:finishedWithAuth:error:) ) ) } // Handle completion of the authorization process, and update the Drive API // with the new credentials. func viewController(vc : UIViewController, finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) { if let error = error { service.authorizer = nil showAlert(title: "Authentication Error", message: error.localizedDescription) return } service.authorizer = authResult dismiss(animated: true, completion: nil) fetchFiles() } // Helper for showing an alert func showAlert(title : String, message: String) { let alert = UIAlertController( title: title, message: message, preferredStyle: UIAlertControllerStyle.alert ) let ok = UIAlertAction( title: "OK", style: UIAlertActionStyle.default, handler: nil ) alert.addAction(ok) self.present(alert, animated: true, completion: nil) } The code now requests authentication, loads files, and then prints them in the debug area. Now, try to build and run, you will see the following: Click on the arrow button in the bottom right corner to maximize the screen and try to log in with any Google account you have. Once the authentication is done, you will see the files' information printed in the debug area. Now, let's add a table view that will display the files' information and once a user selects a file, we will download this file to send it as an attachment to the conversation. Now, open theMainInterface.storyboard, drag a table view from Object Library, and add the following constraints: Set the delegate and data source of the table view from interface builder by dragging while holding down the Ctrl key to theMessagesViewController. Then, add an outlet to the table view, as follows, to be used to refresh the table with the files:  Drag a UITabeView cell from Object Library and drop it in the table view. For Attribute Inspector, set the cell style to Basic and the identifier to cell. Now, return to MessagesViewController.swift. Add the following property to hold the current display files: private var currentFiles = [GTLDriveFile]() Edit the displayResultWithTicket function to be like this: // Parse results and display func displayResultWithTicket(ticket : GTLServiceTicket, finishedWithObject response : GTLDriveFileList, error : NSError?) { if let error = error { showAlert(title: "Error", message: error.localizedDescription) return } var filesString = "" let files = response.files as! [GTLDriveFile] self.currentFiles = files if !files.isEmpty{ filesString += "Files:n" for file in files{ filesString += "(file.name) ((file.identifier) ((file.webViewLink) ((file.webContentLink))n" } } else { filesString = "No files found." } print(filesString) self.filesTableView.reloadData() } Now, add the following method for the table view delegate and data source: // MARK: - Table View methods - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.currentFiles.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell") let file = self.currentFiles[indexPath.row] cell?.textLabel?.text = file.name return cell! } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let file = self.currentFiles[indexPath.row] // Download File here to send as attachment. if let downloadURLString = file.webContentLink{ let url = NSURL(string: downloadURLString) if let name = file.name{ let downloadedPath = (documentsPath() as NSString).appendingPathComponent("(name)") let fetcher = service.fetcherService.fetcher(with: url as! URL) let destinationURL = NSURL(fileURLWithPath: downloadedPath) as URL fetcher.destinationFileURL = destinationURL fetcher.beginFetch(completionHandler: { (data, error) in if error == nil{ self.activeConversation?.insertAttachment(destinationURL, withAlternateFilename: name, completionHandler: nil) } }) } } } private func documentsPath() -> String{ let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) return paths.first ?? "" } Now, build and run the app, and you will see the magic: select any file and the app will download and save it to the local disk and send it as an attachment to the conversation, as illustrated: How it works… We started by installing the Google Drive SDK to the Xcode project. This SDK has all the APIs that we need to manage drive files and user authentication. When you visit the Google developers' website, you will see two options to install the SDK: manually or using CocoaPods. I totally recommend using CocoaPods to manage your dependencies as it is simple and efficient. Once the SDK has been installed via CocoaPods, we added some variables to be used for the Google Drive API and the most important one is the client key. You can access this value from the project you have created in the Google Developers Console. In the viewDidLoad function, first, we check if we have an authentication saved in KeyChain, and then, we use it. We can do that by calling GTMOAuth2ViewControllerTouch.authForGoogleFromKeychain, which takes the Keychain name and client key as parameters to search for authentication. It's useful as it helps you remember the last authentication and there is no need to ask for user authentication again if a user has already been authenticated before. In viewDidAppear, we check if a user is already authenticated; so, in that case, we start fetching files from the drive and, if not, we display the authentication controller, which asks a user to enter his Google account credentials. To display the authentication controller, we present the authentication view controller created in the createAuthController() function. In this function, the Google Drive API provides us with the GTMOAuth2ViewControllerTouch class, which encapsulates all logic for Google account authentication for your app. You need to pass the client key for your project, keychain name to save the authentication details there, and the finished  viewController(vc : UIViewController, finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) selector that will be called after the authentication is complete. In that function, we check for errors and if something wrong happens, we display an alert message to the user. If no error occurs, we start fetching files using the fetchFiles() function. In the fetchFiles() function, we first create a query by calling GTLQueryDrive.queryForFilesList(). The GTLQueryDrive class has all the information you need about your query, such as which fields to read, for example, name, fileExtension, and a lot of other fields that you can fetch from the Google drive. You can specify the page size if you are going to call with pagination, for example, 10 by 10 files. Once you are happy with your query, execute it by calling service.executeQuery, which takes the query and the finished selector to be called when finished. In our example, it will call the displayResultWithTicket function, which prepares the files to be displayed in the table view. Then, we call self.filesTableView.reloadData() to refresh the table view to display the list of files. In the delegate function of table view didSelectRowAt indexPath:, we first read the webContentLink property from the GTLDriveFile instance, which is a download link for the selected file. To fetch a file from the Google drive, the API provides us with GTMSessionFetcher that can fetch a file and write it directly to a device's disk locally when you pass a local path to it. To create GTMSessionFetcher, use the service.fetcherService factory class, which gives you instance to a fetcher via the file URL. Then, we create a local path to the downloaded file by appending the filename to the documents path of your app and then, pass it to fetcher via the following command: fetcher.destinationFileURL = destinationURL Once you set up everything, call fetcher.beginFetch and pass a completion handler to be executed after finishing the fetching. Once the fetching is completed successfully, you can get a reference to the current conversation so that you can insert the file to it as an attachment. To do this, just call the following function: self.activeConversation?.insertAttachment(destinationURL, withAlternateFilename: name, completionHandler: nil) There's more… Yes, there's more that you can do in the preceding example to make it fancier and more appealing to users. Check the following options to make it better: You can show a loading indicator or progress bar while a file is downloading. Checks if the file is already downloaded, and if so, there is no need to download it again. Adding pagination to request only 10 files at a time. Options to filter documents by type, such as PDF, images, or even by date. Search for a file in your drive. Showing Progress indicator As we said, one of the features that we can add in the preceding example is the ability to show a progress bar indicating the downloading progress of a file. Before starting how to show a progress bar, let's install a library that is very helpful in managing/showing HUD indicators, which is MBProgressHUD. This library is available in GitHub at https://github.com/jdg/MBProgressHUD. As we agreed before, all packages are managed via CocoaPods, so now, let's install the library via CocoaPods, as shown: Open the Podfile and update it to be as follows: use_frameworks! target 'PDFPicker' do end target 'MessagesExtension' do pod 'GoogleAPIClient/Drive', '~> 1.0.2' pod 'GTMOAuth2', '~> 1.1.0' pod 'MBProgressHUD', '~> 1.0.0' end Run the following command to install the dependencies: pod install Now, at the top of the MessagesViewController.swift file, add the following import statement to import the library: Now, let's edit the didSelectRowAtIndexPath function to be like this: func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let file = self.currentFiles[indexPath.row] // Download File here to send as attachment. if let downloadURLString = file.webContentLink{ let url = NSURL(string: downloadURLString) if let name = file.name{ let downloadedPath = (documentsPath() as NSString).appendingPathComponent("(name)") let fetcher = service.fetcherService.fetcher(with: url as! URL) let destinationURL = NSURL(fileURLWithPath: downloadedPath) as URL fetcher.destinationFileURL = destinationURL var progress = Progress() let hud = MBProgressHUD.showAdded(to: self.view, animated: true) hud.mode = .annularDeterminate; hud.progressObject = progress fetcher.beginFetch(completionHandler: { (data, error) in if error == nil{ hud.hide(animated: true) self.activeConversation?.insertAttachment(destinationURL, withAlternateFilename: name, completionHandler: nil) } }) fetcher.downloadProgressBlock = { (bytes, written, expected) in let p = Double(written) * 100.0 / Double(expected) print(p) progress.totalUnitCount = expected progress.completedUnitCount = written } } } } First, we create an instance of MBProgressHUD and set its type to annularDeterminate, which means to display a circular progress bar. HUD will update its progress by taking a reference to the NSProgress object. Progress has two important variables to determine the progress value, which are totalUnitCount and completedUnitCount. These two values will be set inside the progress completion block, downloadProgressBlock, in the fetcher instance. HUD will be hidden in the completion block that will be called once the download is complete. Now build and run; after authentication, when you click on a file, you will see something like this: As you can see, the progressive view is updated with the percentage of download to give the user an overview of what is going on. Request files with pagination Loading all files at once is easy from the development side, but it's incorrect from the user experience side. It will take too much time at the beginning when you get the list of all the files and it would be great if we could request only 10 files at a time with pagination. In this section, we will see how to add the pagination concept to our example and request only 10 files at a time. When a user scrolls to the end of the list, we will display a loading indicator, call the next page, and append the results to our current results. Implementation of pagination is pretty easy and requires only a few changes in our code. Let's see how to do it: We will start by adding the progress cell design in MainInterface.storyboard. Open the design of MessagesViewController and drag a new cell along with our default cell. Drag a UIActivityIndicatorView from ObjectLibrary and place it as a subview to the new cell. Add center constraints to center it horizontally and vertically as shown: Now, select the new cell and go to attribute inspector to add an identifier to the cell and disable the selection as illustrated: Now, from the design side, we are ready. Open MessagesViewController.swift to add some tweaks to it. Add the following two variables to the list of our current variables: private var doneFetchingFiles = false private var nextPageToken: String! The doneFetchingFiles flag will be used to hide the progress cell when we try to load the next page from Google Drive and returns an empty list. In that case, we know that we are done with the fetching files and there is no need to display the progress cell any more. The nextPageToken contains the token to be passed to the GTLQueryDrive query to ask it to load the next page. Now, go to the fetchFiles() function and update it to be as shown: func fetchFiles() { print("Getting files...") if let query = GTLQueryDrive.queryForFilesList(){ query.fields = "nextPageToken, files(id, name, webViewLink, webContentLink, fileExtension)" query.mimeType = "application/pdf" query.pageSize = 10 query.pageToken = nextPageToken service.executeQuery(query, delegate: self, didFinish: #selector(MessagesViewController.displayResultWithTicket(ticket:finishedWit hObject:error:))) } } The only difference you can note between the preceding code and the one before that is setting the pageSize and pageToken. For pageSize, we set how many files we require for each call and for pageToken, we pass the token to get the next page. We receive this token as a response from the previous page call. This means that, at the first call, we don't have a token and it will be passed as nil. Now, open the displayResultWithTicket function and update it like this: // Parse results and display func displayResultWithTicket(ticket : GTLServiceTicket, finishedWithObject response : GTLDriveFileList, error : NSError?) { if let error = error { showAlert(title: "Error", message: error.localizedDescription) return } var filesString = "" nextPageToken = response.nextPageToken let files = response.files as! [GTLDriveFile] doneFetchingFiles = files.isEmpty self.currentFiles += files if !files.isEmpty{ filesString += "Files:n" for file in files{ filesString += "(file.name) ((file.identifier) ((file.webViewLink) ((file.webContentLink))n" } } else { filesString = "No files found." } print(filesString) self.filesTableView.reloadData() } As you can see, we first get the token that is to be used to load the next page. We get it by calling response.nextPageToken and setting it to our new  nextPageToken property so that we can use it while loading the next page. The doneFetchingFiles will be true only if the current page we are loading has no files, which means that we are done. Then, we append the new files we get to the current files we have. We don't know when to fire the calling of the next page. We will do this once the user scrolls down to the refresh cell that we have. To do so, we will implement one of the UITableViewDelegate methods, which is willDisplayCell, as illustrated: func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if !doneFetchingFiles && indexPath.row == self.currentFiles.count { // Refresh cell fetchFiles() return } } For any cell that is going to be displayed, this function will be triggered with indexPath of the cell. First, we check if we are not done with the fetching files and the row is equal to the last row, then, we fire fetchFiles() again to load the next page. As we added a new refresh cell at the bottom, we should update our UITableViewDataSource functions, such as numbersOfRowsInSection and cellForRow. Check our updated functions, shown as follows: func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return doneFetchingFiles ? self.currentFiles.count : self.currentFiles.count + 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if !doneFetchingFiles && indexPath.row == self.currentFiles.count{ return tableView.dequeueReusableCell(withIdentifier: "progressCell")! } let cell = tableView.dequeueReusableCell(withIdentifier: "cell") let file = self.currentFiles[indexPath.row] cell?.textLabel?.text = file.name return cell! } As you can see, the number of rows will be equal to the current files' count plus one for the refresh cell. If we are done with the fetching files, we will return only the number of files. Now, everything seems perfect. When you build and run, you will see only 10 files listed, as shown: And when you scroll down you would see the progress cell that and 10 more files will be called. Summary In this article, we learned how to integrate iMessage app with iMessage app. Resources for Article: Further resources on this subject: iOS Security Overview [article] Optimizing JavaScript for iOS Hybrid Apps [article] Testing our application on an iOS device [article]
Read more
  • 0
  • 0
  • 1604

article-image-toy-bin
Packt
08 Mar 2017
8 min read
Save for later

Toy Bin

Packt
08 Mar 2017
8 min read
In this article by Steffen Damtoft Sommer and Jim Campagno, the author of the book Swift 3 Programming for Kids, we will walk you through what an array is. These are considered collection types in Swift and are very powerful. (For more resources related to this topic, see here.) Array An array stores values of the same type in an ordered list. The following is an example of an array: let list = ["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Rubix Cube"] This is an array (which you can think of as a list). Arrays are an ordered collections of values. We've created a constant called list of the [String] type and assigned it a value that represents our list of toys that we want to take with us. When describing arrays, you surround the type of the values that are being stored in the array by square brackets, [String]. Following is another array called numbers which contains four values being 5, 2, 9 and 22: let numbers = [5, 2, 9, 22] You would describe numbers as being an array which contains Int values which can be written as [Int]. We can confirm this by holding Alt and selecting the numbers constant to see what its type is in a playground file: What if we were to go back and confirm that list is an array of String values. Let's Alt click that constant to make sure: Similar to how we created instances of String and Int without providing any type information, we're doing the same thing here when we create list and numbers. Both list and numbers are created taking advantage of type inference. In creating our two arrays, we weren't explicit in providing any type information, we just created the array and Swift was able to figure out the type of the array for us. If we want to, though, we can provide type information, as follows: let colors: [String] = ["Red", "Orange", "Yellow"] colors is a constant of the [String] type. Now that we know how to create an array in swift, which can be compared to a list in real life, how can we actually use it? Can we access various items from the array? If so, how? Also, can we add new items to the list in case we forgot to include any items? Yes to all of these questions. Every element (or item) in an array is indexed. What does that mean? Well, you can think of being indexed as being numbered. Except that there's one big difference between how we humans number things and how arrays number things. Humans start from 1 when they create a list (just like we did when we created our preceding list). An array starts from 0. So, the first element in an array is considered to be at index 0:  Always remember that the first item in any array begins at 0. If we want to grab the first item from an array, we will do so as shown using what is referred to as subscript syntax: That 0 enclosed in two square brackets is what is known as subscript syntax. We are looking to access a certain element in the array at a certain index. In order to do that, we need to use subscript index, including the index of the item we want within square brackets. In doing so, it will return the value at the index. The value at the index in our preceding example is Legos. The = sign is also referred to as the assignment operator. So, we are assigning the Legos value to a new constant, called firstItem. If we were to print out firstItem, Legos should print to the console: print(firstItem) // Prints "Legos" If we want to grab the last item in this array, how do we do it? Well, there are five items in the array, so the last item should be at index 5, right? Wrong! What if we wrote the following code (which would be incorrect!): let lastItem = list[5] This would crash our application, which would be bad. When working with arrays, you need to ensure that you don't attempt to grab an item at a certain index which doesn't exist. There is no item in our array at index 5, which would make our application crash. When you run your app, you will receive the fatal error: Index out of range error. This is shown in the screenshot below: Let's correctly grab the last item in the array: let lastItem = list[4] print("I'm not as good as my sister, but I love solving the (lastItem)") // Prints "I'm not as good as my sister, but I love solving the Rubix Cube" Comments in code are made by writing text after //. None of this text will be considered code and will not be executed; it's a way for you to leave notes in your code. All of a sudden, you've now decided that you don't want to take the rubix cube as it's too difficult to play with. You were never able to solve it on Earth, so you start wondering why bringing it to the moon would help solve that problem. Bringing crayons is a much better idea. Let's swap out the rubix cube for crayons, but how do we do that? Using subscript syntax, we should be able to assign a new value to the array. Let's give it a shot: list[4] = "Crayons" This will not work! But why, can you take a guess? It's telling us that we cannot assign through subscript because list is a constant (we declared it using the let keyword). Ah! That's exactly how String and Int work. We decide whether or not we can change (mutate) the array based upon the let or var keyword just like every other type in Swift. Let's change the list array to a variable using the var keyword: var list = ["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Rubix Cube"] After doing so, we should be able to run this code without any problem: list[4] = "Crayons" If we decide to print the entire array, we will see the following print to console: ["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Crayons"] Note how Rubix Cube is no longer the value at index 4 (our last index); it has been changed to Crayons.  That's how we can mutate (or change) elements at certain indexes in our array. What if we want to add a new item to the array, how do we do that? We've just saw that trying to use subscript syntax with an index that doesn't exist in our array crashes our application, so we know we can't use that to add new items to our array. Apple (having created Swift) has created hundreds, if not thousands, of functions that are available in all the different types (like String, Int, and array). You can consider yourself an instance of a person (person being the name of the type). Being an instance of a person, you can run, eat, sleep, study, and exercise (among other things). These things are considered functions (or methods) that are available to you. Your pet rock doesn't have these functions available to it, why? This is because it's an instance of a rock and not an instance of a person. An instance of a rock doesn't have the same functions available to it that an instance of a person has. All that being said, an array can do things that a String and Int can't do. No, arrays can't run or eat, but they can append (or add) new items to themselves. An array can do this by calling the append(_:) method available to it. This method can be called on an instance of an array (like the preceding list) using what is known as dot syntax. In dot syntax, you write the name of the method immediately after the instance name, separated by a period (.), without any space: list.append("Play-Doh") Just as if we were to tell a person to run, we are telling the list to append. However, we can't just tell it to append, we have to pass an argument to the append function so that it can add it to the list. Our list array now looks like this: ["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Crayons", "Play-Doh"] Summary We have covered a lot of material important to understanding Swift and writing iOS apps here. Feel free to reread what you've read so far as well as write code in a playground file. Create your own arrays, add whatever items you want to it, and change values at certain indexes. Get used to the syntax of working with creating an arrays as well as appending new items. If you can feel comfortable up to this point with how arrays work, that's awesome, keep up the great work! Resources for Article:  Further resources on this subject: Introducing the Swift Programming Language [article] The Swift Programming Language [article] Functions in Swift [article]
Read more
  • 0
  • 0
  • 1678

article-image-lldb-and-command-line
Packt
15 Feb 2017
23 min read
Save for later

LLDB and the Command Line

Packt
15 Feb 2017
23 min read
In this article by Stuart Grimshaw, authors of the book Mastering macOS Programming, we're going to shift up a gear and reach deep into the LLDB environment, both in Xcode and as a standalone process. Some of this stuff may look a little esoteric at first glance, but rest assured, it is anything but. Working professionally as a developer means being comfortable with the command line, being aware of what is available in LLDB, and having at least a working knowledge of the scripting languages that are a part of developing software on almost all platforms. It's also really, really cool. Once you have a bit of momentum behind you, you'll wonder how you ever lived without these tools, which allow you to automate and customize and get right into the inner workings of your code. (For more resources related to this topic, see here.) In this article, you will learn about the following: What LLDB does Running code from within LLDB Creating and manipulating breakpoints and watchpoints How to customize LLDB to your requirements How to run the Swift REPL from within LLDB How to run a Python REPL within LLDB This one will require a lot of hands-on experimentation to familiarize yourself with what is presented here. So, take this one slowly, and try out as much as you can. Once you have absorbed this stuff, you'll never be the same developer again. LLDB So, what is this LLDB? Why do we see it at the console whenever we hit a breakpoint (or a bug)? This is the LLDB prompt: (lldb) Well, LLDB stands for low level debugger, and it does what it says it does. It actually does a bit more than that, which we'll get to very soon. LLDB makes use of several components from the LLVM project, which is basically a compiler, parser, and loads of other stuff that weneed (or Xcode needs) to build a program. However, as interesting as that is to look at, it is out of the scope of this article, and we will focus entirely on the debugger in this article. LLDB is not only available in the debug area console of Xcode, where we have made frequent use of it already,it can also be used at the command line, which we will also cover in this article. The LLDB interface is a very powerful tool; you can print to it from your code, you can interact with it, configure it to your requirements, change settings on the fly, and access both the Swift REPL and the Unix system that underlies Mac OS, without having to terminate a session. Using LLDB Returning to the code, set a breakpoint at this line of code: printObservation() Once execution has stopped at that breakpoint, type the following into the console: (lldb)bt This will produce a list of all the stack frames on the current thread that lead up to the line of code where execution was halted by the breakpoint, bt stands for backtrace. The output will look like this: (lldb) bt * thread #1: tid = 0x105a7d, 0x000000010000177c 5623_16_code`ViewController.testStepCommands(self=0x00006080000c2220) -> () + 12 at ViewController.swift:62, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 * frame #0: 0x000000010000177c 5623_16_code`ViewController.testStepCommands(self=0x00006080000c2220) -> () + 12 at ViewController.swift:62 frame #1: 0x00000001000014b2 5623_16_code`ViewController.viewDidLoad(self=0x00006080000c2220) -> () + 98 at ViewController.swift:17 ... lots more edited out frame #16: 0x00007fffbfafc255 libdyld.dylib`start + 1 (lldb) There is no need to understand all of this immediately,but looking through it, you will probably recognize the symbols at the top of the stack, since they are the methods that you have coded yourself. Everything leading up to that is provided by the frameworks you use. In this case, we see that most of the work is being done by AppKit, with a modest contribution from our own code (sorry, the truth can hurt at times). Frame0 is the last frame that was put on the stack. Above that, we see some information about the current thread, its number and thread ID (tid) and so on, as well as the line of code at which execution has been halted, and even the reason for doing so. In this case, we have hit breakpoint 3.1. Remember the viewDidLoad symbolic breakpoint produced several breakpoints? That's what the number behind the decimal point means, and breakpoint3 only occurs once, hence there can only be a 3.1. In large projects, this is an excellent way to answer the perennial question, how did the code get here? Debugging line-by-line Once we have halted the program at a breakpoint, we can take over control of its further execution: Continue The following commands are available to continue execution of the code (up until any subsequent breakpoints): (lldb) continue (lldb) c (lldb) thread continue They are all equivalent. Step over The following commands are equivalent to clicking on the step-over button: (lldb) next (lldb) n (lldb) thread step-over Step into The following commands are equivalent to clicking on the step-into button: (lldb) step (lldb) s (lldb) thread step-in This will advance execution of the code by one line, and when used at a function call, it will step into the function. The step-over and step-incommands are only different at a function call, otherwise they behave the same. Step out The following commands are equivalent to clicking on the step-out button: (lldb) finish (lldb) f (lldb) thread step-out Trying it out Spend some time getting used to stepping through the code using these commands. You'll find that very quickly, your muscle memory starts to do most of the work for you, freeing up some mental capacity for actually dealing with whatever bugs you may be dealing with. If you use one of the step-over or step-into commands (n and s are the most convenient), you can hit return again to repeat that command, which is a more comfortable way of repeatedly stepping through a debug session. Enable The following commands are available for enabling and disabling breakpoints: br disable br enable Enabling breakpoints en massedoes not re-enable those that were disabled individually. Printing in LLDB LLDB provides three convenient commands to print details of an object to the console: The p or printcommand gives us a standard description of the object. The pocommand (meaning print object) gives us either a standard description, or a custom description if we have implemented one. The frame variable command prints the same asprint, but does so without executing any code, thus avoiding the danger of side effects. Preparing classes for LLDB description We can provide custom classeswith any description we wish, and have it print with the po command, by declaring the class to conform to the CustomDebugStringConvertibleprotocol, and then implementing its required variable debugDescription. Let's create a Report class, that has a few properties of mixed types, and a debugDescription variable: class Report: CustomDebugStringConvertible { var title: String? var date: Date? var approved: Bool? var debugDescription: String { return "("Report")n(title)n(date)n(approved)" } } Now create an instance of the class, and one by one, populate the optional properties: let report = Report() report.title = "Weekly Summary" report.date = Date() report.approved = true Set a breakpoint at the first of those lines, where the report instance is declared. Now type po into the LLDB console. You'll get <uninitialized> in the console. Type s into the console, to step through the code. Keep your eye on the green tinted position pointer in the breakpoint gutter, as it will show you which lines of code are being executed during the initialization of a Report instance. Type s (or return) another five times until the initialization is complete, and then type po again. At this point we have the report variable initialized, with its properties still set to nil. (lldb) po report <uninitialized> (lldb) s (lldb) s (lldb) s (lldb) s (lldb) s (lldb) po report <uninitialized> (lldb) s (lldb) po report Report nil nil nil Continue stepping through the code, following this session: (lldb) s (lldb) s (lldb) s (lldb) po report Report Optional("Weekly Summary") nil nil etc... As you step through the code, you can see the report variable being populated with data. Stop hooks But what about having an update of an object's state automatically logged to the console everytime the code is stopped? Then we wouldn't need to go through all this typing, and as we continue debugging, we only need to worry about where to set breakpoints. To do that, we can add a so-called one-liner to a breakpoint that is then attached to a stop hook. Run the code again, and this time when the code halts, enter the following into the LLDB console: (lldb) b logReport (lldb) target stop-hook add --one-liner "po report" The first line creates a breakpoint and names it logReport. The second line attaches a command to that breakpoint, equivalent to selecting the debugger commandfrom the Edit Breakpoint... menu as, which does the same as if we had typed po report ourselves. Now add breakpoints to the next three lines of code, so that the code stops at each: Use the continue command (or just c) to move from one breakpoint to the next, and you'll see that, each time, we get a description of the report current state. Later on, we'll see that there is a way to log an object's state every time it changes, without it being dependent on halting the code, and without our having to specify where the changes take place. Printing formattednumbers LLDB contains its own formatting functionality, so when necessary we can format integers before they are printed to the console. We can print the integer asis, using the p command: (lldb) p 111 (Int) $R10 = 111 We can print its binary representation using the p/t command (the t stands for two): (lldb) p/t 111 (Int) $R11 = 0b0000...lots of zeroes...001101111 A hexadecimal representation is also available, using the p/x command: (lldb) p/x 111 (Int) $R12 = 0x000000000000006f An octal representation is available with the p/o command: (lldb) p/o 111 (Int) $R13 = 0157 Executingcode from LLDB One of LLDB's great strengths is its ability to reach into the current state of the program and alter it. So, for example, we could change the title property of our report object while the code is halted on a later breakpoint, with this code: expr report.title = "Monthly Summary" After this, the code runs as if "Monthly Summary" had been entered all along. This is an essential tool to master when debugging; the ability to change values on the fly can save you hundreds of program relaunches in a single day's debugging or prototyping. Type lookups This one is short, but sweet. Using the typelookup command, we get a summary of whatever type we may be interested in. Taking a very small class, our own Report class, as an example, type the following into LLDB: (lldb) type lookup Report This will produce output to the console like the following: class Report : CustomDebugStringConvertible { var title: Swift.String? var date: Foundation.Date? var approved: Swift.Bool? var debugDescription: Swift.String { get {} } @objc deinit init() } Now try it again with a few other types. Try this: (lldb) type lookup Array We won't reproduce the output here (it's quite extensive), but it will give you an idea of just how much information is only an LLDB command away. Breakpoints in LLDB We can add breakpoints of all types, and configure them to our requirements, from within LLDB, and with a little practice you'll find this is a whole lot quicker (as well as being more flexible) than clicking in the breakpoint gutter, right-clicking the breakpoint, and wading through the breakpoint edit window (which rather bizarrely is extremely difficult not to dismiss accidentally). It might take a while before you're convinced of this, but try to remember the time that you quit Macintosh or OS X apps using the menus. Adding a breakpoint To set a breakpoint that is in the current scope, use this command: (lldb) breakpoint set --line 56 This can be abbreviated to: (lldb) b 56 To set a breakpoint somewhere else, add the name of the file after the --file option: (lldb) breakpoint set --file AppDelegate.swift --line 17 You can also set a breakpoint by specifying a method name: (lldb) breakpoint set --method testBreakpoints Imagine setting two dozen breakpoints, across a number of files, some by name, some by line number, and doing it all with the mouse. This console business seriously is faster. Breakpoint list We can inspect a detailed list of all the breakpoints, of whatever type, and whether they are user or project bound breakpoints, with the following command: (lldb) breakpoint list Or its shorter version: (lldb) br li Attaching commands to breakpoints Using the breakpoint list, take note of one of the breakpoints (set an extra one if you have to), which you will alter once you have hit the first breakpoint set in your code. With the program execution halted on that first breakpoint, type the following into the LLDB console (using a breakpoint number from your own breakpoint list): (lldb) breakpoint command add 4.1 You will be prompted to add the command: Enter your debugger command(s). Type 'DONE' to end. > So, do as it says: Enter your debugger command(s). Type 'DONE' to end. >bt > DONE We have chosen bt here, as it's a nice big dump of data onto the console; we won't miss it. Now type: (lldb) c to continue execution, and the program will continue, until it halts at breakpoint 4.1 (or whichever breakpoint you added the command to), and prints the backtrace to the console. Creating a Swift Error breakpoint We saw that we can select Swift Error from the breakpoint types list at the bottom of the breakpoint navigator pane (using the + button), and this can also be done (substantially more quickly) directly in LLDB. (lldb) breakpoint set -E swift Or we can abbreviate this to one of the following: (lldb) br s -E swift (lldb) b -E swift We can also restrict the breakpoint to a specific error type: (lldb) b -E swift -O myError Naming breakpoints You can create de facto groups of breakpoints by naming them. Multiple breakpoints can share a name, and by referring to that name you can enable, disable and delete all those breakpoints with a single command: (lldb) br set -n testStepCommands -N group1 Breakpoint 3: 2 locations. (lldb) br set -n testBreakpoints -N group1 Breakpoint 4: 2 locations. (lldb) breakpoint disable group1 2 breakpoints disabled. Having created two breakpoints that share the name group1, we then disable them by passing the breakpointdisable command the name of the group. Watchpoints We mentioned earlier that we were going to see a way to log any changes tovariables without having to stop the code. This is where we do that. We could, of course, use the variable's setter and getter methods to do this, but the point here is that we don't need to change the program's code and then recompile etc.; all of this can be done without restarting the app. The syntax and console output of watchpoints is generally similar to that of breakpoints, as is made clear from the following input (and console output): (lldb) watchpoint set variable x LLDB confirms watchpoint creation with the details of the watchpoint: Watchpoint created: Watchpoint 1: addr = 0x7fff5fbfec48 size = 8 state = enabled type = w declare @ '/Users/stu/Documents/Books/myBooks/Packt/macOS Programming/Content/ch17 LLDB CLI/5623_17_code/5623_17_code/ViewController.swift:93' watchpoint spec = 'x' When you run code containing a watchpoint, you are basically creating a breakpoint that is attached to a variable rather than a line of code. Adding conditions and commands You can also attach conditions to watchpoint, just as you can to a breakpoint: (lldb) watchpoint modify -c (x==2) And similarly, command add works just as you would expect it to: (lldb) watchpoint command add 1 Enter your debugger command(s). Type 'DONE' to end. >print x > DONE So now you can monitor the progress and state of a variable without disturbing the code at all. In the preceding code, we simply assign a debugger command to watchpoint 1;but how did we get the number of the watchpoint? By checking the watchpoint list. Watchpoint lists Just as for breakpoints, we can get a list of watchpoints, using the following command: (lldb) watchpoint list This gives us a detailed list of all watchpoints, similar to the list below: Number of supported hardware watchpoints: 4 Current watchpoint: Watchpoint 1: addr = 0x7fff5fbfec48 size = 8 state = enabled type = w declare @ '/Users/stu/Documents/Books/myBooks/Packt/macOS Programming/Content/ch17 LLDB CLI/5623_17_code/5623_17_code/ViewController.swift:93' watchpoint spec = 'x' Watchpoints must be small. An Int of Bool is okay, a String object will be too large. Enabling, disabling, and deleting watchpoints The syntax for disabling, enabling and deleting watchpoints is the same as for breakpoints: (lldb) watchpoint disable All watchpoints disabled. (1 watchpoints) This disables watchpoints only, not breakpoints (lldb) watchpoint delete About to delete all watchpoints, do you want to do that?: [Y/n] y All watchpoints removed. (1 watchpoints) Persistent customization If you want LLDB to run commands as it starts up, like defining a list of watchpoints, breakpoints (and a whole ton of other stuff), you can place them in the .lldbinitfile, at these paths: ~.lldbinit-Xcode for LLDB within the Xcode debug console ~/.lldbinit for LLDB in the command line My .initlldb file contains a group of named symbolic breakpoints for things such as viewDidLoad, which are of use in most projects, but which are also disabled by the same file. When I need them for debugging I just need to enable them by name, as opposed to define them from scratch. This is particularly appropriate when breakpoints and watchpoints are a complicated combination of conditions, commands and so on. Getting help in LLBD The following command will provide you with the online docs regarding breakpoints and watchpoints: (lldb) help breakpoint (lldb) help watchpoint Of course, you can access the complete help pages with the following: (lldb) help Try to get accustomed to using the help pages; they contain a lot of information that is otherwise difficult (or tedious) to find, and overcoming any (understandable) reticence to dive into them is better done sooner rather than later. Using shell commands in LLDB You can send shell commands to the system without leaving LLDB by using the platform shell command: (lldb) platform shell pwd /path/to/current/directory... Often, being able to type in these one liners will save you breaking the flow while opening up a Terminal window, navigating to the current directory and so on. Given that shell commands can achieve almost anything on Unix-based machines, you might be tempted to spend a fair amount of time getting used to doing this. Forgot to launch your local server? No problem, just drop into the shell and fire up the server, and then return to the debug session. REPL in LLDB The Swift REPL also runs in LLDB. Actually, whenever you use the Swift REPL, it is running on LLDB: The next time you're running the REPL in a terminal window, try typing a colon, and you'll suddenly discover you were running LLDB all along. To the Swift REPL it from the LLDB console type repl: (lldb) repl 1> Now the REPL prompt is awaiting input. A session might look something like this: (lldb) repl 1>var a = [1,2] a: [Int] = 2 values { [0] = 1 [1] = 2 } 2>a.append(3) 3>a $R0: [Int] = 3 values { [0] = 1 [1] = 2 [2] = 3 } 4> This come is really handy when you need to check an idea in Swift while you are, for example, in the middle of an LLDB session. Switching between LLDB and Swift REPL While in the REPL, you can still punch through to the underlying LLDB session by prepending a command with a colon: 2>:type lookup Report class Report : CustomDebugStringConvertible { var title: Swift.String? var date: Foundation.Date? var approved: Swift.Bool? var debugDescription: Swift.String { get {} } @objc deinit init() } In this example, we’ve printed a summary of our Report class (from a REPL session that knows nothing of that class) by using LLDB's type lookup command preceded by the colon. Leaving a REPL session To leave the REPL and return to LLDB, type just a colon on its own: (lldb) repl 4>print("repl says hi") repl says hi 5>: (lldb) The REPL session has not ended, however. If you re-enter it with the repl command, the variables you defined previously are still in scope. As long as you don't terminate the LLDB session, you can switch between the two as necessary. Using Python in LLDB LLDB has full, built-in Python support. If you type script into the LLDB console, it will open a Python REPL: (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()'. >>> This is about as powerful a scripting language as you are likely to come across, and now it's just waiting there for you to use it. We'll see an example of what it can do shortly, once we have seen how to reach into the debug session from within the Python REPL. Accessing program variables from Python The Python REPL has a number of tools with which you can reach into your program's session: >>> mynumber = lldb.frame.FindVariable("i") >>> mynumber.GetValue() '42' So, we have the entire Python scripting language at our disposal, with access to the running program's variables. How we could attach Python scripts to a breakpoint; now we can run spontaneous ad hoc Python sessions as well. Pretty amazing stuff! Switching between LLDB and Python REPL Enter quit to leave the Python REPL. However, just as we saw with the Swift REPL, the Python REPL session is not killed as long as the LLDB session is not terminated: (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()'. >>>a = 1 >>>quit (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()'. >>>a 1 >>> This ability to switch seamlessly between the two without interrupting either LLDB or the REPL makes this a killer feature of LLDB. One liners from LLDB to Python You can also pass a line of Python to the script command and have it executed without entering the REPL: (lldb) script import os (lldb) script os.system("open http://www.apple.com") Once again, we see an enormous amount of scripting power at our disposal. Getting help in Python There is a pretty huge help document available with the following command from within LLDB: (lldb) script help(lldb) Or use the following command for a smaller, more manageable doc set: (lldb) script help(lldb.process) From these documents you have access to more specific topics about Python in LLDB. It has to be said that these docs are going to be of more use to an experienced Python programmer who is integrating Python into his or her workflow. To learn Python from scratch (and you should, it's easy), you're better off checking out more tutorial-orientated resources. Altogether now You can see that from the debug console, you can run LLDB, shell commands, the Swift REPL, and a Python REPL, all without having to kill the running debug session or fire up the Terminal. Once you get comfortable with making use of this, it becomes an indispensable part of the development process. Standalone LLDB Starting with Xcode 8, LLDB can run in a standalone Terminal session. To launch it, type the lldb command: ~ lldb (lldb) This is great for exploring LLDB, but prototyping and debugging an Xcode project is what we are here for, so let's do that next. Running Xcode projects in a standalone LLDB session To debug an Xcode project, we first need the path of the debug build, which we can get by right-clicking on theProductsfolder in the projectnavigator, and selecting Show in Finder: Now add that path as the argument to the lldb command (dragging the build from the Finder into the Terminal window is a pretty quick way to do that): ~ lldb /Users/.../Build/Products/Debug/5623_17_code.app Hit return. This will produce something similar to the following output: (lldb) target create "/Users/.../Build/Products/Debug/5623_17_code.app" Current executable set to '/Users/.../Build/Products/Debug/5623_17_code.app' (x86_64). We haven't got the code running yet, and we'll set a breakpoint before we do: (lldb) breakpoint set --file ViewController.swift --line 31 Breakpoint 2: where = 5623_17_code`_623_17_code.ViewController.viewDidLoad () -> () + 12 at ViewController.swift:31, address = 0x0000000100001bdc Now run the code: (lldb) run Once we hit that breakpoint, something like the following output will be generated: Process 32522 launched: '/Users/.../Build/Products/Debug/5623_17_code.app/Contents/MacOS/5623_17_code' (x86_64) Process 32522 stopped * thread #1: tid = 0x2039e3, 0x0000000100001bdc 5623_17_code`ViewController.viewDidLoad(self=0x0000000100a69620) -> () + 12 at ViewController.swift:31, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 frame #0: 0x0000000100001bdc 5623_17_code`ViewController.viewDidLoad(self=0x0000000100a69620) -> () + 12 at ViewController.swift:31 28 29 override func viewDidLoad() 30 { -> 31 super.viewDidLoad() 32 33 let report = Report() 34 report.title = "Weekly Summary" (lldb) As we can see (there's even a little arrow and a code excerpt), the code has been stopped at line 31, as required. Let's print details of the view controller's viewproperty while we're here: (lldb) p self.view (NSView) $R0 = 0x0000000100d18070 { AppKit.NSResponder = { baseNSObject@0 = { isa = NSView } _nextResponder = 0x0000000100d324f0 } } (lldb) Now we can type c to continue the program's execution;but what happens then is that the terminal's input is no longer going to LLDB, which you will notice by the absence of the (lldb) prompt. It is going to the running program. You need to typecontrol + C to get back to LLDB. To return to using the terminal for input to the program, type c to continue. Using these two commands, you can toggle between LLDB and the running process, should you need to. Differences between standalone and Xcode LLDB There are a few important things to note about running standalone LLDB sessions: An LLDB session that is running in a Terminal window ignores all Xcode breakpoints (and other sessions). A command line session is available all of the time, without necessarily having to halt the program's execution. We can have two (or more) open processes at the same time by launching from different terminal sessions (that isTerminal app windows). This means we can try variations in our code at the same time: We can use different variable values We can set different breakpoints and watchpoints Some of these differences make clear the advantages that a standalone session offers over LLDB running in Xcode. We won't necessarily need these features all the time, but it's a good idea to get comfortable with debugging in a separate app. When you do need those features, you'll be glad you did. Summary In this article, we have had more than a superficial glimpse of Xcode and LLDB's advanced features, though there is more to discover. You have learned the following in this article: Using LLDB's advanced debugging features Making your custom classes debug-printable Running your projects in standalone LLDB sessions Using stop hooks and watchpoints in addition to breakpoints Using shell, Swift, and Python commands in an LLDB session in addition to LLDB's own command set Resources for Article:  Further resources on this subject: Maximizing everyday debugging [article] Debugging Applications with PDB and Log Files [article] Debugging mechanisms in Oracle SQL Developer 2.1 using Pl/SQL [article]
Read more
  • 0
  • 1
  • 10702
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-working-closures
Packt
03 Jan 2017
31 min read
Save for later

Working with Closures

Packt
03 Jan 2017
31 min read
In this article, Jon Hoffman, the author of the book Mastering Swift 3 - Linux, talks about most major programming languages have functionalities similar to what closures offer. Some of these implementations are really hard to use (Objective-C blocks), while others are easy (Java lambdas and C# delegates). I found that the functionality that closures provide is especially useful when developing frameworks. I have also used them extensively when communicating with remote services over a network connection. While blocks in Objective-C are incredibly useful (and I have used them quite a bit), their syntax used to declare a block was absolutely horrible. Luckily, when Apple was developing the Swift language, they made the syntax of closures much easier to use and understand. In this article, we will cover the following topics: An introduction to closures Defining a closure Using a closure Several useful examples of closures How to avoid strong reference cycles with closures (For more resources related to this topic, see here.) An introduction to closures Closures are self-contained blocks of code that can be passed around and used throughout our application. We can think of an Int type as a type that stores an integer, and a String type as a type that stores a string. In this context, a closure can be thought of as a type that contains a block of code. What this means is that we can assign closures to a variable, pass them as arguments to functions, and also return them from functions. Closures have the ability to capture and store references to any variable or constant from the context in which they were defined. This is known as closing over the variables or constants, and the best thing is, for the most part, Swift will handle the memory management for us. The only exception is when we create a strong reference cycle, and we will look at how to resolve this in the Creating strong reference cycles with closures section of this article. Closures in Swift are similar to blocks in Objective-C; however, closures in Swift are a lot easier to use and understand. Let's look at the syntax used to define a closure in Swift: { (parameters) -> return-type in statements } As we can see, the syntax used to create a closure looks very similar to the syntax we use to create functions in Swift, and actually, in Swift, global and nested functions are closures. The biggest difference in the format between closures and functions is the in keyword. The in keyword is used in place of curly brackets to separate the definition of the closure's parameter and return types from the body of the closure. There are many uses for closures, and we will go over a number of them later in this article, but first we need to understand the basics of closures. Let's start by looking at some very basic uses for closures so that we can get a better understanding of what they are, how to define them, and how to use them. Simple closures We will begin by creating a very simple closure that does not accept any arguments and does not return a value. All it does is print Hello World to the console. Let's take a look at the following code: let clos1 = { () -> Void in print("Hello World") } In this example, we create a closure and assign it to the constant clos1. Since there are no parameters defined between the parentheses, this closure will not accept any parameters. Also, the return type is defined as Void; therefore, this closure will not return any value. The body of the closure contains one line that prints Hello World to the console. There are many ways to use closures; in this example, all we want to do is execute it. We execute this closure such as this: clos1() When we execute the closure, we will see that Hello World is printed to the console. At this point, closures may not seem that useful, but as we get further along in this article, we will see how useful and powerful they can be. Let's look at another simple closure example. This closure will accept one string parameter named name, but will still not return a value. Within the body of the closure, we will print out a greeting to the name passed into the closure through the name parameter. Here is the code for this second closure: let clos2 = { (name: String) -> Void in print("Hello (name)") } The big difference between clos2 defined in this example and the previous clos1 closure is that we define a single string parameter between the parentheses. As we can see, we define parameters for closures just like we define parameters for functions. We can execute this closure in the same way in which we executed clos1. The following code shows how this is done: clos2(name: "Jon") This example, when executed, will print the message Hello Jon to the console. Let's look at another way we can use the clos2 closure. Our original definition of closures stated, closures are self-contained blocks of code that can be passed around and used throughout our application code. What this tells us is that we can pass our closure from the context that it was created in to other parts of our code. Let's look at how to pass our clos2 closure into a function. We will define a function that accepts our clos2 closure such as this: func testClosure(handler:(String)->Void) { handler("Dasher") } We define the function just like we would any other function; however, in our parameter list, we define a parameter named handler, and the type defined for the handler parameter is (String)->Void. If we look closely, we can see that the (String)->Void definition of the handler parameter matches the parameter and return types that we defined for clos2 closure. This means that we can pass the clos2 closure into the function. Let's look at how to do this: testClosure(handler: clos2) We call the testClosure() function just like any other function and the closure that is being passed in looks like any other variable. Since the clos2 closure executed in the testClosure() function, we will see the message, Hello Dasher, printed to the console when this code is executed. As we will see a little later in this article, the ability to pass closures to functions is what makes closures so exciting and powerful. As the final piece to the closure puzzle, let's look at how to return a value from a closure. The following example shows this: let clos3 = { (name: String) -> String in return "Hello (name)" } The definition of the clos3 closure looks very similar to how we defined the clos2 closure. The difference is that we changed the Void return type to a String type. Then, in the body of the closure, instead of printing the message to the console, we used the return statement to return the message. We can now execute the clos3 closure just like the previous two closures, or pass the closure to a function like we did with the clos2 closure. The following example shows how to execute clos3 closure: var message = clos3("Buddy") After this line of code is executed, the message variable will contain the Hello Buddy string. The previous three examples of closures demonstrate the format and how to define a typical closure. Those who are familiar with Objective-C can see that the format of closures in Swift is a lot cleaner and easier to use. The syntax for creating closures that we have shown so far in this article is pretty short; however, we can shorten it even more. In this next section, we will look at how to do this. Shorthand syntax for closures In this section, we will look at a couple of ways to shorten the definition of closures. Using the shorthand syntax for closures is really a matter of personal preference. There are a lot of developers that like to make their code as small and compact as possible and they take great pride in doing so. However, at times, this can make code hard to read and understand by other developers. The first shorthand syntax for closures that we are going to look at is one of the most popular and is the syntax we saw when we were using algorithms with arrays. This format is mainly used when we want to send a really small (usually one line) closure to a function, like we did with the algorithms for arrays. Before we look at this shorthand syntax, we need to write a function that will accept a closure as a parameter: func testFunction(num: Int, handler:()->Void) { for _ in 0..< num { handler() } } This function accepts two parameters—the first parameter is an integer named num, and the second parameter is a closure named handler that does not have any parameters and does not return any value. Within the function, we create a for loop that will use the num integer to define how many times it loops. Within the for loop, we call the handler closure that was passed into the function. Now lets create a closure and pass it to the testFunction()such as this: let clos = { () -> Void in print("Hello from standard syntax") } testFunction(num: 5,handler: clos) This code is very easy to read and understand; however, it does take five lines of code. Now, let's look at how to shorten this code by writing the closure inline within the function call: testFunction(num: 5,handler: {print("Hello from Shorthand closure")}) In this example, we created the closure inline within the function call using the same syntax that we used with the algorithms for arrays. The closure is placed in between two curly brackets ({}), which means the code to create our closure is {print("Hello from Shorthand closure")}. When this code is executed, it will print out the message, Hello from Shorthand closure, five times on the screen. Let's look at how to use parameters with this shorthand syntax. We will begin by creating a new function that will accept a closure with a single parameter. We will name this function testFunction2. The following example shows what the new testFunction2 function does: func testFunction2(num: Int, handler:(name: String)->Void) { for _ in 0..< num { handler(name: "Me") } } In testFunction2, we define our closure such as this: (name: String)->Void. This definition means that the closure accepts one parameter and does not return any value. Now, let's see how to use the same shorthand syntax to call this function: testFunction2(num: 5,handler: {print("Hello from ($0)")}) The difference between this closure definition and the previous one is $0. The $0 parameter is shorthand for the first parameter passed into the function. If we execute this code, it prints out the message, Hello from Me, five times. Using the dollar sign ($) followed by a number with inline closures allows us to define the closure without having to create a parameter list in the definition. The number after the dollar sign defines the position of the parameter in the parameter list. Let's examine this format a bit more because we are not limited to only using the dollar sign ($) and number shorthand format with inline closures. This shorthand syntax can also be used to shorten the closure definition by allowing us to leave the parameter names off. The following example demonstrates this: let clos5: (String, String) ->Void = { print("($0) ($1)") } In this example, our closure has two string parameters defined; however, we do not give them names. The parameters are defined such as this: (String, String). We can then access the parameters within the body of the closure using $0 and $1. Also, note that closure definition is after the colon (:), using the same syntax that we use to define a variable type, rather than inside the curly brackets. When we use anonymous arguments, this is how we would define the closure. It will not be valid to define the closure such as this: let clos5b = { (String, String) -> Void in print("($0) ($1)") } In this example, we will receive the Anonymous closure arguments cannot be used inside a closure that has explicit arguments error. We will use the clos5 closure such as this: clos5("Hello","Kara") Since Hello is the first string in the parameter list, it is accessed with $0, and as Kara is the second string in the parameter list, it is accessed with $1. When we execute this code, we will see the message Hello Kara printed to the console. This next example is used when the closure doesn't return any value. Rather than defining the return type as Void, we can use parentheses, as the following example shows: let clos6: () -> () = { print("Howdy") } In this example, we define the closure as () -> (). This tells Swift that the closure does not accept any parameters and also does not return a value. We will execute this closure such as this: clos6() As a personal preference, I am not very fond of this shorthand syntax. I think the code is much easier to ready when the void keyword is used rather than the parentheses. We have one more shorthand closure example to demonstrate before we begin showing some really useful examples of closures. In this last example, we will demonstrate how we can return a value from the closure without the need to include the return keyword. If the entire closure body consists of only a single statement, then we can omit the return keyword, and the results of the statement will be returned. Let's take a look at an example of this: let clos7 = { (first: Int, second: Int) -> Int in first + second } In this example, the closure accepts two parameters of the Int type and will return an Int type. The only statement within the body of the closure adds the first parameter to the second parameter. However, if you notice, we do not include the return keyword before the addition statement. Swift will see that this is a single statement closure and will automatically return the results, just as if we put the return keyword before the addition statement. We do need to make sure the result type of our statement matches the return type of the closure. All of the examples that were shown in the previous two sections were designed to show how to define and use closures. On their own, these examples did not really show off the power of closures and they did not show how incredibly useful closures are. The remainder of this article is written to demonstrate the power and usefulness of closures in Swift. Using closures with Swift's array algorithms Now that we have a better understanding of closures, let's see how we can expand on these algorithms using more advanced closures. In this section, we will primarily be using the map algorithm for consistency purposes; however, we can use the basic ideas demonstrated with any of the algorithms. We will start by defining an array to use: let guests = ["Jon", "Kim", "Kailey", "Kara"] This array contains a list of names and the array is named guests. This array will be used for all the examples in this section, except for the very last ones. Now that we have our guests array, let's add a closure that will print a greeting to each of the names in the guests array: guests.map({ (name: String) -> Void in print("Hello (name)") }) Since the map algorithm applies the closure to each item of the array, this example will print out a greeting for each name within the guests array. After the first section in this article, we should have a pretty good understanding of how this closure works. Using the shorthand syntax that we saw in the last section, we could reduce the preceding example down to the following single line of code: guests.map({print("Hello ($0)")}) This is one of the few times, in my opinion, where the shorthand syntax may be easier to read than the standard syntax. Now, let's say that rather than printing the greeting to the console, we wanted to return a new array that contained the greetings. For this, we would have returned a string type from our closure, as shown in the following example: var messages = guests.map({ (name:String) -> String in return "Welcome (name)" }) When this code is executed, the messages array will contain a greeting to each of the names in the guests array while the guests array will remain unchanged. The preceding examples in this section showed how to add a closure to the map algorithm inline. This is good if we only had one closure that we wanted to use with the map algorithm, but what if we had more than one closure that we wanted to use, or if we wanted to use the closure multiple times or reuse them with different arrays. For this, we could assign the closure to a constant or variable and then pass in the closure, using its constant or variable name, as needed. Let's see how to do this. We will begin by defining two closures. One of the closures will print a greeting for each name in the guests array, and the other closure will print a goodbye message for each name in the guests array: let greetGuest = { (name:String) -> Void in print("Hello guest named (name)") } let sayGoodbye = { (name:String) -> Void in print("Goodbye (name)") } Now that we have two closures, we can use them with the map algorithm as needed. The following code shows how to use these closures interchangeably with the guests array: guests.map(greetGuest) guests.map(sayGoodbye) Whenever we use the greetGuest closure with the guests array, the greetings message is printed to the console, and whenever we use the sayGoodbye closure with the guests array, the goodbye message is printed to the console. If we had another array named guests2, we could use the same closures for that array, as shown in the following example: guests.map(greetGuest) guests2.map(greetGuest) guests.map(sayGoodbye) guests2.map(sayGoodbye) All of the examples in this section so far have either printed a message to the console or returned a new array from the closure. We are not limited to such basic functionality in our closures. For example, we can filter the array within our closure, as shown in the following example: let greetGuest2 = { (name:String) -> Void in if (name.hasPrefix("K")) { print("(name) is on the guest list") } else { print("(name) was not invited") } } In this example, we print out a different message depending on whether the name starts with the letter K or not. As we mentioned earlier in the article, closures have the ability to capture and store references to any variable or constant from the context in which they were defined. Let's look at an example of this. Let's say that we have a function that contains the highest temperature for the last seven days at a given location, and this function accepts a closure as a parameter. This function will execute the closure on the array of temperature. The function can be written such as this: func temperatures(calculate:(Int)->Void) { var tempArray = [72,74,76,68,70,72,66] tempArray.map(calculate) } This function accepts a closure defined as (Int)->Void. We then use the map algorithm to execute this closure for each item of the tempArray array. The key to using a closure correctly in this situation is to understand that the temperatures function does not know or care what goes on inside the calculate closure. Also, be aware that the closure is also unable to update or change the items within the function's context, which means that the closure cannot change any other variable within the temperature's function; however, it can update variables in the context that it was created in. Let's look at the function that we will create the closure in. We will name this function testFunction. Let's take a look at the following code: func testFunction() { var total = 0 var count = 0 let addTemps = { (num: Int) -> Void in total += num count += 1 } temperatures(calculate: addTemps) print("Total: (total)") print("Count: (count)") print("Average: (total/count)") } In this function, we begin by defining two variables named total and count, where both variables are of the Int type. We then create a closure named addTemps that will be used to add all of the temperatures from the temperatures function together. The addTemps closure will also count how many temperatures there are in the array. To do this, the addTemps closure calculates the sum of each item in the array and keeps the total in the total variable that was defined at the beginning of the function. The addTemps closure also keeps track of the number of items in the array by incrementing the count variable for each item. Notice that neither the total nor count variables are defined within the closure; however, we are able to use them within the closure because they were defined in the same context as the closure. We then call the temperatures function and pass it the addTemps closure. Finally, we print the total, count, and average temperature to the console. When the testFunction is executed, we see the following output to the console: Total: 498 Count: 7 Average: 71 As we can see from the output, the addTemps closure is able to update and use items that are defined within the context that it was created in, even when the closure is used in a different context. Now that we have looked at using closures with the array map algorithm, let's look at using closures by themselves. We will also look at the ways we can clean up our code to make it easier to read and use. Changing functionality Closures also give us the ability to change the functionality of classes on the fly. With closures, we are able to write functions and classes whose functionality can change, based on the closure that is passed into it as a parameter. In this section, we will show how to write a function whose functionality can be changed with a closure. Let's begin by defining a class that will be used to demonstrate how to swap out functionality. We will name this class TestClass: class TestClass { typealias getNumClosure = ((Int, Int) -> Int) var numOne = 5 var numTwo = 8 var results = 0 func getNum(handler: getNumClosure) -> Int { results = handler(numOne,numTwo) return results } } We begin this class by defining a type alias for our closure that is namedgetNumClosure. Any closure that is defined as a getNumClosure closure will take two integers and return an integer. Within this closure, we assume that it does something with the integers that we pass in to get the value to return, but it really doesn't have to. To be honest, this class doesn't really care what the closure does as long as it conforms to the getNumClosure type. Next, we define three integers that are named numOne, NumTwo, and results. We also define a method named getNum(). This method accepts a closure that confirms the getNumClosure type as its only parameter. Within the getNum() method, we execute the closure by passing in the numOne and numTwo class variables, and the integer that is returned is put into the results class variable. Now, let's look at several closures that conform to the getNumClosure type that we can use with the getNum() method: var max: TestClass.getNumClosure = { if $0 > $1 { return $0 } else { return $1 } } var min: TestClass.getNumClosure = { if $0 < $1 { return $0 } else { return $1 } } var multiply: TestClass.getNumClosure = { return $0 * $1 } var second: TestClass.getNumClosure = { return $1 } var answer: TestClass.getNumClosure = { var tmp = $0 + $1 return 42 } In this code, we define five closures that conform to the getNumClosure type: max: This returns the maximum value of the two integers that are passed in min: This returns the minimum value of the two integers that are passed in multiply: This multiplies both the values that are passed in and returns the product second: This returns the second parameter that was passed in answer: This returns the answer to life, the universe, and everything In the answer closure, we have an extra line that looks like it does not have a purpose: var tmp = $0 + $1. We do this purposely because the following code is not valid: var answer: TestClass.getNumClosure = { return 42 } This class gives us the error: contextual type for closure argument list expects 2 arguments, which cannot be implicitly ignored error. As we can see by the error, Swift does not think that our closure accepts any parameters unless we use $0 and $1 within the body of the closure. In the closure named second, Swifts assumes that there are two parameters because $1 specifies the second parameter. We can now pass each one of these closures to the getNum method of our TestClass to change the functionality of the function to suit our needs. The following code illustrates this: var myClass = TestClass() myClass.getNum(handler: max) myClass.getNum(handler: min) myClass.getNum(handler: multiply) myClass.getNum(handler: second) myClass.getNum(handler: answer) When this code is run, we will receive the following results for each of the closures: max: results = 8 min: results = 5 multiply: results = 40 second: results = 8 answer: results = 42 The last example we are going to show you in this article is one that is used a lot in frameworks, especially the ones that have a functionality that is designed to be run asynchronously. Selecting a closure based on results In the final example, we will pass two closures to a method, and then depending on some logic, one, or possibly both, of the closures will be executed. Generally, one of the closures is called if the method was successfully executed and the other closure is called if the method failed. Let's start off by creating a class that will contain a method that will accept two closures and then execute one of the closures based on the defined logic. We will name this class TestClass. Here is the code for the TestClass class: class TestClass { typealias ResultsClosure = ((String) -> Void) func isGreater(numOne: Int, numTwo:Int, successHandler: ResultsClosure, failureHandler: ResultsClosure) { if numOne > numTwo { successHandler("(numOne) is greater than (numTwo)") } else { failureHandler("(numOne) is not greater than (numTwo)") } } } We begin this class by creating a type alias that defines the closure that we will use for both the successful and failure closures. We will name this type alias ResultsClosure. This example also illustrates why to use a type alias, rather than retyping the closure definition. It saves us a lot of typing and also prevents us from making mistakes. In this example, if we did not use a type alias, we would need to retype the closure definition four times, and if we needed to change the closure definition, we would need to change it in four spots. With the type alias, we only need to type the closure definition once and then use the alias throughout the remaining code. We then create a method named isGreater that takes two integers as the first two parameters and then two closures as the next two parameters. The first closure is named successHandler, and the second closure is named failureHandler. Within the isGreater method, we check whether the first integer parameter is greater than the second one. If the first integer is greater, the successHandler closure is executed; otherwise, the failureHandler closure is executed. Now, let's create two of our closures. The code for these two closures is as follows: var success: TestClass. ResultsClosure = { print("Success: ($0)") } var failure: TestClass. ResultsClosure = { print("Failure: ($0)") } Note that both closures are defined as the TestClass.ResultsClosure type. In each closure, we simply print a message to the console to let us know which closure was executed. Normally, we would put some functionality in the closure. We will then call the method with both the closures such as this: var test = TestClass() test.isGreater(numOne: 8, numTwo: 6, successHandler:success, failureHandler:failure) Note that in the method call, we are sending both the success closure and the failure closure. In this example, we will see the message, Success: 8 is greater than 6. If we reversed the numbers, we would see the message, Failure: 6 is not greater than 8. This use case is really good when we call asynchronous methods, such as loading data from a web service. If the web service call was successful, the success closure is called; otherwise, the failure closure is called. One big advantage of using closures such as this is that the UI does not freeze while we wait for the web service call to complete. As an example, try to retrieve data from a web service such as this: var data = myWebClass.myWebServiceCall(someParameter) Our UI would freeze while we wait for the response to come back, or we would have to make the call in a separate thread so that the UI would not hang. With closures, we pass the closures to the networking framework and rely on the framework to execute the appropriate closure when it is done. This does rely on the framework to implement concurrency correctly to make the calls asynchronously, but a decent framework should handle that for us. Creating strong reference cycles with closures Earlier in this article, we said, the best thing is, for the most part, Swift will handle the memory management for us. The for the most part section of the quote means that if everything is written in a standard way, Swift will handle the memory management of the closures for us. However, there are times where memory management fails us. Memory management will work correctly for all of the examples that we have seen in this article so far. It is possible to create a strong reference cycle that prevents Swift's memory management from working correctly. Let's look at what happens if we create a strong reference cycle with closures. A strong reference cycle may happen if we assign a closure to a property of a class instance and within that closure, we capture the instance of the class. This capture occurs because we access a property of that particular instance using self, such as self.someProperty, or we assign self to a variable or constant, such as let c = self. By capturing a property of the instance, we are actually capturing the instance itself, thereby creating a strong reference cycle where the memory manager will not know when to release the instance. As a result, the memory will not be freed correctly. Let's begin by creating a class that has a closure and an instance of the String type as its two properties. We will also create a type alias for the closure type in this class and define a deinit() method that prints a message to the console. The deinit() method is called when the class gets released and the memory is freed. We will know when the class gets released when the message from the deinit() method is printed to the console. This class will be named TestClassOne. Let's take a look at the following code: class TestClassOne { typealias nameClosure = (() -> String) var name = "Jon" lazy var myClosure: nameClosure = { return self.name } deinit { print("TestClassOne deinitialized") } } Now, let's create a second class that will contain a method that accepts a closure that is of the nameClosure type that was defined in the TestClassOne class. This class will also have a deinit() method, so we can also see when it gets released. We will name this class TestClassTwo. Let's take a look at the following code: class TestClassTwo { func closureExample(handler: TestClassOne.nameClosure) { print(handler()) } deinit { print("TestClassTwo deinitialized") } } Now, let's see this code in action by creating instances of each class and then trying to manually release the instance by setting them to nil: var testClassOne: TestClassOne? = TestClassOne() var testClassTwo: TestClassTwo? = TestClassTwo() testClassTwo?.closureExample(testClassOne!.myClosure) testClassOne = nil print("testClassOne is gone") testClassTwo = nil print("testClassTwo is gone") What we do in this code is create two optionals that may contain an instance of our two test classes or nil. We need to create these variables as optionals because we will be setting them to nil later in the code so that we can see whether the instances are released properly. We then call the closureExample() method of the TestClassTwo instance and pass it the myClosure property from the TestClassOne instance. We now try to release the TestClassOne and TestClassTwo instances by setting them to nil. Keep in mind that when an instance of a class is released, it attempts to call the deinit() method of the class, if it exists. In our case, both classes have a deinit() method that prints a message to the console, so we know when the instances are actually released. If we run this project, we will see the following messages printed to the console: testClassOne is gone TestClassTwo deinitialized testClassTwo is gone As we can see, we do attempt to release the TestClassOne instances, but the deinit() method of the class is never called, indicating that it was not actually released; however, the TestClassTwo instance was properly released because the deinit() method of that class was called. To see how this is supposed to work without the strong reference cycle, change the myClosure closure to return a string type that is defined within the closure itself, as shown in the following code: lazy var myClosure: nameClosure = { return "Just Me" } Now, if we run the project, we should see the following output: TestClassOne deinitialized testClassOne is gone TestClassTwo deinitialized testClassTwo is gone This shows that the deinit() methods from both the TestClassOne and TestClassTwo instances were properly called, indicating that they were both released properly. In the first example, we capture an instance of the TestClassOne class within the closure because we accessed a property of the TestClassOne class using self.name. This created a strong reference from the closure to the instance of the TestClassOne class, preventing memory management from releasing the instance. Swift does provide a very easy and elegant way to resolve strong reference cycles in closures. We simply need to tell Swift not to create a strong reference by creating a capture list. A capture list defines the rules to use when capturing reference types within a closure. We can declare each reference to be a weak or un owned reference rather than a strong reference. A weak keyword is used when there is the possibility that the reference will become nil during its lifetime; therefore, the type must be an optional. The unowned keyword is used when there is not a possibility of the reference becoming nil. We define the capture list by pairing the weak or unowned keywords with a reference to a class instance. These pairings are written within square brackets ([ ]). Therefore, if we update the myClosure closure and define an unowned reference to self, we should eliminate the strong reference cycle. The following code shows what the new myClosure closure will look similar to: lazy var myClosure: nameClosure = { [unowned self] in return self.name } Notice the new line—[unowned self] in. This line says that we do not want to create a strong reference to the instance of self. If we run the project now, we should see the following output: TestClassOne deinitialized testClassOne is gone TestClassTwo deinitialized testClassTwo is gone This shows that both the TestClassOne and TestClassTwo instances were properly released. Summary In this article, we saw that we can define a closure just like we can define an Int or String type. We can assign closures to a variable, pass them as an argument to functions, and also return them from functions. Closures capture a store references to any constants or variables from the context in which the closure was defined. We have to be careful with this functionality to make sure that we do not create a strong reference cycle, which would lead to memory leaks in our applications. Swift closures are very similar to blocks in Objective-C, but they have a much cleaner and eloquent syntax. This makes them a lot easier to use and understand. Having a good understanding of closures is vital to mastering the Swift programming language and will make it easier to develop great applications that are easy to maintain. They are also essential for creating first class frameworks that are both easy to use and maintain. The three use cases that we saw in this article are by no means the only three useful uses for closures. I can promise you that the more you use closures in Swift, the more uses you will find for them. Closures are definitely one of the most powerful and useful features of the Swift language, and Apple did a great job by implementing them in the language. Resources for Article: Further resources on this subject: Revisiting Linux Network Basics [article] An Introduction to the Terminal [article] Veil-Evasion [article]
Read more
  • 0
  • 0
  • 2884

article-image-digging-deeper
Packt
03 Jan 2017
6 min read
Save for later

Digging Deeper

Packt
03 Jan 2017
6 min read
In the article by, Craig Clayton, author of the book, iOS 10 Programming for Beginners, we went over the basics of Swift to get you warmed up. Now, we will dig deeper and learn some more programming concepts. These concepts will build on what you have already learned. In this article, we will cover the following topics: Ranges Control flow (For more resources related to this topic, see here.) Creating a Playground project Please launch Xcode and click on Get started with a playground. The options screen for creating a new playground screen will appear: Please name your new Playground SwiftDiggingDeeper and make sure that your Platform is set to iOS. Now, let's delete everything inside of your file and toggle on the Debug panel using either the toggle button or Cmd + Shift + Y. Your screen should look like mine: Ranges These generic data types represent a sequence of numbers. Let's look at the image below to understand: Closed range Notice that, in the image above, we have numbers ranging from 10 to 20. Rather than having to write each value, we can use ranges to represent all of these numbers in shorthand form. In order to do this, let's remove all of the numbers in the image except 10 and 20. Now that we have removed those numbers, we need a way to tell Swift that we want to include all of the numbers that we just deleted. This is where the range operator (…) comes into play. So, in Playgrounds, let's create a constant called range and set it equal to 10...20: let range = 10...20 The range that we just entered says that we want the numbers between 10 and 20 as well as both 10 and 20 themselves. This type of range is known as a closed range. We also have what is called a half closed range. Half closed range Let's make another constant called half closed range and set it equal to 10..<20: let halfClosedRange = 10..<20 A half closed range is the same as a closed range except that the end value will not be included. In this example, that means that 10 through 19 will be included and 20 will be excluded. At this point, you will notice that your results panel just shows you CountableClosedRange(10...20) and CountableRange(10..<20). We cannot see all the numbers within the range. In order to see all the numbers, we need to use a loop. Control flow In Swift, we use a variety of control statements. For-In Loop One of the most common control statements is a for-in loop. A for-in loop allows you to iterate over each element in a sequence. Let's see what a for-loop looks like: for <value> in <sequence> { // Code here } So, we start our for-in loop with for, which is followed by <value>. This is actually a local constant (only the for-in loop can access it) and can be any name you like. Typically, you will want to give this value an expressive name. Next, we have in, which is followed by <sequence>. This is where we want to give it our sequence of numbers. Let's write the following into Playgrounds: for value in range { print("closed range - (value)") } Notice that, in our debug panel, we see all of the numbers we wanted in our range: Let's do the same for our variable halfClosedRange by adding the following: for index in halfClosedRange { print("half closed range - (index)") } In our debug panel, we see that we get the numbers 10 through 19. One thing to note is that these two for-in loops have different variables. In the first loop, we used value, and in the second one, we used index. You can make these whatever you choose. In addition, in the two examples, we used constants, but we could actually just use the ranges within the loop. Please add the following: for index in 0...3 { print("range inside - (index)") } Now, you will see 0 to 3 print inside the debug panel: What if you wanted numbers to go in reverse order? Let's input the following for-in loop: for index in (10...20).reversed() { print("reversed range - (index)") } We now have the numbers in descending order in our debug panel. When we add ranges into a for-in loop, we have to wrap our range inside parentheses so that Swift recognizes that our period before reversed() is not a decimal. The while Loop A while loop executes a Boolean expression at the start of the loop, and the set of statements run until a condition becomes false. It is important to note that while loops can be executed zero or more times. Here is the basic syntax of a while loop: while <condition> { // statement } Let's write a while loop in Playgrounds and see how it works. Please add the following: var y = 0 while y < 50 { y += 5 print("y:(y)") } So, this loop starts with a variable that begins at zero. Before the while loop executes, it checks to see if y is less than 50; and, if so, it continues into the loop. By using the += operator, we increment y by 5 each time. Our while loop will continue to do this until y is no longer less than 50. Now, let's add the same while loop after the one we created and see what happens: while y < 50 { y += 5 print("y:(y)") } You will notice that the second while loop never runs. This may not seem like it is important until we look at our next type of loop. The repeat-while loop A repeat-while loop is pretty similar to a while loop in that it continues to execute the set of statements until a condition becomes false. The main difference is that the repeat-while loop does not evaluate its Boolean condition until the end of the loop. Here is the basic syntax of a repeat-while loop: repeat { // statement } <condition> Let's write a repeat-while loop in Playgrounds and see how it works. Type the following into Playgrounds: var x = 0 repeat { x += 5 print("x:(x)") } while x < 100 print("repeat completed x: (x)") So, our repeat-while loop executes first and increments x by 5, and afterwards (as opposed to checking the condition before as with a while loop), it checks to see if x is less than 100. This means that our repeat-while loop will continue until the condition hits 100. But here is where it gets interesting. Let's add another repeat-while loop after the one we just created: repeat { x += 5 print("x:(x)") } while x < 100 print("repeat completed again x: (x)") This time, the repeat…while loop incremented to 105. This happens because the Boolean expression does not get evaluated until after it is incremented by 5. Knowing this behavior will help you pick the right loop for your situation. So far, we have looked at three loops: the for-in loop, the while loop, and the repeat-while loop. We will use the for-in loop again, but first we need to talk about collections. Summary The article summarizes Ranges and control flow using Xcode 8. Resources for Article: Further resources on this subject: Tools inTypeScript [article] Design with Spring AOP [article] Thinking Functionally [article]
Read more
  • 0
  • 0
  • 1456

article-image-uitableview-touch
Packt
14 Dec 2016
24 min read
Save for later

UITableView Touch Up

Packt
14 Dec 2016
24 min read
 In this article by Donny Wals, from the book Mastering iOS 10 Programming, we will go through UITableView touch up. Chances are that you have built a simple app before. If you have, there's a good probability that you have used UITableView. The UITableView is a core component in many applications. Virtually all applications that display a list of some sort make use of UITableView. Because UITableView is such an important component in the world of iOS I want you to dive in to it straight away. You may or may not have looked at UITableView before but that's okay. You'll be up to speed in no time and you'll learn how this component achieves that smooth 60 frames per seconds (fps) scrolling that users know and love. If your app can maintain a steady 60,fps your app will feel more responsive and scrolling will feel perfectly smooth to users which is exactly what you want. We'll also cover new UITableView features that make it even easier to optimize your table views. In addition to covering the basics of UITableView, you'll learn how to make use of Contacts.framework to build an application that shows a list of your users' contacts. This is similar to what the native Contacts app does on iOS. The contacts in the UITableView component will be rendered in a custom cell. You will learn how to create such a cell, using Auto Layout. Auto Layout is a technique that will be covered throughout this book because it's an important part of every iOS developer's tool belt. If you haven't used Auto Layout before, that's okay. This article will cover a few basics, and the layout is relatively simple, so you can gradually get used to it as we go. To sum it all up, this article covers: Configuring and displaying UITableView Fetching a user's contacts through Contacts.framework UITableView delegate and data source (For more resources related to this topic, see here.) Setting up the User Interface (UI) Every time you start a new project in Xcode, you have to pick a template for your application. These templates will provide you with a bit of boiler plate code or sometimes they will configure a very basic layout for you. Throughout this book, the starting point will always be the Single View Application template. This template will provide you with a bare minimum of boilerplate code. This will enable you to start from scratch every time, and it will boost your knowledge of how the Xcode-provided templates work internally. In this article, you'll create an app called HelloContacts. This is the app that will render your user's contacts in a UITableView. Create a new project by selecting File | New | Project. Select the Single View template, give your project a name (HelloContacts), and make sure you select Swift as the language for your project. You can uncheck all CoreData- and testing-related checkboxes; they aren't of interest right now. Your configuration should resemble the following screenshot: Once you have your app configured, open the Main.storyboard file. This is where you will create your UITableView and give it a layout. Storyboards are a great way for you to work on an app and have all of the screens in your app visualized at once. If you have worked with UITableView before, you may have used UITableViewController. This is a subclass of UIViewController that has a UITableView set as its view. It also abstracts away some of the more complex concepts of UITableView that we are interested in. So, for now, you will be working with the UIViewController subclass that holds UITableView and is configured manually. On the right hand side of the window, you'll find the Object Library. The Object Library is opened by clicking on the circular icon that contains a square in the bottom half of the sidebar.  In the Object Library, look for a UITableView. If you start typing the name of what you're looking for in the search box, it should automatically be filtered out for you. After you locate it, drag it into your app's view. Then, with UITableView selected, drag the white squares to all corners of the screen so that it covers your entire viewport. If you go to the dynamic viewport inspector at the bottom of the screen by clicking on the current device name as shown in the following screenshot and select a larger device such as an iPad or a smaller device such as the iPhone SE, you will notice that UITableView doesn't cover the viewport as nicely. On smaller screens, the UITableView will be larger than the viewport. On larger screens, UITableView simply doesn't stretch: This is why we will use Auto Layout. Auto Layout enables you to create layouts that will adapt to different viewports to make sure it looks good on all of the devices that are currently out there. For UITableView, you can pin the edges of the table to the edges of the superview, which is the view controller's main view. This will make sure the table stretches or shrinks to fill the screen at all times. Auto Layout uses constraints to describe layouts. UITableView should get some constraints that describe its relation to the edges of your view controller. The easiest way to add these constraints is to let Xcode handle it for you. To do this, switch the dynamic viewport inspector back to the view you initially selected. First, ensure UITableView properly covers the entire viewport, and then click on the Resolve Auto Layout Issues button at the bottom-right corner of the screen and select Reset to Suggested Constraints: This button automatically adds the required constraints for you. The added constraints will ensure that each edge of UITableView sticks to the corresponding edge of its superview. You can manually inspect these constraints in the Document Outline on the left-hand side of the Interface Builder window. Make sure that everything works by changing the preview device in the dynamic viewport inspector again. You should verify that no matter which device you choose now, the table will stretch or shrink to cover the entire view at all times. Now that you have set up your project with UITableView added to the interface and the constraints have been added, it's time to start writing some code. The next step is to use Contacts.framework to fetch some contacts from your user's address book. Fetching a user's contacts In the introduction for this article, it was mentioned that we would use Contacts.framework to fetch a user's contacts list and display it in UITableView. Before we get started, we need to be sure we have access to the user's address book. In iOS 10, privacy is a bit more restrictive than it was earlier. If you want to have access to a user's contacts, you need to specify this in your application's Info.plist file. If you fail to specify the correct keys for the data your application uses, it will crash without warning. So, before attempting to load your user's contacts, you should take care of adding the correct key to Info.plist. To add the key, open Info.plist from the list of files in the Project Navigator on the left and hover over Information Property List at the top of the file. A plus icon should appear, which will add an empty key with a search box when you click on it. If you start typing Privacy – contacts, Xcode will filter options until there is just one left, that is, the key for contact access. In the value column, fill in a short description about what you are going to use this access for. In our app, something like read contacts to display them in a list should be sufficient. Whenever you need access to photos, Bluetooth, camera, microphone, and more, make sure you check whether your app needs to specify this in its Info.plist. If you fail to specify a key that's required, your app will crash and will not make it past Apple's review process. Now that you have configured your app to specify that it wants to be able to access contact data, let's get down to writing some code. Before reading the contacts, you'll need to make sure the user has given permission to access contacts. You'll have to check this first, after which the code should either fetch contacts or it should ask the user for permission to access the contacts. Add the following code to ViewController.swift. After doing so, we'll go over what this code does and how it works: class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let store = CNContactStore() if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined { store.requestAccess(for: .contacts, completionHandler: {[weak self] authorized, error in if authorized { self?.retrieveContacts(fromStore: store) } }) } else if CNContactStore.authorizationStatus(for: .contacts) == .authorized { retrieveContacts(fromStore: store) } } func retrieveContacts(fromStore store: CNContactStore) { let keysToFetch = [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactImageDataAvailableKey as CNKeyDescriptor] let containerId = store.defaultContainerIdentifier() let predicate = CNContact.predicateForContactsInContainer(withIdentifier: containerId) let contacts = try! store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) print(contacts) } } In the viewDidLoad method, we will get an instance of CNContactStore. This is the object that will access the user's contacts database to fetch the results you're looking for. Before you can access the contacts, you need to make sure that the user has given your app permission to do so. First, check whether the current authorizationStatus is equal to .notDetermined. This means that we haven't asked permission yet and it's a great time to do so. When asking for permission, we pass a completionHandler. This handler is called a closure. It's basically a function without a name that gets called when the user has responded to the permission request. If your app is properly authorized after asking permission, the retrieveContacts method is called to actually perform the retrieval. If the app already had permission, we'll call retrieveContacts right away. Completion handlers are found throughout the Foundation and UIKit frameworks. You typically pass them to methods that perform a task that could take a while and is performed parallel to the rest of your application so the user interface can continue running without waiting for the result. A simplified implementation of such a function could look like this: func doSomething(completionHandler: Int -> Void) { // perform some actions
 var resultingValue = theResultOfSomeAction() completionHandler(resultingValue)
 } You'll notice that actually calling completionHandler looks identical to calling an ordinary function or method. The idea of such a completion handler is that we can specify a block of code, a closure, that is supposed to be executed at a later time. For example, after performing a task that is potentially slow. You'll find plenty of other examples of callback handlers and closures throughout this book as it's a common pattern in programming. The retrieveContacts method in ViewController.swift is responsible for actually fetching the contacts and is called with a parameter named store. It's set up like this so we don't have to create a new store instance since we already created one in viewDidLoad. When fetching a list of contacts, you use a predicate. We won't go into too much detail on predicates and how they work yet. The main goal of the predicate is to establish a condition to filter the contacts database on. In addition to a predicate, you also provide a list of keys your code wants to fetch. These keys represent properties that a contact object can have. They represent data, such as e-mail, phone numbers, names, and more. In this example, you only need to fetch a contact's given name, family name, and contact image. To make sure the contact image is available at all, there's a key request for that as well. When everything is configured, a call is made to unifiedContacts(matching:, keysToFetch:). This method call can throw an error, and since we're currently not interested in the error, try! is used to tell the compiler that we want to pretend the call can't fail and if it does, the application should crash. When you're building your own app, you might want to wrap this call in do {} catch {} block to make sure that your app doesn't crash if errors occur. If you run your app now, you'll see that you're immediately asked for permission to access contacts. If you allow this, you will see a list of contacts printed in the console. Next, let's display some content information in the contacts table! Creating a custom UITableViewCell for our contacts To display contacts in your UITableView, you will need to set up a few more things. First and foremost, you'll need to create a UITableViewCell that displays contact information. To do this, you'll create a custom UITableViewCell by creating a subclass. The design for this cell will be created in Interface Builder, so you're going to add @IBOutlets in your UITableViewCell subclass. These @IBOutlets are the connection points between Interface Builder and your code. Designing the contact cell The first thing you need to do is drag a UITableViewCell out from the Object Library and drop it on top of UITableView. This will add the cell as a prototype. Next, drag out UILabel and a UIImageView from the Object Library to the newly added UITableViewCell, and arrange them as they are arranged in the following figure. After you've done this, select both UILabel and UIImage and use the Reset to Suggested Constraints option you used earlier to lay out UITableView. If you have both the views selected, you should also be able to see the blue lines that are visible in following screenshot: These blue lines represent the constraints that were added to lay out your label and image. You can see a constraint that offsets the label from the left side of the cell. However, there is also a constraint that spaces the label and the image. The horizontal line through the middle of the cell is a constraint that vertically centers the label and image inside of the cell. You can inspect these constraints in detail in the Document Outline on the right. Now that our cell is designed, it's time to create a custom subclass for it and hook up @IBOutlets. Creating the cell subclass To get started, create a new file (File | New | File…) and select a Cocoa Touch file. Name the file ContactTableViewCell and make sure it subclasses UITableViewCell, as shown in the following screenshot: When you open the newly created file, you'll see two methods already added to the template for ContactTableViewCell.swift: awakeFromNib and setSelected(_:animated:). The awakeFromNib method is called the very first time this cell is created; you can use this method to do some initial setup that's required to be executed only once for your cell. The other method is used to customize your cell when a user taps on it. You could, for instance, change the background color or text color or even perform an animation. For now, you can delete both of these methods and replace the contents of this class with the following code: @IBOutlet var nameLabel: UILabel! @IBOutlet var contactImage: UIImageView! The preceding code should be the entire body of the ContactTableViewCell class. It creates two @IBOutlets that will allow you to connect your prototype cell with so that you can use them in your code to configure the contact's name and image later. In the Main.storyboard file, you should select your cell, and in the Identity Inspector on the right, set its class to ContactTableViewCell (as shown in the following screenshot). This will make sure that Interface Builder knows which class it should use for this cell, and it will make the @IBOutlets available to Interface Builder. Now that our cell has the correct class, select the label that will hold the contact's name in your prototype cell and click on Connections Inspector. Then, drag a new referencing outlet from the Connections Inspector to your cell and select nameLabel to connect the UILabel in the prototype cell to @IBOutlet in the code (refer to the following screenshot). Perform the same steps for UIImageView and select the contactImage option instead of nameLabel. The last thing we need to do is provide a reuse identifier for our cell. Click on Attributes Inspector after selecting your cell. In Attributes Inspector, you will find an input field for the Identifier. Set this input field to ContactTableViewCell. The reuse identifier is the identifier that is used to inform the UITableView about the cell it should retrieve when it needs to be created. Since the custom UITableViewCell is all set up now, we need to make sure UITableView is aware of the fact that our ViewController class will be providing it with the required data and cells. Displaying the list of contacts When you're implementing UITableView, it's good to be aware of the fact that you're actually working with a fairly complex component. This is why we didn't pick a UITableViewController at the beginning of this article. UITableViewController does a pretty good job of hiding the complexities of UITableView from thedevelopers. The point of this article isn't just to display a list of contacts; it's purpose is also to introduce some advanced concepts about a construct that you might have seen before, but never have been aware of. Protocols and delegation  Throughout the iOS SDK and the Foundation framework the delegate design pattern is used. Delegation provides a way for objects to have some other object handle tasks on their behalf. This allows great decoupling of certain tasks and provides a powerful way to allow communication between objects. The following image visualizes the delegation pattern for a UITableView component and its UITableViewDataSource: The UITableView uses two objects that help in the process of rendering a list. One is called the delegate, the other is called the data source. When you use a UITableView, you need to explicitly  configure the data source and delegate properties. At runtime, the UITableView will call methods on its delegate and data source in order to obtain information about cells, handle interactions and more. If you look at the documentation for the UITableView delegate property it will tell you that its type is UITableViewDelegate?. This means that the delegate's type is UITableViewDelegate. The question mark indicates that this value could be nil; we call this an Optional. The reason for the delegate to be Optional is that it might not ever be set at all. Diving deeper into what this UITableViewDelegate is exactly, you'll learn that it's actually a protocol and not a class or struct. A protocol provides a set of properties and/or methods that any object that conforms to (or adopts) this protocol must implement. Sometimes a protocol will provide optional methods, such as UITableViewDelegate does. If this is the case, we can choose which delegate methods we want to implement and which method we want to omit. Other protocols mandatory methods. The UITableViewDataSource has a couple of mandatory methods to ensure that a data source is able to provide UITableView with the minimum amount of information needed in order to render the cells you want to display. If you've never heard of delegation and protocols before, you might feel like this is all a bit foreign and complex. That's okay; throughout this book you'll gain a deeper understanding of protocols and how they work. In particular, the next section, where we'll cover swift and protocol-oriented programming, should provide you with a very thorough overview of what protocols are and how they work. For now, it's important to be aware that a UITableView always asks another object for data through the UITableViewDataSource protocol and their interactions are handled though the UITableViewDelegate. If you were to look at what UITableView does when it's rendering contents it could be dissected like this: UITableView needs to reload the data. UITableView checks whether it has a delegate; it asks the dataSource for the number of sections in this table. Once the delegate responds with the number of sections, the table view will figure out how many cells are required for each section. This is done by asking the dataSource for the number of cells in each section. Now that the cell knows the amount of content it needs to render, it will ask its data source for the cells that it should display. Once the data source provides the required cells based on the number of contacts, the UITableView will request display the cells one by one. This process is a good example of how UITableView uses other objects to provide data on its behalf. Now that you know how the delegation works for UITableView, it's about time you start implementing this in your own app. Conforming to the UITableViewDataSource and UITableViewDelegate protocol In order to specify the UITableView's delegate and data source, the first thing you need to do is to create an @IBOutlet for your UITableView and connect it to ViewController.swift. Add the following line to your ViewController, above the viewDidLoad method. @IBOutlet var tableView: UITableView! Now, using the same technique as before when designing UITableViewCell, select the UITableView in your Main.storyboard file and use the Connections Inspector to drag a new outlet reference to the UITableView. Make sure you select the tableView property and that's it. You've now hooked up your UITableView to the ViewController code. To make the ViewController code both the data source and the delegate for UITableView, it will have to conform to the UITableViewDataSource and UITableViewDelegate protocols. To do this, you have to add the protocols you want to conform to your class definition. The protocols are added, separated by commas, after the superclass. When you add the protocols to the ViewController, it should look like this: class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { // class body
 } Once you have done this, you will have an error in your code. That's because even though your class definition claim to implement these protocols, you haven't actually implemented the required functionality yet. If you look at the errors Xcode is giving you, it becomes clear that there's two methods you must implement. These methods are tableView(_:numberOfRowsInSection:) and tableView(_:cellForRowAt:). So let's fix the errors by adjusting our code a little bit in order to conform to the protocols. This is also a great time to refactor the contacts fetching a little bit. You'll want to access the contacts in multiple places so that the list should become an instance variable. Also, if you're going to create cells anyway, you might as well configure them to display the correct information right away. To do so, perform the following code: var contacts = [CNContact]() // … viewDidLoad // … retrieveContacts func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return contacts.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell") as! ContactTableViewCell let contact = contacts[indexPath.row] cell.nameLabel.text = "(contact.givenName) (contact.familyName)" if let imageData = contact.imageData where contact.imageDataAvailable { cell.contactImage.image = UIImage(data: imageData) } return cell }  The preceding code is what's needed to conform to the UITableViewDataSource protocol. Right below the @IBOutlet of your UITableView, a variable is declared that will hold the list of contacts. The following code snippet was also added to the ViewController: func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return contacts.count }  This method is called by the UITableView to determine how many cells it will have to render. This method just returns the total number of contacts that's in the contacts list. You'll notice that there's a section parameter passed to this method. That's because a UITableView can contain multiple sections. The contacts list only has a single section; if you have data that contains multiple sections, you should also implement the numberOfSections(in:) method. The second method we added was: func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell") as! ContactTableViewCell let contact = contacts[indexPath.row] cell.nameLabel.text = "(contact.givenName) (contact.familyName)" if let imageData = contact.imageData where contact.imageDataAvailable { cell.contactImage.image = UIImage(data: imageData) } return cell }  This method is used to get an appropriate cell for our UITableView to display. This is done by calling dequeueReusableCell(withIdentifier:) on the UITableView instance that's passed to this method. This is because UITableView can reuse cells that are currently off screen. This is a performance optimization that allows UITableView to display vast amounts of data without becoming slow or consuming big chunks of memory. The return type of dequeueReusableCell(withIdentifier:) is UITableViewCell, and our custom outlets are not available on this class. This is why we force cast the result from that method to ContactTableViewCell. Force casting to your own subclass will make sure that the rest of your code has access to your nameLabel and contactImage. Casting objects will convert an object from one class or struct to another. This usually only works correctly when you're casting from a superclass to a subclass like we're doing in our example. Casting can fail, so force casting is dangerous and should only be done if you want your app to crash or consider it a programming error in case the cast fails. We also grab a contact from the contacts array that corresponds to the current row of indexPath. This contact is then used to assign all the correct values to the cell and then the cell is returned. This is all the setup needed to make your UITableView display the cells. Yet, if we build and run our app, it doesn't work! A few more changes will have to be made for it to do so. Currently, the retrieveContacts method does fetch the contacts for your user, but it doesn't update the contacts variable in ViewController. Also, the UITableView won't know that it needs to reload its data unless it's told to. Currently, the last few lines of retrieveContacts will look like this: let contacts = try! store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) print(contacts) Let's update these lines to the following code: contacts = try! store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) tableView.reloadData() Now, the result of fetching contacts is assigned to the instance variable that's declared at the top of your ViewController. After doing that, we tell the tableView to reload its data, so it will go through the delegate methods that provide the cell count and cells again. Lastly, the UITableView doesn't know that the ViewControler instance will act as both the dataSource and the delegate. So, you should update the viewDidLoad method to assign the UITableView's delegate and dataSource properties. Add the following lines to the end of the viewDidLoad method: tableView.dataSource = self tableView.delegate = self If you build and run it now, your app works! If you're running it in the simulator or you haven't assigned images to your contacts, you won't see any images. If you'd like to assign some images to the contacts in the simulator, you can drag your images into the simulator to add them to the simulator's photo library. From there, you can add pictures to contacts just as you would on a real device. However, if you have assigned images to some of your contacts you will see their images appear. You can now scroll through all of your contacts, but there seems to be an issue. When you're scrolling down your contacts list, you might suddenly see somebody else's photo next to the name of a contact that has no picture! This is actually a performance optimization. Summary Your contacts app is complete for now. We've already covered a lot of ground on the way to iOS mastery. We started by creating a UIViewController that contains a UITableView. We used Auto Layout to pin the edges of the UITableView to the edges of main view of ViewController. We also explored the Contacts.framework and understood how to set up our app so it can access the user's contact data. Resources for Article:  Further resources on this subject: Offloading work from the UI Thread on Android [article] Why we need Design Patterns? [article] Planning and Structuring Your Test-Driven iOS App [article]
Read more
  • 0
  • 0
  • 2945

article-image-planning-and-structuring-your-test-driven-ios-app
Packt
11 Nov 2016
13 min read
Save for later

Planning and Structuring Your Test-Driven iOS App

Packt
11 Nov 2016
13 min read
In this article written by Dr. Dominik Hauser, author of the book Test–Driven iOS Development with Swift 3.0, you will learn that when starting TDD, writing unit tests would be easy for most people. The hard part is to transfer the knowledge from writing the test to driving the development. What can be assumed? What should be done before one writes the first test? What should be tested to end up with a complete app? (For more resources related to this topic, see here.) As a developer, you are used to thinking in terms of code. When you see a feature on the requirement list for an app, your brain already starts to layout the code for this feature. And for recurring problems in iOS development (such as building table views), you most probably have already developed your own best practices. In TDD, you should not think about the code while working on the test. The tests have to describe what the unit under test should do and not how it should do it. It should be possible to change the implementation without breaking the tests. To practice this approach of development, we will develop a simple to-do list app in the remainder of this book. It is, on purpose, a boring and easy app. We want to concentrate on the TDD workflow, not complex implementations. An interesting app would distract from what is important in this book—how to do TDD. This article introduces the app that we are going to build, and it shows the views that the finished app will have. We will cover the following topics in this article: The task list view The task detail view The task input view The structure of an app Getting started with Xcode Setting up useful Xcode behaviors for testing The task list view When starting the app, the user sees a list of to-do items. The items in the list consist of a title, an optional location, and the due date. New items can be added to the list by an add (+) button, which is shown in the navigation bar of the view. The task list view will look like this: User stories: As a user, I want to see the list of to-do items when I open the app As a user, I want to add to-do items to the list In a to-do list app, the user will obviously need to be able to check items when they are finished. The checked items are shown below the unchecked items, and it is possible to uncheck them again. The app uses the delete button in the UI of UITableView to check and uncheck items. Checked items will be put at the end of the list in a section with the Finished header. The user can also delete all the items from the list by tapping the trash button. The UI for the to-do item list will look like this: User stories: As a user, I want to check a to-do item to mark it as finished As a user, I want to see all the checked items below the unchecked items As a user, I want to uncheck a to-do item As a user, I want to delete all the to-do items When the user taps an entry, the details of this entry is shown in the task detail view. The task detail view The task detail view shows all the information that's stored for a to-do item. The information consists of a title, due date, location (name and address), and a description. If an address is given, a map with an address is shown. The detail view also allows checking the item as finished. The detail view looks like this: User stories: As a user, given that I have tapped a to-do item in the list, I want to see its details As a user, I want to check a to-do item from its details view The task input view When the user selects the add (+) button in the list view, the task input view is shown. The user can add information for the task. Only the title is required. The Save button can only be selected when a title is given. It is not possible to add a task that is already in the list. The Cancel button dismisses the view. The task input view will look like this: User stories: As a user, given that I have tapped the add (+) button in the item list, I want to see a form to put in the details (title, optional date, optional location name, optional address, and optional description) of a to-do item As a user, I want to add a to-do item to the list of to-do items by tapping on the Save button We will not implement the editing and deletion of tasks. But when you have worked through this book completely, it will be easy for you to add this feature yourself by writing the tests first. Keep in mind that we will not test the look and design of the app. Unit tests cannot figure out whether an app looks like it was intended. Unit tests can test features, and these are independent of their presentation. In principle, it would be possible to write unit tests for the position and color of UI elements. But such things are very likely to change a lot in the early stages of development. We do not want to have failing tests only because a button has moved 10 points. However, we will test whether the UI elements are present on the view. If your user cannot see the information for the tasks, or if it is not possible to add all the information of a task, then the app does not meet the requirements. The structure of the app The following diagram shows the structure of the app: The Table View Controller, the delegate, and the data source In iOS apps, data is often presented using a table view. Table views are highly optimized for performance; they are easy to use and to implement. We will use a table view for the list of to-do items. A table view is usually represented by UITableViewController, which is also the data source and delegate for the table view. This often leads to a massive Table View Controller, because it is doing too much: presenting the view, navigating to other view controllers, and managing the presentation of the data in the table view. It is a good practice to split up the responsibility into several classes. Therefore, we will use a helper class to act as the data source and delegate for the table view. The communication between the Table View Controller and the helper class will be defined using a protocol. Protocols define what the interface of a class looks like. This has a great benefit: if we need to replace an implementation with a better version (maybe because we have learned how to implement the feature in a better way), we only need to develop against the clear interface. The inner workings of other classes do not matter. Table view cells As you can see in the preceding screenshots, the to-do list items have a title and, optionally, they can have a due date and a location name. The table view cells should only show the set data. We will accomplish this by implementing our own custom table view cell. The model The model of the application consists of the to-do item, the location, and an item manager, which allows the addition and removal of items and is also responsible for managing the items. Therefore, the controller will ask the item manager for the items to present. The item manager will also be responsible for storing the items on disc. Beginners often tend to manage the model objects within the controller. Then, the controller has a reference to a collection of items, and the addition and removal of items is directly done by the controller. This is not recommended, because if we decide to change the storage of the items (for example, by using Core Data), their addition and removal would have to be changed within the controller. It is difficult to keep an overview of such a class; and because of this reason, it is a source of bugs. It is much easier to have a clear interface between the controller and the model objects, because if we need to change how the model objects are managed, the controller can stay the same. We could even replace the complete model layer if we just keep the interface the same. Later in the article, we will see that this decoupling also helps to make testing easier. Other view controllers The application will have two more view controllers: a task detail View Controller and a View Controller for the input of the task. When the user taps a to-do item in the list, the details of the item are presented in the task detail View Controller. From the Details screen, the user will be able to check an item. New to-do items will be added to the list of items using the view presented by the input View Controller. The development strategy In this book, we will build the app from inside out. We will start with the model, and then build the controllers and networking. At the end of the book, we will put everything together. Of course, this is not the only way to build apps. But by separating on the basis of layers instead of features, it is easier to follow and keep an overview of what is happening. When you later need to refresh your memory, the relevant information you need is easier to find. Getting started with Xcode Now, let's start our journey by creating a project that we will implement using TDD. Open Xcode and create a new iOS project using the Single View Application template. In the options window, add ToDo as the product name, select Swift as language, choose iPhone in the Devices option, and check the box next to Include Unit Tests. Let the Use Core Data and Include UI Tests boxes stay unchecked. Xcode creates a small iOS project with two targets: one for the implementation code and the other for the unit tests. The template contains code that presents a single view on screen. We could have chosen to start with the master-detail application template, because the app will show a master and a detail view. However, we have chosen the Single View Application template because it comes with hardly any code. And in TDD, we want to have all the implementation code demanded by failing tests. To take a look at how the application target and test target fit together, select the project in Project Navigator, and then select the ToDoTests target. In the General tab, you'll find a setting for the Host Application that the test target should be able to test. It will look like this: Xcode has already set up the test target correctly to allow the testing of the implementations that we will write in the application target. Xcode has also set up a scheme to build the app and run the tests. Click on the Scheme selector next to the stop button in the toolbar, and select Edit Scheme.... In the Test action, all the test bundles of the project will be listed. In our case, only one test bundle is shown—ToDoTests. On the right-hand side of the shown window is a column named Test, with a checked checkbox. This means that if we run the tests while this scheme is selected in Xcode, all the tests in the selected test suite will be run. Setting up useful Xcode behaviors for testing Xcode has a feature called behaviors. With the use of behaviors and tabs, Xcode can show useful information depending on its state. Open the Behaviors window by going to Xcode | Behaviors | Edit Behaviors. On the left-hand side are the different stages for which you can add behaviors (Build, Testing, Running, and so on). The following behaviors are useful when doing TDD. The behaviors shown here are those that I find useful. Play around with the settings to find the ones most useful for you. Overall, I recommend using behaviors because I think they speed up development. Useful build behaviors When building starts, Xcode compiles the files and links them together. To see what is going on, you can activate the build log when building starts. It is recommended that you open the build log in a new tab because this allows switching back to the code editor when no error occurs during the build. Select the Starts stage and check Show tab named. Put in the Log name and select in active window. Check the Show navigator setting and select Issue Navigator. At the bottom of the window, check Navigate to and select current log. After you have made these changes, the settings window will look like this: Build and run to see what the behavior looks like. Testing behaviors To write some code, I have an Xcode tab called Coding. Usually, in this tab, the test is open on the left-hand side, and in the Assistant Editor which is on the right-hand side, there is the code to be tested (or in the case of TDD, the code to be written). It looks like this: When the test starts, we want to see the code editor again. So, we add a behavior to show the Coding tab. In addition to this, we want to see the Test Navigator and debugger with the console view. When the test succeeds, Xcode should show a bezel to notify us that all tests have passed. Go to the Testing | Succeeds stage, and check the Notify using bezel or system notification setting. In addition to this, it should hide the navigator and the debugger, because we want to concentrate on refactoring or writing the next test. In case the testing fails (which happens a lot in TDD), Xcode will show a bezel again. I like to hide the debugger, because usually, it is not the best place to figure out what is going on in the case of a failing test. And in most of the cases in TDD, we already know what the problem is. But we want to see the failing test. Therefore, check Show navigator and select Issue navigator. At the bottom of the window, check Navigate to and select first new issue. You can even make your Mac speak the announcements. Check Speak announcements using and select the voice you like. But be careful not to annoy your coworkers. You might need their help in the future. Now, the project and Xcode are set up, and we can start our TDD journey. Summary In this article, we took a look at the app that we are going to build throughout the course of this book. We took a look at how the screens of the app will look when we are finished with it. We created the project that we will use later on and learned about Xcode behaviors. Resources for Article: Further resources on this subject: Thinking Functionally [article] Hosting on Google App Engine [article] Cloud and Async Communication [article]
Read more
  • 0
  • 0
  • 1618
article-image-swifts-core-libraries
Packt
13 Oct 2016
26 min read
Save for later

Swift's Core Libraries

Packt
13 Oct 2016
26 min read
In this article, Jon Hoffman, the author of the book Mastering Swift 3, talks about how he was really excited when Apple announced that they were going to release a version of Swift for Linux, so he could use Swift for his Linux and embedded development as well as his Mac OS and iOS development. When Apple first released Swift 2.2 for Linux, he was very excited but was also a little disappointed because he could not read/write files, access network services, or use Grand Central Dispatch (GCD or libdispatch) on Linux like he could on Apple's platforms. With the release of Swift 3, Apple has corrected this with the release of Swift's core libraries. (For more resources related to this topic, see here.) In this article, you will learn about the following topics: What are the Swift core libraries How to use Apple's URL loading system How to use the Formatter classes How to use the File Manager class The Swift Core Libraries are written to provide a rich set of APIs that are consistent across the various platforms that Swift supports. By using these libraries, developers will be able to write code that will be portable to all platforms that Swift supports. These libraries provide a higher level of functionality as compared to the Swift standard library. The core libraries provide functionality in a number of areas, such as: Networking Unit testing Scheduling and execution of work (libdispatch) Property lists, JSON parsing, and XML parsing Support for dates, times, and calendar calculations Abstraction of OS-specific behavior Interaction with the file system User preferences We are unable to cover all of the Core Libraries in this single article; however, we will look at some of the more useful ones. We will start off by looking at Apple's URL loading system that is used for network development. Apple's URL loading system Apple's URL loading system is a framework of classes available to interact with URLs. We can use these classes to communicate with services that use standard Internet protocols. The classes that we will be using in this section to connect to and retrieve information from REST services are as follows: URLSession: This is the main session object. URLSessionConfiguration: This is used to configure the behavior of the URLSession object. URLSessionTask: This is a base class to handle the data being retrieved from the URL. Apple provides three concrete subclasses of the URLSessionTask class. URL: This is an object that represents the URL to connect to. URLRequest: This class contains information about the request that we are making and is used by the URLSessionTask service to make the request. HTTPURLResponse: This class contains the response to our request. Now, let's look at each of these classes a little more in depth so that we have a basic understanding of what each does. URLSession A URLSession object provides an API for interacting with various protocols such as HTTP and HTTPS. The session object, which is an instance of the URLSession, manages this interaction. These session objects are highly configurable, which allows us to control how our requests are made and how we handle the data that is returned. Like most networking APIs, URLSession is asynchronous. This means that we have to provide a way to return the response from the service back to the code that needs it. The most popular way to return the results from a session is to pass a completion handler block (closure) to the session. This completion handler is then called when the service successfully responds or we receive an error. All of the examples in this article use completion handlers to process the data that is returned from the services. URLSessionConfiguration The URLSessionConfiguration class defines the behavior and policies to use when using the URLSession object to connect to a URL. When using the URLSession object, we usually create a URLSessionConfiguration instance first, because an instance of this class is required when we create an instance of the URLSession class. The URLSessionConfiguration class defines three session types: Default session configuration: Manages the upload and download tasks with default configurations Ephemeral session configuration: This configuration behaves similar to the default session configuration, except that it does not cache anything to disk Background session configuration: This session allows for uploads and downloads to be performed, even when the app is running in the background It is important to note that we should make sure that we configure the URLSessionConfiguration object appropriately before we use it to create an instance of the URLSession class. When the session object is created, it creates a copy of the configuration object that we provide. Any changes made to the configuration object once the session object is created are ignored by the session. If we need to make changes to the configuration, we must create another instance of the URLSession class. URLSessionTask The URLSession service uses an instance of the URLSessionTask class to make the call to the service that we are connecting to. The URLSessionTask class is a base class, and Apple has provided three concrete subclasses that we can use: URLSessionDataTask: This returns the response, in memory, directly to the application as one or more Data objects. This is the task that we generally use most often. URLSessionDownloadTask: This writes the response directly to a temporary file. URLSessionUploadTask: This is used for making requests that require a request body, such as a POST or PUT request. It is important to note that a task will not send the request to the service until we call the resume() method. URL The URL object represents the URL that we are going to connect to. The URL class is not limited to URLs that represent remote servers, but it can also be used to represent a local file on disk. In this article, we will be using the URL class exclusively to represent the URL of the remote service that we are connecting to. URLRequest We use the URLRequest class to encapsulate our URL and the request properties. It is important to understand that the URLRequest class is used to encapsulate the necessary information to make our request, but it does not make the actual request. To make the request, we use instances of the URLSession and URLSessionTask classes. HTTPURLResponse The HTTPURLResponse class is a subclass of the URLResponse class that encapsulates the metadata associated with the response to a URL request. The HTTPURLResponse class provides methods for accessing specific information associated with an HTTP response. Specifically, this class allows us to access the HTTP header fields and the response status codes. We briefly covered a number of classes in this section and it may not be clear how they all actually fit together; however, once you see the examples a little further in this article, it will become much clearer. Before we go into our examples, let's take a quick look at the type of service that we will be connecting to. REST web services REST has become one of the most important technologies for stateless communications between devices. Due to the lightweight and stateless nature of the REST-based services, its importance is likely to continue to grow as more devices are connected to the Internet. REST is an architecture style for designing networked applications. The idea behind REST is that instead of using complex mechanisms, such as SOAP or CORBA to communicate between devices, we use simple HTTP requests for the communication. While, in theory, REST is not dependent on the Internet protocols, it is almost always implemented using them. Therefore, when we are accessing REST services, we are almost always interacting with web servers in the same way that our web browsers interact with these servers. REST web services use the HTTP POST, GET, PUT, or DELETE methods. If we think about a standard Create, Read, Update, Delete (CRUD) application, we would use a POST request to create or update data, a GET request to read data, and a DELETE request to delete data. When we type a URL into our browser's address bar and hit Enter, we are generally making a GET request to the server and asking it to send us the web page associated with that URL. When we fill out a web form and click the submit button, we are generally making a POST request to the server. We then include the parameters from the web form in the body of our POST request. Now, let's look at how to make an HTTP GET request using Apple's networking API. Making an HTTP GET request In this example, we will make a GET request to Apple's iTunes search API to get a list of items related to the search term "Jimmy Buffett". Since we are retrieving data from the service, by REST standards, we should use a GET request to retrieve the data. While the REST standard is to use GET requests to retrieve data from a service, there is nothing stopping a developer of a web service from using a GET request to create or update a data object. It is not recommended to use a GET request in this manner, but just be aware that there are services out there that do not adhere to the REST standards. The following code makes a request to Apple's iTunes search API and then prints the results to the console: public typealias dataFromURLCompletionClosure = (URLResponse?, Data?) -> Void public func sendGetRequest ( _ handler: @escaping dataFromURLCompletionClosure) { let sessionConfiguration = URLSessionConfiguration.default; let urlString = "https://itunes.apple.com/search?term=jimmy+buffett" if let encodeString = urlString.addingPercentEncoding( withAllowedCharacters: CharacterSet.urlQueryAllowed), let url = URL(string: encodeString) { var request = URLRequest(url:url) request.httpMethod = "GET" let urlSession = URLSession( configuration:sessionConfiguration, delegate: nil, delegateQueue: nil) let sessionTask = urlSession.dataTask(with: request) { (data, response, error) in handler(response, data) } sessionTask.resume() } } We start off by creating a type alias named DataFromURLCompletionClosure. The DataFromURLCompletionClosure type will be used for both the GET and POST examples of this article. If you are not familiar with using a typealias object to define a closure type. We then create a function named sendGetRequest(), which will be used to make the GET request to Apple's iTunes API. This function accepts one argument named handler, which is a closure that conforms to the DataFromURLCompletionClosure type. The handler closure will be used to return the results from the request. Stating with Swift 3, the default for closure arguments to functions is not escaping, which means that, by default, the closures argument cannot escape the function body. A closure is considered to escape a function when that closure, which is passed as an argument to the function, is called after the function returns. Since the closure will be called after the function returns, we use the @escaping attribute before the parameter type to indicate it is allowed to escape. Within our sendGetRequest() method, we begin by creating an instance of the URLSessionConfiguration class using the default settings. If we need to, we can modify the session configuration properties after we create it, but in this example, the default configuration is what we want. After we create our session configuration, we create the URL string. This is the URL of the service we are connecting to. With a GET request, we put our parameters in the URL itself. In this specific example, https://itunes.apple.com/search is the URL of the web service. We then follow the web service URL with a question mark (?), which indicates that the rest of the URL string consists of parameters for the web service. The parameters take the form of key/value pairs, which means that each parameter has a key and a value. The key and value of a parameter, in a URL, are separated by an equals sign (=). In our example, the key is term and the value is jimmy+buffett. Next, we run the URL string that we just created through the addingPercentEncoding() method to make sure our URL string is encoded properly. We use the CharacterSet.urlQueryAllowed character set with this method to ensure we have a valid URL string. Next, we use the URL string that we just built to create a URL instance named url. Since we are making a GET request, this URL instance will represent both the location of the web service and the parameters that we are sending to it. We create an instance of the URLRequest class using the URL instance that we just created. In this example, we set the HTTPMethod property; however, we can also set other properties, such as the timeout interval or add items to our HTTP header. Now, we use the sessionConfiguration constant that we created at the beginning of the sendGetRequest() function to create an instance of the URLSession class. The URLSession class provides the API that we will use to connect to Apple's iTunes search API. In this example, we use the dataTask(with:) method of the URLSession instance to return an instance of the URLSessionDataTask type named sessionTask. The sessionTask instance is what makes the request to the iTunes search API. When we receive the response from the service, we use the handler callback to return both the URLResponse object and the Data object. The URLResponse contains information about the response, and the Data instance contains the body of the response. Finally, we call the resume() method of the URLSessionDataTask instance to make the request to the web service. Remember, as we mentioned earlier, a URLSessionTask instance will not send the request to the service until we call the resume() method. Now, let's look at how we would call the sendGetRequest() function. The first thing we need to do is to create a closure that will be passed to the sendGetRequest() function and called when the response from the web service is received. In this example, we will simply print the response to the console. Since the response is in the JSON format, we could use the JSONSerialization class, which we will see later in this article, to parse the response; however, since this section is on networking, we will simply print the response to the console. Here is the code: let printResultsClosure: dataFromURLCompletionClosure = { if let data = $1 { let sString = let sString = String(data: data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) print(sString) } else { print("Data is nil") } } We define this closure, named printResultsClosure, to be an instance of the DataFromURLCompletionClosure type. Within the closure, we unwrap the first parameter and set the value to a constant named data. If the first parameter is not nil, we convert the data constant to an instance of the String class, which is then printed to the console. Now, let's call the sendGetRequest() method with the following code: let aConnect = HttpConnect() aConnect.sendGetRequest(printResultsClosure) This code creates an instance of the HttpConnect class and then calls the sendGetRequest() method, passing the printResultsClosure closure as the only parameter. If we run this code while we are connected to the Internet, we will receive a JSON response that contains a list of items related to Jimmy Buffett on iTunes. Now that we have seen how to make a simple HTTP GET request, let's look at how we would make an HTTP POST request to a web service. Making an HTTP POST request Since Apple's iTunes, APIs use GET requests to retrieve data. In this section, we will use the free http://httpbin.org service to show you how to make a POST request. The POST service that http://httpbin.org provides can be found at http://httpbin.org/post. This service will echo back the parameters that it receives so that we can verify that our request was made properly. When we make a POST request, we generally have some data that we want to send or post to the server. This data takes the form of key/value pairs. These pairs are separated by an ampersand (&) symbol, and each key is separated from its value by an equals sign (=). As an example, let's say that we want to submit the following data to our service: firstname: Jon lastname: Hoffman age: 47 years The body of the POST request would take the following format: firstname=Jon&lastname=Hoffman&age=47 Once we have the data in the proper format, we will then use the dataUsingEncoding() method, as we did with the GET request to properly encode the POST data. Since the data going to the server is in the key/value format, the most appropriate way to store this data, prior to sending it to the service, is with a Dictionary object. With this in mind, we will need to create a method that will take a Dictionary object and return a string object that can be used for the POST request. The following code will do that: private func dictionaryToQueryString(_ dict: [String : String]) -> String { var parts = [String]() for (key, value) in dict { let part : String = key + "=" + value parts.append(part); } return parts.joined(separator: "&") } This function loops through each key/value pair of the Dictionary object and creates a String object that contains the key and the value separated by the equals sign (=). We then use the joinWithSeperator() function to join each item in the array, separated by the specified sting. In our case, we want to separate each string with the ampersand symbol (&). We then return this newly created string to the code that called it. Now, let's create the sendPostRequest() function that will send the POST request to the http://httpbin.org post service. We will see a lot of similarities between this sendPostRequest() function and the sendGetRequest() function, which we showed you in the Making an HTTP GET request section. Let's take a look at the following code: public func sendPostRequest(_ handler: @escaping dataFromURLCompletionClosure) { let sessionConfiguration = URLSessionConfiguration.default let urlString = "http://httpbin.org/post" if let encodeString = urlString.addingPercentEncoding( withAllowedCharacters: CharacterSet.urlQueryAllowed), let url = URL(string: encodeString) { var request = URLRequest(url:url) request.httpMethod = "POST" let params = dictionaryToQueryString(["One":"1 and 1", "Two":"2 and 2"]) request.httpBody = params.data( using: String.Encoding.utf8, allowLossyConversion: true) let urlSession = URLSession( configuration:sessionConfiguration, delegate: nil, delegateQueue: nil) let sessionTask = urlSession.dataTask(with: request) { (data, response, error) in handler(response, data) } sessionTask.resume() } } This code is very similar to the sendGetRequest() function that we saw earlier in this section. The two main differences are the httpMethod of the URLRequest is set to POST rather than GET and how we set the parameters. In this function, we set the httpBody property of the URLRequest instance to the parameters we are submitting. Now that we have seen how to use the URL Loading System, let's look at how we can use Formatters. Formatter Formatter is an abstract class that declares an interface for an object that creates, converts, or validates a human readable form of some data. The types that subclass the Formatter class are generally used when we want to take a particular object, like an instance of the Date class, and present the value in a form that the user of our application can understand. Apple has provided several concrete implementations of the Formatter class, and in this section we will look at two of them. It is important to remember that the formatters that Apple provides will provide the proper format for the default locale of the device the application is running on. We will start off by looking at the DateFormatter type. DateFormatter The DateFormatter class is a subclass of the Formatter abstract class that can be used to convert a Date object into a human readable string. It can also be used to convert a String representation of a date into a Date object. We will look at both of these use cases in this section. Let's begin by seeing how we could covert a Date object into a human readable string. The DateFormatter type has five predefined styles that we can use when we are converting a Date object to a human readable string. The following chart shows what the five styles look like for an en-US locale. DateFormatter Style Date Time .none No Format No Format .short 12/25/16 6:00 AM .medium Dec 25, 2016 6:00:00 AM .long December 25, 2016 6:00:00 AM EST .full Sunday December 25, 2016 6:00:00 AM Eastern Standard Time The following code shows how we would use the predefined DateFormatter styles: let now = Date() let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium let dateStr = formatter.string(from: now) We use the string(from:) method to convert the now date to a human readable string. In this example, for the en-US locale, the dateStr constant would contain text similar to "Aug 19, 2016, 6:40 PM". There are numerous times when the predefined styles do not meet our needs. For those times, we can define our own styles using a custom format string. This string is a series of characters that the DateFormatter type knows are stand-ins for the values we want to show. The DateFormatter instance will replace these stand-ins with the appropriate values. The following table shows some of the formatting values that we can use to format our Date objects: Stand-in Format Description Example output yy Two digit year 16, 14, 04 yyyy Four digit year 2016, 2014, 2004 MM Two digit month 06, 08, 12 MMM Three letter Month Jul, Aug, Dec MMMM Full month name July, August dd Two digit day 10, 11, 30 EEE Three letter Day Mon, Sat, Sun EEEE Full day Monday, Sunday a Period of day AM, PM hh Two digit hour 02, 03, 04 HH Two digit hour for 24 hour clock 11, 14, 16 mm Two digit minute 30, 35, 45 ss Two digit seconds 30, 35, 45 The following code shows how we would use these custom formatters: let now = Date() let formatter2 = DateFormatter() formatter2.dateFormat = "YYYY-MM-dd HH:mm:ss" let dateStr2 = formatter2.string(from: now) We use the string(from:) method to convert the now date to a human readable string. In this example, for the en-US locale, the dateStr2 constant would contain text similar to 2016-08-19 19:03:23. Now let's look at how we would take a formatted date string and convert it to a Date object. We will use the same format string that we used in our last example: formatter2.dateFormat = "YYYY-MM-dd HH:mm:ss" let dateStr3 = "2016-08-19 16:32:02" let date = formatter2.date(from: dateStr3) In this example, we took the human readable date string and converted it to a Date object using the date(from:) method. If the format of the human readable date string does not match the format specified in the DateFormatter instance, then the conversion will fail and return nil. Now let's take a look at the NumberFormatter type. NumberFormatter The NumberFormatter class is a subclass of the Formatter abstract class that can be used to convert a number into a human readable string with a specified format. This formatter is especially useful when we want to display a currency string since it will convert the number to the proper currency to the proper locale. Let's begin by looking at how we would convert a number into a currency string: let formatter1 = NumberFormatter() formatter1.numberStyle = .currency let num1 = formatter1.string(from: 23.99) In the previous code, we define our number style to be .currency, which tells our formatter that we want to convert our number to a currency string. We then use the string(from:) method to convert the number to a string. In this example, for the en-US locale, the num1 constant would contain the string "$23.99". Now let's see how we would round a number using the NumberFormatter type. The following code will round the number to two decimal points: let formatter2 = NumberFormatter() formatter2.numberStyle = .decimal formatter2.maximumFractionDigits = 2 let num2 = formatter2.string(from: 23.2357) In this example, we set the numberStyle property to the .decimal style and we also define the maximum number of decimal digits to be two using the maximumFractionDigits property. We use the string(from:) method to convert the number to a string. In this example, the num2 constant will contain the string "23.24". Now let's see how we can spell out a number using the NumberFormatter type. The following code will take a number and spell out the number: let formatter3 = NumberFormatter() formatter3.numberStyle = .spellOut let num3 = formatter3.string(from: 2015) In this example, we set the numberStyle property to the .spellout style. This style will spell out the number. For this example, the num3 constant will contain the string two thousand fifteen. Now let's look at how we can manipulate the file system using the FileManager class FileManager The file system is a complex topic and how we manipulate it within our applications is generally specific to the operating system that our application is running on. This can be an issue when we are trying to port code from one operating system to another. Apple has addressed this issue by putting the FileManager object in the core libraries. The FileManager object lets us examine and make changes to the file system in a uniform manner across all operating systems that Swift supports. The FileManager class provides us with a shared instance that we can use. This instance should be suitable for most of our file system related tasks. We can access this shared instance using the default property. When we use the FileManager object we can provide paths as either an instance of the URL or String types. In this section, all of our paths will be String types for consistency purposes. Let's start off by seeing how we could list the contents of a directory using the FileManager object. The following code shows how to do this: let fileManager = FileManager.default do { let path = "/Users/jonhoffman/" let dirContents = try fileManager.contentsOfDirectory(atPath: path) for item in dirContents { print(item); } } catch let error { print("Failed reading contents of directory: (error)") } We start off by getting the shared instance of the FileManager object using the default property. We will use this same shared instance for all of our examples in this section rather than redefining it for each example. Next, we define our path and use it with the contentsOfDirectory(atPath:) method. This method returns an array of String types that contains the names of the items in the path. We use a for loop to list these items. Next let's look at how we would create a directory using the file manager. The following code shows how to do this: do { let path = "/Users/jonhoffman/masteringswift/test/dir" try fileManager.createDirectory(atPath: path, withIntermediateDirectories: true) } catch let error { print("Failed creating directory, (error) ") } In this example, we use the createDirectory(atPath: withIntermediateDirectories:) method to create the directory. When we set the withIntermediateDirectories parameter to true, this method will create any parent directories that are missing. When this parameter is set to false, if any parent directories are missing the operation will fail. Now let's look at how we would copy an item from one location to another: do { let pathOrig = "/Users/jonhoffman/masteringswift/" let pathNew = "/Users/jonhoffman/masteringswift2/" try fileManager.copyItem(atPath: pathOrig, toPath: pathNew) } catch let error { print("Failed copying directory, (error) ") } In this example, we use the copyItem(atPath: toPath:) method to copy one item to another location. This method can be used to copy either directories or files. If it is used for directories, the entire path structure below the directory specified by the path is copied. The file manager also has a method that will let us move an item rather than copying it. Let's see how to do this: do { let pathOrig = "/Users/jonhoffman/masteringswift2/" let pathNew = "/Users/jonhoffman/masteringswift3/" try fileManager.moveItem(atPath: pathOrig, toPath: pathNew) } catch let error { print("Failed moving directory, (error) ") } To move an item, we use the moveItem(atPath: toPath:) method. Just like the copy example we just saw, the move method can be used for either files or directories. If the path specifies a directory, then the entire directory structure below that path will be moved. Now let's see how we can delete an item from the file system: do { let path = "/Users/jonhoffman/masteringswift/" try fileManager.removeItem(atPath: path) } catch let error { print("Failed Removing directory, (error) ") } In this example, we use the removeItem(atPath:) method to remove the item from the file system. A word of warning, once you delete something it is gone and there is no getting it back. Next let's look at how we can read permissions for items in our file system. For this we will need to create a file named test.file, which our path will point to: let path = "/Users/jonhoffman/masteringswift3/test.file" if fileManager.fileExists(atPath: path) { let isReadable = fileManager.isReadableFile(atPath: path) let isWriteable = fileManager.isWritableFile(atPath: path) let isExecutable = fileManager.isExecutableFile(atPath: path) let isDeleteable = fileManager.isDeletableFile(atPath: path) print("can read (isReadable)") print("can write (isWriteable)") print("can execute (isExecutable)") print("can delete (isDeleteable)") } In this example, we use four different methods to read the file system permissions for the file system item. These methods are: isReadableFile(atPath:): true if the file is readable isWritableFile(atPath:):true if the file is writable isExecutableFile(atPath:): true if the file is executable isDeletableFile(atPath:): true if the file is deletable If we wanted to read or write text files, we could use methods provided by the String type rather than the FileManager type. Even though we do not use the FileManager class, for this example, we wanted to show how to read and write text files. Let's see how we would write some text to a file: let filePath = "/Users/jonhoffman/Documents/test.file" let outString = "Write this text to the file" do { try outString.write(toFile: filePath, atomically: true, encoding: .utf8) } catch let error { print("Failed writing to path: (error)") } In this example, we start off by defining our path as a String type just as we did in the previous examples. We then create another instance of the String type that contains the text we want to put in our file. To write the text to the file we use the write(toFile: atomically: encoding:) method of the String instance. This method will create the file if needed and write the contents of the String instance to the file. It is just as easy to read a text file using the String type. The following example shows how to do this: let filePath = "/Users/jonhoffman/Documents/test.file" var inString = "" do { inString = try String(contentsOfFile: filePath) } catch let error { print("Failed reading from path: (error)") } print("Text Read: (inString)") In this example, we use the contentsOfFile: initiator to create an instance of the String type that contains the contents of the file specified by the file path. Now that we have seen how to use the FileManager type, let's look at how we would serialize and deserialize JSON documents using the JSONSerialization type. Summary In this article, we looked at some of the libraries that make up the Swift Core Libraries. While these libraries are only a small portion of the libraries that make up the Swift Core Libraries they are arguable some of the most useful libraries. To explore the other libraries that, you can refer to Apple's GitHub page: https://github.com/apple/swift-corelibs-foundation. Resources for Article: Further resources on this subject: Flappy Swift [article] Network Development with Swift [article] Your First Swift 2 Project [article]
Read more
  • 0
  • 0
  • 2619

article-image-fake-swift-enums-user-friendly-frameworks
Daniel Leping
28 Sep 2016
7 min read
Save for later

Fake Swift Enums and User-Friendly Frameworks

Daniel Leping
28 Sep 2016
7 min read
Swift Enums are a great and useful hybrid of classic enums with tuples. One of the most attractive points of consideration here is their short .something API when you want to pass one as an argument to a function. Still, they are quite limited in certain situations. OK, enough about enums themselves, because this post is about fake enums (don't take my word literally here). Why? Everybody is talking about usability, UX, and how to make the app user-friendly. This all makes sense, because at the end of the day we all are humans and perceive things with feelings. So the app just leaves the impression footprint. There are a lot of users who would prefer a more friendly app to the one providing a richer functionality. All these talks are proven by Apple and their products. But what about a developer, and what tools and bricks are we using? Right now I'm not talking about the IDEs, but rather about the frameworks and the APIs. Being an open source framework developer myself (right now I'm working on Swift Express, a web application framework in Swift, and the foundation around it), I'm concerned about the looks of the APIs we are providing. It matches one-to-one to the looks of the app, so it has the same importance. Call it Framework's UX if you would like. If the developer is pleased with the API the framework provides, he is 90% already your user. This post was particularly inspired by the Event sub-micro-framework, a part of the Reactive Swift foundation I'm creating right now to make Express solid. We took the basic idea from node.js EvenEmitter, which is very easy to use in my opinion. Though, instead of using the String Event ID approach provided by node, we wanted to use the .something approach (read above about what I think of nice APIs) and we are hoping that enums would work great, we encountered limitations with it. The hardest thing was to create a possibility to use different arguments for closures of different event types. It's all very simple with dynamic languages like JavaScript, but well, here we are type-safe. You know... The problem Usually, when creating a new framework, I try to first write an ideal API, or the one I would like to see at the very end. And here is what I had: eventEmitter.on(.stringevent) { string in print("string:", string) } eventEmitter.on(.intevent) { i in print("int:", i) } eventEmitter.on(.complexevent) { (s, i) in print("complex: string:", s, "int:", i) } This seems easy enough for the final user. What I like the most here is that different EventEmitters can have different sets of events with specific data payloads and still provide the .something enum style notation. This is easier to say than to do. With enums, you cannot have an associated type bound to a specific case. It must be bound to the entire enum, or nothing. So there is no possibility to provide a specific data payload for a particular case. But I was very clear that I want everything type-safe, so there can be no dynamic arguments. The research First of all, I began investigating if there is a possibility to use the .something notation without using enums. The first thing I recalled was the OptionSetType that is mainly used to combine the flags for C APIs. And it allows the .somthing notation. You might want to investigate this protocol as it's just cool and useful in many situations where enums are not enough. After a bit of experimenting, I found out that any class or struct having a static member of Self type can mimic an enum. Pretty much like this: struct TestFakeEnum { private init() { } static let one:TestFakeEnum = TestFakeEnum() static let two:TestFakeEnum = TestFakeEnum() static let three:TestFakeEnum = TestFakeEnum() } func funWithEnum(arg:TestFakeEnum) { } func testFakeEnum() { funWithEnum(.one) funWithEnum(.two) funWithEnum(.three) } This code will compile and run correctly. These are the basics of any fake enum. Even though the example above does not provide any particular benefit over built-in enums, it demonstrates the fundamental possibility. Getting generic Let's move on. First of all, to make our events have a specific data payload, we've added an EventProtocol (just keep in mind; it will be important later): //do not pay attention to Hashable, it's used for internal event routing mechanism. Not a subject here public protocol EventProtocol : Hashable { associatedtype Payload } To make our emitter even better we should not limit it to a restricted set of events, but rather allow the user to extend it. To achieve this, I've added a notion of EventGroup. It's not a particular type but rather an informal concept, so every event group should follow. Here is an example of an EventGroup: struct TestEventGroup<E : EventProtocol> { internal let event:E private init(_ event:E) { self.event = event } static var string:TestEventGroup<TestEventString> { return TestEventGroup<TestEventString>(.event) } static var int:TestEventGroup<TestEventInt> { return TestEventGroup<TestEventInt>(.event) } static var complex:TestEventGroup<TestEventComplex> { return TestEventGroup<TestEventComplex>(.event) } } Here is what TestEventString, TestEventInt and TestEventComplex are (real enums are used here only to have conformance with Hashable and to be a case singleton, so don't bother): //Notice, that every event here has its own Payload type enum TestEventString : EventProtocol { typealias Payload = String case event } enum TestEventInt : EventProtocol { typealias Payload = Int case event } enum TestEventComplex : EventProtocol { typealias Payload = (String, Int) case event } So to get a generic with .something notation, you have to create a generic class or struct having static members of the owner type with a particular generic param applied. Now, how can you use it? How can you discover what generic type is associated with a specific option? For that, I used the following generic function: // notice, that Payload is type-safety extracted from the associated event here with E.Payload func on<E : EventProtocol>(groupedEvent: TestEventGroup<E>, handler:E.Payload->Void) -> Listener { //implementation here is not of the subject of the article } Does this thing work? Yes. You can use the API exactly like it was outlined in the very beginning of this post: let eventEmitter = EventEmitterTest() eventEmitter.on(.string) { s in print("string:", s) } eventEmitter.on(.int) { i in print("int:", i) } eventEmitter.on(.complex) { (s, i) in print("complex: string:", s, "int:", i) } All the type inferences work. It's type safe. It's user friendly. And it is all thanks to a possibility to associate the type with the .something enum-like member. Conclusion Pity that this functionality is not available out of the box with built-in enums. For all the experiments to make this happen, I had to spend several hours. Maybe in one of the upcoming versions of Swift (3? 4.0?), Apple will let us get the type of associated values of an enum or something. But… okay. Those are dreams and out of the scope of this post. For now, we have what we have, and I'm really glad that are abele to have an associatedtype with enum-like entity, even if it's not straightforward. The examples were taken from the Event project. The complete code can be found here and it was tested with Swift 2.2 (Xcode 7.3), which is the latest at the time of writing. Thanks for reading. Use user-friendly frameworks only and enjoy your day! About the Author Daniel Leping is the CEO of Crossroad Labs. He has been working with Swift since the early beta releases and continues to do so at the Swift Express project. His main interests are reactive and functional programming with Swift, Swift-based web technologies, and bringing the best of modern techniques to the Swift world. He can be found on GitHub.
Read more
  • 0
  • 0
  • 1994

article-image-extra-extra-collection-and-closure-changes-rock
Packt
28 Sep 2016
19 min read
Save for later

Extra, Extra Collection, and Closure Changes that Rock!

Packt
28 Sep 2016
19 min read
In this article by Keith Elliott, author of, Swift 3 New Features , we are focusing on collection and closure changes in Swift 3. There are several nice additions that will make working with collections even more fun. We will also explore some of the confusing side effects of creating closures in Swift 2.2 and how those have been fixed in Swift 3. (For more resources related to this topic, see here.) Collection and sequence type changes Let’s begin our discussion with Swift 3 changes to Collection and Sequence types. Some of the changes are subtle and others are bound to require a decent amount of refactoring to your custom implementations. Swift provides three main collection types for warehousing your values: arrays, dictionaries, and sets. Arrays allow you to store values in an ordered list. Dictionaries provide unordered the key-value storage for your collections. Finally, sets provide an unordered list of unique values (that is, no duplicates allowed). Lazy FlatMap for sequence of optionals [SE-0008] Arrays, sets, and dictionaries are implemented as generic types in Swift. They each implement the new Collection protocol, which implements the Sequence protocol. Along this path from top-level type to Sequence protocol, you will find various other protocols that are also implemented in this inheritance chain. For our discussion on flatMap and lazy flatMap changes, I want to focus in on Sequences. Sequences contain a group of values that allow the user to visit each value one at a time. In Swift, you might consider using a for-in loop to iterate through your collection. The Sequence protocol provides implementations of many operations that you might want to perform on a list using sequential access, all of which you could override when you adopt the protocol in your custom collections. One such operation is the flatMap function, which returns an array containing the flattened (or rather concatenated) values, resulting from a transforming operation applied to each element of the sequence. let scores = [0, 5, 6, 8, 9] .flatMap{ [$0, $0 * 2] } print(scores) // [0, 0, 5, 10, 6, 12, 8, 16, 9, 18]   In our preceding example, we take a list of scores and call flatMap with our transforming closure. Each value is converted into a sequence containing the original value and a doubled value. Once the transforming operations are complete, the flatMap method flattens the intermediate sequences into a single sequence. We can also use the flatMap method with Sequences that contain optional values to accomplish a similar outcome. This time we are omitting values from the sequence we flatten by return nil on the transformation. let oddSquared = [1, 2, 3, 4, 5, 10].flatMap { n in n % 2 == 1 ? n*n : nil } print(oddSquared) // [1, 9, 25] The previous two examples were fairly basic transformations on small sets of values. In a more complex situation, the collections that you need to work with might be very large with expensive transformation operations. Under those parameters, you would not want to perform the flatMap operation or any other costly operation until it was absolutely needed. Luckily, in Swift, we have lazy operations for this very use case. Sequences contain a lazy property that returns a LazySequence that can perform lazy operations on Sequence methods. Using our first example, we can obtain a lazy sequence and call flatMap to get a lazy implementation. Only in the lazy scenario, the operation isn’t completed until scores is used sometime later in code. let scores = [0, 5, 6, 8, 9] .lazy .flatMap{ [$0, $0 * 2] } // lazy assignment has not executed for score in scores{ print(score) } The lazy operation works, as we would expect in our preceding test. However, when we use the lazy form of flatMap with our second example that contains optionals, our flatMap executes immediately in Swift 2. While we expected oddSquared variable to hold a ready to run flatMap, delayed until we need it, we instead received an implementation that was identical to the non-lazy version. let oddSquared = [1, 2, 3, 4, 5, 10] .lazy // lazy assignment but has not executed .flatMap { n in n % 2 == 1 ? n*n : nil } for odd in oddSquared{ print(odd) } Essentially, this was a bug in Swift that has been fixed in Swift 3. You can read the proposal at the following link https://github.com/apple/swift-evolution/blob/master/proposals/0008-lazy-flatmap-for-optionals.md Adding the first(where:) method to sequence A common task for working with collections is to find the first element that matches a condition. An example would be to ask for the first student in an array of students whose test scores contain a 100. You can accomplish this using a predicate to return the filtered sequence that matched the criteria and then just give back the first student in the sequence. However, it would be much easier to just call a single method that could return the item without the two-step approach. This functionality was missing in Swift 2, but was voted in by the community and has been added for this release. In Swift 3, there is a now an extension method on the Sequence protocol to implement first(where:). ["Jack", "Roger", "Rachel", "Joey"].first { (name) -> Bool in name.contains("Ro") } // =>returns Roger This first(where:) extension is a nice addition to the language because it ensures that a simple and common task is actually easy to perform in Swift. You can read the proposal at the following link https://github.com/apple/swift-evolution/blob/master/proposals/0032-sequencetype-find.md. Add sequence(first: next:) and sequence(state: next:) public func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldSequence<T, (T?, Bool)> public func sequence<T, State>(state: State, next: @escaping (inout State) -> T?) -> UnfoldSequence<T, State> public struct UnfoldSequence<Element, State> : Sequence, IteratorProtocol These two functions were added as replacements to the C-style for loops that were removed in Swift 3 and to serve as a compliment to the global reduce function that already exists in Swift 2. What’s interesting about the additions is that each function has the capability of generating and working with infinite sized sequences. Let’s examine the first sequence function to get a better understanding of how it works. /// - Parameter first: The first element to be returned from the sequence. /// - Parameter next: A closure that accepts the previous sequence element and /// returns the next element. /// - Returns: A sequence that starts with `first` and continues with every /// value returned by passing the previous element to `next`. /// func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldSequence<T, (T?, Bool)> The first sequence method returns a sequence that is created from repeated invocations of the next parameter, which holds a closure that will be lazily executed. The return value is an UnfoldSequence that contains the first parameter passed to the sequence method plus the result of applying the next closure on the previous value. The sequence is finite if next eventually returns nil and is infinite if next never returns nil. let mysequence = sequence(first: 1.1) { $0 < 2 ? $0 + 0.1 : nil } for x in mysequence{ print (x) } // 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 In the preceding example, we create and assign our sequence using the trailing closure form of sequence(first: next:). Our finite sequence will begin with 1.1 and will call next repeatedly until our next result is greater than 2 at which case next will return nil. We could easily convert this to an infinite sequence by removing our condition that our previous value must not be greater than 2. /// - Parameter state: The initial state that will be passed to the closure. /// - Parameter next: A closure that accepts an `inout` state and returns the /// next element of the sequence. /// - Returns: A sequence that yields each successive value from `next`. /// public func sequence<T, State>(state: State, next: (inout State) -> T?) -> UnfoldSequence<T, State> The second sequence function maintains mutable state that is passed to all lazy calls of next to create and return a sequence. This version of the sequence function uses a passed in closure that allows you to update the mutable state each time the next called. As was the case with our first sequence function, a finite sequence ends when next returns a nil. You can turn an finite sequence into an infinite one by never returning nil when next is called. Let’s create an example of how this version of the sequence method might be used. Traversing a hierarchy of views with nested views or any list of nested types is a perfect task for using the second version of the sequence function. Let’s create a an Item class that has two properties. A name property and an optional parent property to keep track of the item’s owner. The ultimate owner will not have a parent, meaning the parent property will be nil. class Item{ var parent: Item? var name: String = "" } Next, we create a parent and two nested children items. Child1 parent will be the parent item and child2 parent will be child1. let parent = Item() parent.name = "parent" let child1 = Item() child1.name = "child1" child1.parent = parent let child2 = Item() child2.name = "child2" child2.parent = child1 Now, it’s time to create our sequence. The sequence needs two parameters from us: a state parameter and a next closure. I made the state an Item with an initial value of child2. The reason for this is because I want to start at the lowest leaf of my tree and traverse to the ultimate parent. Our example only has three levels, but you could have lots of levels in a more complex example. As for the next parameter, I’m using a closure expression that expects a mutable Item as its state. My closure will also return an optional Item. In the body of our closure, I use our current Item (mutable state parameter) to access the item’s parent. I also updated the state and return the parent. let itemSeq = sequence(state: child2, next: { (next: inout Item)->Item? in let parent = next.parent next = parent != nil ? parent! : next return parent }) for item in itemSeq{ print("name: (item.name)") } There are some gotchas here that I want to address so that you will better understand how to define your own next closure for this sequence method. The state parameter could really be anything you want it to be. It’s for your benefit in helping you determine the next element of the sequence and to give you relevant information about where you are in the sequence. One idea to improve our example above would be to track how many levels of nesting we have. We could have made our state a tuple that contained an integer counter for the nesting level along with the current item. The next closure needs to be expanded to show the signature. Because of Swift’s expressiveness and conciseness, when it comes to closures, you might be tempted to convert the next closure into a shorter form and omit the signature. Do not do this unless your next closure is extremely simple and you are positive that the compiler will be able to infer your types. Your code will be harder to maintain when you use the short closure format, and you won’t get extra points for style when someone else inherits it. Don’t forget to update your state parameter in the body of your closure. This really is your best chance to know where you are in your sequence. Forgetting to update the state will probably cause you to get unexpected results when you try to step through your sequence. Make a clear decision ahead of the time about whether you are creating a finite or infinite sequence. This decision is evident in how you return from your next closure. An infinite sequence is not bad to have when you are expecting it. However, if you iterate over this sequence using a for-in loop, you could get more than you bargained for, provided you were assuming this loop would end. A new Model for Collections and Indices [SE-0065]Swift 3 introduces a new model for collections that moves the responsibility of the index traversal from the index to the collection itself. To make this a reality for collections, the Swift team introduced four areas of change: The Index property of a collection can be any type that implements the Comparable protocol Swift removes any distinction between intervals and ranges, leaving just ranges Private index traversal methods are now public Changes to ranges make closed ranges work without the potential for errors You can read the proposal at the following link https://github.com/apple/swift-evolution/blob/master/proposals/0065-collections-move-indices.md. Introducing the collection protocol In Swift 3, Foundation collection types such as Arrays, Sets, and Dictionaries are generic types that implement the newly created Collection protocol. This change was needed in order to support traversal on the collection. If you want to create custom collections of your own, you will need to understand the Collection protocol and where it lives in the collection protocol hierarchy. We are going to cover the important aspects to the new collection model to ease you transition and to get you ready to create custom collection types of your own. The Collection protocol builds on the Sequence protocol to provide methods for accessing specific elements when using a collection. For example, you can use a collection’s index(_:offsetBy:) method to return an index that is a specified distance away from the reference index. let numbers = [10, 20, 30, 40, 50, 60] let twoAheadIndex = numbers.index(numbers.startIndex, offsetBy: 2) print(numbers[twoAheadIndex]) //=> 30 In the preceding example, we create the twoAheadIndex constant to hold the position in our numbers collection that is two positions away from our starting index. We simply use this index to retrieve the value from our collection using subscript notation. Conforming to the Collection Protocol If you would like to create your own custom collections, you need to adopt the Collection protocol by declaring startIndex and endIndex properties, a subscript to support access to your elements, and the index(after: ) method to facilitate traversing your collection’s indices. When we are migrating existing types over to Swift 3, the migrator has some known issues with converting custom collections. It’s likely that you can easily resolve the compiler issues by checking the imported types for conformance to the Collection protocol. Additionally, you need to conform to the Sequence and IndexableBase protocols as the Collection protocol adopts them both. public protocol Collection : Indexable, Sequence { … } A simple custom collection could look like the following example. Note that I have defined my Index type to be an Int. In Swift 3, you define the Index to be any type that implements the Comparable protocol. struct MyCollection<T>: Collection{ typealias Index = Int var startIndex: Index var endIndex: Index var _collection: [T] subscript(position: Index) -> T{ return _collection[position] } func index(after i: Index) -> Index { return i + 1 } init(){ startIndex = 0 endIndex = 0 _collection = [] } mutating func add(item: T){ _collection.append(item) } } var myCollection: MyCollection<String> = MyCollection() myCollection.add(item: "Harry") myCollection.add(item: "William") myCollection[0] The Collection protocol has default implementations for most of its methods, the Sequence protocols methods, and the IndexableBase protocols methods. This means you are only required to provide a few things of your own. You can, however, implement as many of the other methods as make sense for your collection. New Range and Associated Indices Types Swift 2’s Range<T>, ClosedInterval<T>, and OpenInterval<T> are going away in Swift 3. These types are being replaced with four new types. Two of the new range types support general ranges with bounds that implement the Comparable protocol: Range<T> and ClosedRange<T>. The other two range types conform to RandomAccessCollection. These types support ranges whose bounds implement the Strideable protocol. Last, ranges are no longer iterable since ranges are now represented as a pair of indices. To keep legacy code working, the Swift team introduced an associated indices type, which is iterable. In addition, three generic types were created to provide a default indices type for each type of collection traversal category. The generics are DefaultIndices<C>, DefaultBidirectionalIndices<C>, and DefaultRandomAccessIndices<C>; each stores its underlying collection for traversal. Quick Takeaways I covered a lot of stuff in a just a few pages on collection types in Swift 3. Here are the highlights to keep in mind about the collections and indices. Collections types (built-in and custom) implement the Collection protocol. Iterating over collections has moved to the collection—the index no longer has that ability. You can create your own collections by adopting the Collection protocol. You need to implement: startIndex and endIndex properties The subscript method to support access to your elements The index(after: ) method to facilitate traversing your collection’s indices Closure changes for Swift 3 A closure in Swift is a block of code that can be used in a function call as a parameter or assigned to a variable to execute their functionality at a later time. Closures are a core feature to Swift and are familiar to developers that are new to Swift as they remind you of lambda functions in other programming languages. For Swift 3, there were two notable changes that I will highlight in this section. The first change deals with inout captures. The second is a change that makes non-escaping closures the default. Limiting inout Capture of @noescape Closures]In Swift 2, capturing inout parameters in an escaping closure was difficult for developers to understand. Closures are used everywhere in Swift especially in the standard library and with collections. Some closures are assigned to variables and then passed to functions as arguments. If the function that contains the closure parameter returns from its call and the passed in closure is used later, then you have an escaping closure. On the other hand, if the closure is only used within the function to which it is passed and not used later, then you have a nonescaping closure. The distinction is important here because of the mutating nature of inout parameters. When we pass an inout parameter to a closure, there is a possibility that we will not get the result we expect due to how the inout parameter is stored. The inout parameter is captured as a shadow copy and is only written back to the original if the value changes. This works fine most of the time. However, when the closure is called at a later time (that is, when it escapes), we don’t get the result we expect. Our shadow copy can’t write back to the original. Let’s look at an example. var seed = 10 let simpleAdderClosure = { (inout seed: Int)->Int in seed += 1 return seed * 10 } var result = simpleAdderClosure(&seed) //=> 110 print(seed) // => 11 In the preceding example, we get what we expect. We created a closure to increment our passed in inout parameter and then return the new parameter multiplied by 10. When we check the value of seed after the closure is called, we see that the value has increased to 11. In our second example, we modify our closure to return a function instead of just an Int value. We move our logic to the closure that we are defining as our return value. let modifiedClosure = { (inout seed: Int)-> (Int)->Int in return { (Int)-> Int in seed += 1 return seed * 10 } } print(seed) //=> 11 var resultFn = modifiedClosure(&seed) var result = resultFn(1) print(seed) // => 11 This time when we execute the modifiedClosure with our seed value, we get a function as the result. After executing this intermediate function, we check our seed value and see that the value is unchanged; even though, we are still incrementing the seed value. These two slight differences in syntax when using inout parameters generate different results. Without knowledge of how shadow copy works, it would be hard understand the difference in results. Ultimately, this is just another situation where you receive more harm than good by allowing this feature to remain in the language. You can read the proposal at the following link https://github.com/apple/swift-evolution/blob/master/proposals/0035-limit-inout-capture.md. Resolution In Swift 3, the compiler now limits inout parameter usage with closures to non-escaping (@noescape). You will receive an error if the compiler detects that your closure escapes when it contains inout parameters. Making non-escaping closures the default [SE-0103] You can read the proposal at https://github.com/apple/swift-evolution/blob/master/proposals/0103-make-noescape-default.md. In previous versions of Swift, the default behavior of function parameters whose type was a closure was to allow escaping. This made sense as most of the Objective-C blocks (closures in Swift) imported into Swift were escaping. The delegation pattern in Objective-C, as implemented as blocks, was composed of delegate blocks that escaped. So, why would the Swift team want to change the default to non-escaping as the default? The Swift team believes you can write better functional algorithms with non-escaping closures. An additional supporting factor is the change to require non-escaping closures when using inout parameters with the closure [SE-0035]. All things considered, this change will likely have little impact on your code. When the compiler detects that you are attempting to create an escaping closure, you will get an error warning that you are possibly creating an escaping closure. You can easily correct the error by adding @escaping or via the fixit that accompanies the error. In Swift 2.2: var callbacks:[String : ()->String] = [:] func myEscapingFunction(name:String, callback:()->String){ callbacks[name] = callback } myEscapingFunction("cb1", callback: {"just another cb"}) for cb in callbacks{ print("name: (cb.0) value: (cb.1())") } In Swift 3: var callbacks:[String : ()->String] = [:] func myEscapingFunction(name:String, callback: @escaping ()->String){ callbacks[name] = callback } myEscapingFunction(name:"cb1", callback: {"just another cb"}) for cb in callbacks{ print("name: (cb.0) value: (cb.1())") } Summary In this article, we covered changes to collections and closures. You learned about the new Collection protocol that forms the base of the new collection model and how to adopt the protocol in our own custom collections. The new collection model made a significant change in moving collection traversal from the index to the collection itself. The new collection model changes are necessary in order to support Objective-C interactivity and to provide a mechanism to iterate over the collections items using the collection itself. As for closures, we also explored the motivation for the language moving to non-escaping closures as the default. You also learned how to properly use inout parameters with closures in Swift 3. Resources for Article: Further resources on this subject: Introducing the Swift Programming Language [article] Concurrency and Parallelism with Swift 2 [article] Exploring Swift [article]
Read more
  • 0
  • 0
  • 1282
article-image-string-management-in-swift
Jorge Izquierdo
21 Sep 2016
7 min read
Save for later

String management in Swift

Jorge Izquierdo
21 Sep 2016
7 min read
One of the most common tasks when building a production app is translating the user interface into multiple languages. I won't go into much detail explaining this or how to set it up, because there are lots of good articles and tutorials on the topic. As a summary, the default system is pretty straightforward. You have a file named Localizable.strings with a set of keys and then different values depending on the file's language. To use these strings from within your app, there is a simple macro in Foundation, NSLocalizedString(key, comment: comment), that will take care of looking up that key in your localizable strings and return the value for the user's device language. Magic numbers, magic strings The problem with this handy macro is that as you can add a new string inline, you will presumably end up with dozens of NSLocalizedStrings in the middle of the code of your app, resulting in something like this: mainLabel.text = NSLocalizedString("Hello world", comment: "") Or maybe, you will write a simple String extension for not having to write it every time. That extension would be something like: extension String { var localized: String { return NSLocalizedString(self, comment: "") } } mainLabel.text = "Hello world".localized This is an improvement, but you still have the problem that the strings are all over the place in the app, and it is difficult to maintain a scalable format for the strings as there is not a central repository of strings that follows the same structure. The other problem with this approach is that you have plain strings inside your code, where you could change a character and not notice it until seeing a weird string in the user interface. For that not to happen, you can take advantage of Swift's awesome strongly typed nature and make the compiler catch these errors with your strings, so that nothing unexpected happens at runtime. Writing a Swift strings file So that is what we are going to do. The goal is to be able to have a data structure that will hold all the strings in your app. The idea is to have something like this: enum Strings { case Title enum Menu { case Feed case Profile case Settings } } And then whenever you want to display a string from the app, you just do: Strings.Title.Feed // "Feed" Strings.Title.Feed.localized // "Feed" or the value for "Feed" in Localizable.strings This system is not likely to scale when you have dozens of strings in your app, so you need to add some sort of organization for the keys. The basic approach would be to just set the value of the enum to the key: enum Strings: String { case Title = "app.title" enum Menu: String { case Feed = "app.menu.feed" case Profile = "app.menu.profile" case Settings = "app.menu.settings" } } But you can see that this is very repetitive and verbose. Also, whenever you add a new string, you need to write its key in the file and then add it to the Localizable.strings file. We can do better than this. Autogenerating the keys Let’s look into how you can automate this process so that you will have something similar to the first example, where you didn't write the key, but you want an outcome like the second example, where you get a reasonable key organization that will be scalable as you add more and more strings during development. We will take advantage of protocol extensions to do this. For starters, you will define a Localizable protocol to make the string enums conform to: protocol Localizable { var rawValue: String { get } } enum Strings: String, Localizable { case Title case Description } And now with the help of a protocol extension, you can get a better key organization: extension Localizable { var localizableKey: String { return self.dynamicType.entityName + "." rawValue } static var entityName: String { return String(self) } } With that key, you can fetch the localized string in a similar way as we did with the String extension: extension Localizable { var localized: String { return NSLocalizedString(localizableKey, comment: "") } } What you have done so far allows you to do Strings.Title.localized, which will look in the localizable strings file for the key Strings.Title and return the value for that language. Polishing the solution This works great when you only have one level of strings, but if you want to group a bit more, say Strings.Menu.Home.Title, you need to make some changes. The first one is that each child needs to know who its parent is in order to generate a full key. That is impossible to do in Swift in an elegant way today, so what I propose is to explicitly have a variable that holds the type of the parent. This way you can recurse back the strings tree until the parent is nil, where we assume it is the root node. For this to happen, you need to change your Localizable protocol a bit: public protocol Localizable { static var parent: LocalizeParent { get } var rawValue: String { get } } public typealias LocalizeParent = Localizable.Type? Now that you have the parent idea in place, the key generation needs to recurse up the tree in order to find the full path for the key. rivate let stringSeparator: String = "." private extension Localizable { static func concatComponent(parent parent: String?, child: String) -> String { guard let p = parent else { return child.snakeCaseString } return p + stringSeparator + child.snakeCaseString } static var entityName: String { return String(self) } static var entityPath: String { return concatComponent(parent: parent?.entityName, child: entityName) } var localizableKey: String { return self.dynamicType.concatComponent(parent: self.dynamicType.entityPath, child: rawValue) } } And to finish, you have to make enums conform to the updated protocol: enum Strings: String, Localizable { case Title enum Menu: String, Localizable { case Feed case Profile case Settings static let parent: LocalizeParent = Strings.self } static let parent: LocalizeParent = nil } With all this in place you can do the following in your app: label.text = Strings.Menu.Settings.localized And the label will have the value for the "strings.menu.settings" key in Localizable.strings. Source code The final code for this article is available on Github. You can find there the instructions for using it within your project. But also you can just add the Localize.swift and modify it according to your project's needs. You can also check out a simple example project to see the whole solution together.  Next time The next steps we would need to take in order to have a full solution is a way for the Localizable.strings file to autogenerate. The solution for this at the current state of Swift wouldn't be very elegant, because it would require either inspecting the objects using the ObjC runtime (which would be difficult to do since we are dealing with pure Swift types here) or defining all the children of a given object explicitly, in the same way as open source XCTest does. Each test case defines all of its tests in a static property. About the author Jorge Izquierdo has been developing iOS apps for 5 years. The day Swift was released, he starting hacking around with the language and built the first Swift HTTP server, Taylor. He has worked on several projects and right now works as an iOS development contractor.
Read more
  • 0
  • 0
  • 3270

article-image-intro-swift-repl-and-playgrounds
Dov Frankel
11 Sep 2016
6 min read
Save for later

Intro to the Swift REPL and Playgrounds

Dov Frankel
11 Sep 2016
6 min read
When Apple introduced Swift at WWDC (its annual Worldwide Developers Conference) in 2014, it had a few goals for the new language. Among them was being easy to learn, especially compared to other compiled languages. The following is quoted from Apple's The Swift Programming Language: Swift is friendly to new programmers. It is the first industrial-quality systems programming language that is as expressive and enjoyable as a scripting language. The REPL Swift's playgrounds embody that friendliness by modernizing the concept of a REPL (Read-Eval-Print Loop, pronounced "repple"). Introduced by the LISP language, and now a common feature of many modern scripting languages, REPLs allow you to quickly and interactively build up your code, one line at a time. A post on the Swift Blog gives an overview of the Swift REPL's features, but this is what using it looks like (to launch it, enter swift in Terminal, if you have Xcode already installed): Welcome to Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29). Type :help for assistance.   1> "Hello world" $R0: String = "Hello World"   2> let a = 1 a: Int = 1   3> let b = 2 b: Int = 2   4> a + b $R1: Int = 3   5> func aPlusB() {   6.     print("(a + b)")   7. }   8> aPlusB() 3 If you look at what's there, each line containing input you give has a small indentation. A line that starts a new command begins with the line number, followed by > (1, 2, 3, 4, 5, and 8), and each subsequent line for a given command begins with the line number, followed by  (6 and 7). These help to keep you oriented as you enter your code one line at a time. While it's certainly possible to work on more complicated code this way, it requires the programmer to keep more state about the code in his head, and it limits the output to data types that can be represented in text only. Playgrounds Playgrounds take the concept of a REPL to the next level by allowing you to see all of your code in a single editable code window, and giving you richer options to visualize your data. To get started with a Swift playground, launch Xcode, and select File > New > Playground… (⌥⇧⌘N) to create a new playground. In the following, you can see a new playground with the same code entered into the previous  REPL:   The results are shown in the gray area on the right-hand side of the window update as you type, which allows for rapid iteration. You can write standalone functions, classes, or whatever level of abstraction you wish to work in for the task at hand, removing barriers that prevent the expression of your ideas, or experimentation with the language and APIs. So, what types of goals can you accomplish? Experimentation with the language and APIs Playgrounds are an excellent place to learn about Swift, whether you're new to the language, or new to programming. You don't need to worry about main(), app bundles, simulators, or any of the other things that go into making a full-blown app. Or, if you hear about a new framework and would like to try it out, you can import it into your playground and get your hands dirty with minimal effort. Crucially, it also blows away the typical code-build-run-quit-repeat cycle that can often take up so much development time. Providing living documentation or code samples Playgrounds provide a rich experience to allow users to try out concepts they're learning, whether they're reading a technical article, or learning to use a framework. Aside from interactivity, playgrounds provide a whole other type of richness: built-in formatting via Markdown, which you can sprinkle in your playground as easily as writing comments. This allows some interesting options such as describing exercises for students to complete or providing sample code that runs without any action required of the user. Swift blog posts have included playgrounds to experiment with, as does the Swift Programming Language's A Swift Tour chapter. To author Markdown in your playground, start a comment block with /*:. Then, to see the comment rendered, click on Editor > Show Rendered Markup. There are some unique features available, such as marking page boundaries and adding fields that populate the built-in help Xcode shows. You can learn more at Apple's Markup Formatting Reference page. Designing code or UI You can also use playgrounds to interactively visualize how your code functions. There are a few ways to see what your code is doing: Individual results are shown in the gray side panel and can be Quick-Looked (including your own custom objects that implement debugQuickLookObject()). Individual or looped values can be pinned to show inline with your code. A line inside a loop will read "(10 times)," with a little circle you can toggle to pin it. For instance, you can show how a value changes with each iteration, or how a view looks: Using some custom APIs provided in the XCPlayground module, you can show live UIViews and captured values. Just import XCPlayground and set the XCPlayground.currentPage.liveView to a UIView instance, or call XCPlayground.currentPage.captureValue(someValue, withIdentifier: "My label") to fill the Assistant view. You also still have Xcode's console available to you for when debugging is best served by printing values and keeping scrolled to the bottom. As with any Swift code, you can write to the console with NSLog and print. Working with resources Playgrounds can also include resources for your code to use, such as images: A .playground file is an OS X package (a folder presented to the user as a file), which contains two subfolders: Sources and Resources. To view these folders in Xcode, show the Project Navigator in Xcode's left sidebar, the same as for a regular project. You can then drag in any resources to the Resources folder, and they'll be exposed to your playground code the same as resources are in an app bundle. You can refer to them like so: let spriteImage = UIImage(named:"sprite.png") Xcode versions starting with 7.1 even support image literals, meaning you can drag a Resources image into your source code and treat it as a UIImage instance. It's a neat idea, but makes for some strange-looking code. It's more useful for UIColors, which allow you to use a color-picker. The Swift blog post goes into more detail on how image, color, and file literals work in playgrounds: Wrap-up Hopefully this has opened your eyes to the opportunities afforded by playgrounds. They can be useful in different ways to developers of various skill and experience levels (in Swift, or in general) when learning new things or working on solving a given problem. They allow you to iterate more quickly, and visualize how your code operates more easily than other debugging methods. About the author Dov Frankel (pronounced as in "he dove swiftly into the shallow pool") works on Windows in-house software at a Connecticut hedge fund by day, and independent Mac and iOS apps by night. Find his digital comic reader, on the Mac App Store, and his iPhone app Afterglo is in the iOS App Store. He blogs when the mood strikes him, at dovfrankel.com; he's @DovFrankel on Twitter, and @abbeycode on GitHub.
Read more
  • 0
  • 0
  • 2781