Learn what interstitial ads are and how to add one to your app.
The post Video Tutorial: Monetizing Your App with iAd Part 2: Intermediate iAd appeared first on Ray Wenderlich.
Learn what interstitial ads are and how to add one to your app.
The post Video Tutorial: Monetizing Your App with iAd Part 2: Intermediate iAd appeared first on Ray Wenderlich.
The “smart wearable” market, which includes the Apple Watch, is expected to reach worldwide sales of 53 billion US dollars in 2019. Meanwhile, studies using apps constructed with ResearchKit continue to access data from thousands of consented enrollees, turning consumers into participants and investigating some of our most important scientific mysteries.
One of these mysteries, at the intersection of wearable technology and research, is the heart. Many wearable devices can report heart rate data, providing a large dataset, and now you can tap into that dataset!
In this tutorial, you’ll learn how to easily access heart rate data from HealthKit and use it in a ResearchKit study. You’ll build an app which can:
Along the way, you’ll undoubtedly see some possibilities for customization, so building a similar app for another study will be straightforward.
Download and unzip the starter project for this tutorial and open Karenina.xcodeproj in Xcode. Build and run, and you will see a simple main menu that looks like the following:
The project will look familiar if you’ve completed ResearchKit Tutorial with Swift: Getting Started; it’s the finished sample project from that tutorial and includes ResearchKit and several basic tasks.
The project has been renamed Karenina, and you will expand this project to include two new Active Tasks. To refresh your memory, Active Tasks are HealthKit tasks that collect data while guiding the user through some type of activity.
To access heart rate data, your app needs the HealthKit capability. To add this, first navigate to the General tab for the Karenina target and update the Bundle Identifier to a unique value. Next, make sure that the Team field contains your iOS Developer account.
With a valid team chosen and Bundle Identifier set, select the Capabilities tab, scroll down to HealthKit, and turn the associated switch on, as shown below:
Xcode has kindly added the HealthKit framework to your project and enabled the correct entitlement. If you run into any problems here, they are likely with the team you chose and the associated iOS Developer Account.
Building your app is going to be difficult without a way to test it, and going for a walk every time you build and run would be tiring. You need a way to simulate heart rate data from HealthKit.
First, you need to gain permission to access HealthKit data. Create a new Swift file named HealthKitManager.swift and replace its contents with the following:
import Foundation import HealthKit class HealthKitManager: NSObject { static let healthKitStore = HKHealthStore() static func authorizeHealthKit() { let healthKitTypes: Set = [ HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!, HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)! ] healthKitStore.requestAuthorizationToShareTypes(healthKitTypes, readTypes: healthKitTypes) { _, _ in } } } |
After importing HealthKit, you create a HealthKitManager
class with a method authorizeHealthKit()
for obtaining user permission to read and write certain HealthKit quantity types. Here, the quantity types you care about are the user’s heart rate (HKQuantityTypeIdentifierHeartRate
) and distance walked ( HKQuantityTypeIdentifierDistanceWalkingRunning
). You create a Set
with these quantity types and pass them to requestAuthorizationToShareTypes:readTypes:completion:
which will present the user with a prompt.
Now you need a button to trigger the HealthKit authorization. Open ViewController.swift and add the following method to the ViewController
class:
@IBAction func authorizeTapped(sender: AnyObject) { HealthKitManager.authorizeHealthKit() } |
Then open Main.storyboard, add a UIButton with the title Authorize and connect it to your authorizeTapped(_:)
action:
Optionally, add some Auto Layout constraints to align the button to the other buttons. If you don’t know how to do this, don’t worry as it’s not critical for this tutorial – but if you’re curious you can learn how to do so here.
Build and run your app; tap the Authorize button and you’ll see the Health Access screen, where your users can grant your app access to Health data:
Turn on all the options and tap Allow, and your ResearchKit tasks are now able to access data from HealthKit!
For testing purposes, you can verify the settings at any given time by navigating to the Settings app, then to Privacy\Health\Karenina.
Now you’re able to create mock heart rate data and save it to HealthKit. To do that, open HealthKitManager.swift and add the following method to the class:
static func saveMockHeartData() { // 1. Create a heart rate BPM Sample let heartRateType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)! let heartRateQuantity = HKQuantity(unit: HKUnit(fromString: "count/min"), doubleValue: Double(arc4random_uniform(80) + 100)) let heartSample = HKQuantitySample(type: heartRateType, quantity: heartRateQuantity, startDate: NSDate(), endDate: NSDate()) // 2. Save the sample in the store healthKitStore.saveObject(heartSample, withCompletion: { (success, error) -> Void in if let error = error { print("Error saving heart sample: \(error.localizedDescription)") } }) } |
Let’s review this section by section:
saveMockHeartData()
, you specify that your sample will be a heart rate sample. Then, you use a random measurement from 100-180 beats per minute (BPM) and set the measurement time to be the current NSDate()
.saveObject(_:withCompletion:)
, and print any error encountered.Lastly, you use a timer to continuously trigger your mock data. Add the following property to the top of HealthKitManager
:
static var timer: NSTimer? |
Then add the following two methods to start and stop the timer:
static func startMockHeartData() { timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "saveMockHeartData", userInfo: nil, repeats: true) } static func stopMockHeartData() { self.timer?.invalidate() } |
That wraps it up for HealthKitManager! Now that you’ve authorized HealthKit and set it up to provide mock heart rate data, you can start to define ResearchKit tasks that use this data.
ResearchKit includes a pre-made task to read health data while the user walks and rests for set periods of time. Active and resting heart rates hold key importance in heart studies, and now you can easily measure them.
Create a blank Swift file named WalkTask.swift and add the following:
import ResearchKit public var WalkTask: ORKOrderedTask { return ORKOrderedTask.fitnessCheckTaskWithIdentifier("WalkTask", intendedUseDescription: nil, walkDuration: 15 as NSTimeInterval, restDuration: 15 as NSTimeInterval, options: .None) } |
Here, you create a new ORKOrderedTask
, which you should find familiar after building several in the previous ResearchKit tutorial.
You create this particular task with fitnessCheckTaskWithIdentifier(_:intendedUseDescription:walkDuration:restDuration:options:)
, which you supply with a descriptive string identifier (WalkTask
) and the durations (in seconds) of the walking and resting segments. ResearchKit will take it from there and create your task for you.
Next, open ViewController.swift and add the following IBAction:
@IBAction func walkTapped(sender: AnyObject) { let taskViewController = ORKTaskViewController(task: WalkTask, taskRunUUID: nil) taskViewController.delegate = self taskViewController.outputDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0], isDirectory: true) presentViewController(taskViewController, animated: true, completion: nil) HealthKitManager.startMockHeartData() } |
Here, you create an ORKTaskViewController
for your new fitness task, set this view controller as the delegate, specify an output directory for any result files (of which there will be several), and present the view controller. This should be familiar, as it’s how you presented MicrophoneTask in the previous ResearchKit tutorial.
The only new aspect is that after doing all this, you start mocking the heart data. And since you are starting the mock data, you should stop it as well.
Find taskViewController(_:didFinishWithReason:error:)
– this is a ORKTaskViewControllerDelegate
protocol method that gets called when taskViewController
is dismissed. Stop the mock heart data at that point by adding the following line to the top of the method:
HealthKitManager.stopMockHeartData() |
As you may recall from earlier, stopMockHeartData()
stops the repeating timer you started to generate mock data every second.
Lastly, you need a UI trigger for this IBAction. Open Main.storyboard, add a UIButton entitled “Walk,” and connect it to walkTapped(_:).
Again, optionally add some Auto Layout constraints to align the button to the other buttons.
Build and run your app; tap on Walk and you’ll be led through a short exercise task, during which you’ll see your (unrealistically-widely varying) mock heart rate data:
WARNING: 997: Failure to setup sound, err = -50
You can complete the task now, but what happens to the results? Time to find them.
After completing the walking task, you’ll receive an ORKTaskResult
that contains the results of every step. You’ll get to that in a moment, but first you need to create something to parse that data. Create a new file named ResultParser.swift, and replace its contents with the following:
import Foundation import ResearchKit struct ResultParser { static func findWalkHeartFiles(result: ORKTaskResult) -> [NSURL] { var urls = [NSURL]() if let results = result.results where results.count > 4, let walkResult = results[3] as? ORKStepResult, let restResult = results[4] as? ORKStepResult { // TODO: find ORKFileResults } return urls } } |
findWalkHeartFiles(_:)
accepts an ORKTaskResult
that contains the results of every step in your walk task. Note as you are completing the walking test that the top of the walking step says Step 4 of 6; this is your sign that the fourth member – results[3]
– holds the results for that step.
Likewise, the resting step is fifth and thus its results are at results[4]
. At each of these positions is an ORKStepResult
which itself may contain a number of ORKFileResult
objects with the date for that step.
To parse out each ORKFileResult
and find the file URL within, replace the following:
// TODO: find ORKFileResults |
…with the code below:
for result in walkResult.results! { if let result = result as? ORKFileResult, let fileUrl = result.fileURL { urls.append(fileUrl) } } for result in restResult.results! { if let result = result as? ORKFileResult, let fileUrl = result.fileURL { urls.append(fileUrl) } } |
You cycle through the results for the two steps of interest and add the fileUrl
of each result to an array. The fileUrl
points to a JSON file containing the results of the associated step reading. In this case, these results will contain measurements for heart rate in BPM during both the walk and rest phases.
This may seem like a lot of nesting to find the data you like, but the system will become familiar once you get used to digging into the task results. You will see this again when you get the results of your own custom task.
This function takes in an ORKTaskResult and returns an array of file URLs, but where does the ORKTaskResult come from?
To find it, open ViewController.swift and locate taskViewController(_:didFinishWithReason:error:)
. Every ORKTaskViewController
contains a result
property – that’s what you want. Send it to your parser by adding the following code just below HealthKitManager.stopMockHeartData()
at the top of the method:
if (taskViewController.task?.identifier == "WalkTask" && reason == .Completed) { let heartURLs = ResultParser.findWalkHeartFiles(taskViewController.result) for url in heartURLs { do { let string = try NSString.init(contentsOfURL: url, encoding: NSUTF8StringEncoding) print(string) } catch {} } } |
Here, you check to see if the task is indeed the WalkTask
, and that it completed successfully. Then, you use your ResultParser
to get the series of file URLs for the step results. Finally, you print the contents of each file to the console.
Build an run your app; tap the Walk button, complete the activity again, then tap the Done button. Look at the console in Xcode, and you’ll notice the printed output in JSON format. If you want to send the results to a server, you could do so without any further serialization!
Below is a small piece of data in the format you will see in your console:
{"items":[{"unit":"count\/min","type":"HKQuantityTypeIdentifierHeartRate","endDate":"2015-10-07T22:22:04-0700","source":"Karenina","value":138,"startDate":"2015-10-07T22:22:04-0700"},{"unit":"count\/min","type":"HKQuantityTypeIdentifierHeartRate","endDate":"2015-10-07T22:22:05-0700","source":"Karenina","value":129,"startDate":"2015-10-07T22:22:05-0700"}]} |
As you can see, you have the value, units, and date of every heart rate reading. You’ll notice one array that correlates to the walk step, and another for the rest step – although you haven’t done anything here to flag which is which.
That’s it for the walking task. Now it’s time to build your own task.
Research into music and its effects on our minds and bodies is a fascinating field; you’ll build a sample task to gather data to test whether a random music clip affects a user’s heart rate.
Start by downloading this archive of a few short music clips, and drag them into your Xcode project. Make sure that Copy items if needed, Create groups, and the Karenina target are selected:
Next, you need a way for your app to easily choose and access a random song file.
Create a file named MusicClip.swift and replace its contents with the following enum:
import Foundation enum MusicClip: String { case Chill3 = "chill_preview_3" case Chill4 = "chill_preview_4" case Dark4 = "dark_preview_4" case Happy1 = "happy_preview_1" case Light2 = "light_preview_2" case Light3 = "light_preview_3" static func random() -> MusicClip { switch arc4random_uniform(6) { case 0: return .Chill3 case 1: return .Chill4 case 2: return .Dark4 case 3: return .Happy1 case 4: return .Light2 default: return .Light3 } } func fileURL() -> NSURL { return NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(self.rawValue, ofType: "mp3")!) } } |
The MusicClip
enum contains a case
for each clip that provides its filename. random()
returns a random clip, and fileURL()
provides the exact mp3 file URL for a given clip.
Next, you need to think about the step that will eventually play the music clip. In the fitness check task you implemented earlier, the task defined the duration of the walking and resting steps and knew which types of data to collect during the steps. In this case, you’ll need to create a custom task that plays a specific clip for a set duration.
Luckily, there’s a base class in ResearchKit that lets you set a specific duration: ORKActiveStep. For the music step, the only customization you need to add to this class is a MusicClip
property.
Create a new file named MusicStep.swift and subclass ORKActiveStep with a property for the music clip, like this:
import ResearchKit class MusicStep: ORKActiveStep { var clip: MusicClip! } |
That handles the step, but how will the task know when to start and stop the music clip? That will be handled by the view controller that displays this step.
ResearchKit comes with a custom view controller that handles the display of a step: ORKActiveStepViewController
. You have actually already been using this class behind the scenes for your MicrophoneTask and WalkTask. These predefined tasks come with predefined steps and view controllers – subclasses of ORKActiveStep
and ORKActiveStepViewController
.
Now that you’re delving into the realm of custom steps, you’ll need a custom subclass of ORKActiveStepViewController
too. Create a new file named MusicStepViewController.swift and add the class below:
import AVFoundation import ResearchKit class MusicStepViewController: ORKActiveStepViewController { var audioPlayer: AVAudioPlayer? override func start() { super.start() if let step = step as? MusicStep { do { try audioPlayer = AVAudioPlayer(contentsOfURL: step.clip.fileURL(), fileTypeHint: AVFileTypeMPEGLayer3) audioPlayer?.play() } catch {} } } override func stepDidFinish() { super.stepDidFinish() audioPlayer?.stop() } } |
In addition to the usual view controller life cycle methods like viewDidLoad()
, an ORKActiveStepViewController
keeps track of the life cycle of the step itself. You override start()
and stepDidFinish()
to perform additional actions at those points in the step’s life cycle.
Here, those additional actions are to play the mp3 clip contained in the step when it starts, and to stop playing when the step is over. You call the super
functions as well to let the default implementation complete its work.
Next, it’s time to create a subclass of ORKOrderedTask
to display instructions, play the music clip and thank the user for participating. In the previous ResearchKit tutorial, you created a ConsentTask
and SurveyTask
in this way, so the general process should be familiar.
Create a new file named MusicTask.swift, and add the following class:
import ResearchKit public var MusicTask: ORKOrderedTask { var steps = [ORKStep]() let instructionStep = ORKInstructionStep(identifier: "instruction") instructionStep.title = "Music + Heart Rate" instructionStep.text = "Please listen to a randomized music clip for 30 seconds, and we'll record your heart rate." steps += [instructionStep] // TODO: add recorder configuration // TODO: add Music Step let summaryStep = ORKCompletionStep(identifier: "SummaryStep") summaryStep.title = "Thank you!" summaryStep.text = "You have helped us research music and heart rate!" steps += [summaryStep] return ORKOrderedTask(identifier: "MusicTask", steps: steps) } |
Here, you’ve created the task and added the introduction and summary steps, leaving a spot in the middle to add the step to play the music clip. But what is this TODO
item to add the recorder configuration all about?
An ORKRecorder runs during the step to record data and save it to an output directory. The type of data it records is up to an ORKRecorderConfiguration
, which specifies the desired quantity type and unit.
Replace the following line:
// TODO: add recorder configuration |
…with the code below:
let configuration = ORKHealthQuantityTypeRecorderConfiguration(identifier: "heartRateConfig", healthQuantityType: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!, unit: HKUnit(fromString: "count/min")) |
The quantity type and unit for this configuration are the same as what you used in saveMockHeartData()
, so the recorder will pick up all the mock data as it comes in.
Next, configure the music step by replacing the code below:
// TODO: add Music Step |
with the following:
let musicStep = MusicStep(identifier: "music") musicStep.clip = MusicClip.random() musicStep.stepDuration = 30 musicStep.recorderConfigurations = [configuration] musicStep.shouldShowDefaultTimer = true musicStep.shouldStartTimerAutomatically = true musicStep.shouldContinueOnFinish = true musicStep.title = "Please listen for 30 seconds." steps += [musicStep] |
You create the music step just as you do any other step: by supplying an identifier string. Then you assign a random music clip to the step, set the timer duration to 30 seconds and supply the heart rate recorder configuration you initialized in the previous code block. Note that the configuration is wrapped in an array; you can supply more than one configuration if you want to read several data types.
The remaining properties are common to all subclasses of ORKActiveStep
and allow for some quick and easy customization behavior. I encourage you to experiment with these and other properties which you can find in the class reference.
Now that you have created your music task, it’s time to do the familiar work to trigger the task in your UI. Navigate back to ViewController.swift and add the following method:
@IBAction func musicTapped(sender: AnyObject) { let taskViewController = ORKTaskViewController(task: MusicTask, taskRunUUID: nil) taskViewController.delegate = self taskViewController.outputDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0], isDirectory: true) presentViewController(taskViewController, animated: true, completion: nil) HealthKitManager.startMockHeartData() } |
Here you’ve created and presented a ORKTaskViewController
, while immediately starting your mock heart rate data just as you did for the walk task.
Finally, add a UIButton to Main.storyboard with the title Music, and connect it to musicTapped(_:)
.
Again, optionally add some Auto Layout constraints to align the button to the other buttons.
Build and run your app; tap Music to see your new task at work:
All your steps are there visually, but wait! There’s no music to listen to!
MusicStepViewController is in charge of playing the music clips, but you haven’t connected it yet. What you’re seeing is simply a generic ORKActiveStepViewController
, which can handle the title and countdown, but not the music playback.
To associate your MusicStep and MusicStepViewController, you turn to the ORKTaskViewControllerDelegate
protocol and its taskViewController(_:viewControllerForStep:)
method. Your ViewController class already implements this protocol, and implementing this method lets you supply a custom view controller for a given step.
Copy the following method into ViewController.swift:
func taskViewController(taskViewController: ORKTaskViewController, viewControllerForStep step: ORKStep) -> ORKStepViewController? { if step.identifier == "music" { return MusicStepViewController(step: step) } else { return nil } } |
If the step identifier is music, as you defined in MusicTask, then you create and return a new MusicStepViewController. In any other case, you return nil
.
nil
here doesn’t actually mean “don’t use a view controller.” It means “use the default view controller you would’ve used anyway.”
Build and run your app, and listen for a random music clip to play:
Great! You now have an app with a custom task, reading heart rate during a music clip. You’ve parsed out the data from a walking task, and now it’s time to do the same for your custom task. And while you’re at it, wouldn’t it be nice to associate that heart rate data with the music clip played?
Open ResultParser.swift and add the following method to find the heart rate data:
static func findMusicHeartFiles(result: ORKTaskResult) -> NSURL? { if let results = result.results where results.count > 1, let heartResult = results[1] as? ORKStepResult, let heartSubresults = heartResult.results where heartSubresults.count > 0, let fileResult = heartSubresults[0] as? ORKFileResult, let fileURL = fileResult.fileURL { return fileURL } return nil } |
Underneath all this unwrapping, you retrieve the file URL associated with the music step. This file houses the heart rate data in JSON format.
Next, add the following method to find the music clip:
static func findClip(task: ORKTask?) -> MusicClip? { if let task = task as? ORKOrderedTask where task.steps.count > 1, let musicStep = task.steps[1] as? MusicStep { return musicStep.clip } else { return nil } } |
This time, your input parameter is ORKTask?
, not ORKTaskResult
. You use ORKTask?
as the music clip is a property of the MusicStep
, which you will access via the MusicTask rather than the task results.
With those methods ready, go back to ViewController.swift and find taskViewController(_:didFinishWithReason:error:)
. Remember that this method is called when an ORKTaskViewController
finishes, at which point you can access results of the view controller.
Add the following to the top of the method, just below HealthKitManager.stopMockHeartData()
:
if (taskViewController.task?.identifier == "MusicTask" && reason == .Completed) { let clip = ResultParser.findClip(taskViewController.task) print("clip name: \(clip!.rawValue)") let heartURL = ResultParser.findMusicHeartFiles(taskViewController.result) if let heartURL = heartURL { do { let string = try NSString.init(contentsOfURL: heartURL, encoding: NSUTF8StringEncoding) print(string) } catch {} } } |
If the finishing view controller is for the MusicTask
, you send that task to the ResultParser
to find the clip used and print the clip’s name to the console.
Next, you use the ResultParser
to find the generated file with heart rate data. As you did earlier, you print the JSON contents to the console so that you can easily review the results of your work.
Build and run your app; run the music task again and this time you’ll see results like the following printed to the console when you’re done:
clip name: chill_preview_4 {"items":[{"unit":"count\/min","type":"HKQuantityTypeIdentifierHeartRate","endDate":"2015-10-08T22:53:59-0700","source":"Karenina","value":150,"startDate":"2015-10-08T22:53:59-0700"},{"unit":"count\/min","type":"HKQuantityTypeIdentifierHeartRate","endDate":"2015-10-08T22:54:00-0700","source":"Karenina","value":129,"startDate":"2015-10-08T22:54:00-0700"}]} |
With the clip name and associated heart results readily available, you’ll be able to upload this to your server and start analyzing the data! Who knows what relationships or correlations you might find?
You can download the completed project for this tutorial here.
I encourage you to experiment with other properties of ORKActiveStep
in this project to see easy it can be to customize a Step within ResearchKit. Check out the full list in the RestKit documentation here.
If you need a refresher on working with JSON in Swift, see Working with JSON in Swift. You’ll likely be doing quite a bit more of this in your ResearchKit apps.
For more information on ResearchKit, check the official site, GitHub repository, and official ResearchKit forum. Also, Apple maintains a ResearchKit blog and presented about ResearchKit at WWDC 2015.
Beyond the framework, you can look into useful extensions to ResearchKit in the form of CardioHealth, an app developed with Stanford to study cardiovascular disease, and AppCore, which is shared code among all the launch apps. I recommend you take a look at how tasks are created in those projects, and use that behavior as a model.
Thanks for reading; please comment below, and I hope to see more ResearchKit success stories!
The post Accessing Heart Rate Data for Your ResearchKit Study appeared first on Ray Wenderlich.
Learn how ad mediation works and how to set up your AdMob account so you're ready to receive ads.
The post Video Tutorial: Monetizing Your App with iAd Part 3: Ad Mediation with Google AdMob appeared first on Ray Wenderlich.
Update note: This tutorial was updated to iOS 9 and Swift 2 by Andy Pereira. Original post by Tutorial Team Member Nicolas Martin.
If your app displays large datasets, scrolling through massive lists becomes slow and frustrating. In that case, it’s vitally important to allow users to search for specific items. Lucky for you, UIKit includes UISearchBar
which seamlessly integrates with UITableView
and allows for quick, responsive filtering of information.
In this UISearchController
tutorial, you’ll build a searchable Candy app which is based on a standard table view. You’ll add table view search capability, including dynamic filtering, and add an optional scope bar, all while taking advantage of UISearchController
, added in iOS 8. In the end, you’ll know how to make your apps much more user friendly and satisfy your users’ urgent demands.
Ready for some sugar-coated search results? Read on.
Download the starter project from the tutorial here and open the project. The app has already been set up with a navigation controller and styling. Build and run the app; you’ll see an empty list:
Back in Xcode, the file Candy.swift contains a class to store the information about each piece of candy you’ll be displaying. This class has two properties: the category and name of the candy.
When the user searches for a candy in your app, you’ll be referencing the name property against the user’s search string. You’ll see how the category string will become important near the end of this tutorial when you implement the Scope Bar.
Open MasterViewController.swift. The candies
property will be where you manage all the different Candy
objects for your users to search. Speaking of which, it’s time to create some Candy!
In this tutorial, you only need to create a limited number of values to illustrate how the search bar works; in a production app, you might have thousands of these searchable objects. But whether an app has thousands of objects to search or just a few, the methods used will remain the same. Scalability at its finest!
To populate your candies
array, add the following code to viewDidLoad()
, after the call to super.viewDidLoad()
:
candies = [ Candy(category:"Chocolate", name:"Chocolate Bar"), Candy(category:"Chocolate", name:"Chocolate Chip"), Candy(category:"Chocolate", name:"Dark Chocolate"), Candy(category:"Hard", name:"Lollipop"), Candy(category:"Hard", name:"Candy Cane"), Candy(category:"Hard", name:"Jaw Breaker"), Candy(category:"Other", name:"Caramel"), Candy(category:"Other", name:"Sour Chew"), Candy(category:"Other", name:"Gummi Bear") ] |
Go ahead and build and run your project again. Since the table view’s delegate and datasource methods have already been implemented, you’ll see that you now have a working table view:
Selecting a row in the table will also display a detail view of the corresponding candy:
So much candy, so little time to find what you want! You need a UISearchBar
.
If you look at the UISearchController
documentation, you’ll discover it’s pretty lazy. It doesn’t do any of the work of searching at all. The class simply provides a standard interface that users have come to expect from their iOS apps.
UISearchController
communicates with a delegate protocol to let the rest of your app know what the user is doing. You have to write all of the actual functionality for string matching yourself.
Although this may seem a tad scary at first, writing custom search functions gives you tight control over how results are returned specifically in your app. Your users will appreciate searches that are intelligent — and fast.
If you’ve worked with searching table views iOS in the past, you may be familiar with UISearchDisplayController
. Since iOS 8, this class has been deprecated in favor of UISearchController
, which simplifies the entire search process.
Unfortunately, at the time of this writing, Interface Builder does not support UISearchController
, so you’ll have to create your UI programmatically.
In MasterViewController.swift, add a new property:
let searchController = UISearchController(searchResultsController: nil) |
By initializing UISearchController
without a searchResultsController
, you are telling the search controller that you want use the same view that you’re searching to display the results. If you specify a different view controller here, that will be used to display the results instead.
Next, you’ll need to set up a few parameters for your searchController. Still in MasterViewController.swift, add the following to viewDidLoad()
:
searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false definesPresentationContext = true tableView.tableHeaderView = searchController.searchBar |
Here’s a rundown of what you added:
searchResultsUpdater
is a property on UISearchController
that conforms to the new protocol UISearchResultsUpdating
. This protocol allows your class to be informed as text changes within the UISearchBar
. You’ll make the class conform to the protocol in a short while.UISearchController
will dim the view it is presented over. This is useful if you are using another view controller for searchResultsController
. In this instance, you have set the current view to show the results, so you do not want to dim your view.definesPresentationContext
on your view controller to true
, you ensure that the search bar does not remain on the screen if the user navigates to another view controller while the UISearchController
is active.searchBar
to your table view’s tableHeaderView
. Remember that Interface Builder is not yet compatible with UISearchController
, making this necessary.After setting up the search controller, you’ll need to do some coding work to get it working. First, add the following property near the top of MasterViewController
:
var filteredCandies = [Candy]() |
This property will hold the candies that the user is searching for. Next, add the following helper method to the main MasterViewController
class:
func filterContentForSearchText(searchText: String, scope: String = "All") { filteredCandies = candies.filter { candy in return candy.name.lowercaseString.containsString(searchText.lowercaseString) } tableView.reloadData() } |
This filters the candies
array based based on searchText
and will put the results in the filteredCandies
array you just added. Don’t worry about the scope
parameter for now, you’ll use that in a later section of this tutorial.
To allow MasterViewController
to respond to the search bar, it will have to implement UISearchResultsUpdating
. Open MasterViewController.swift and add the following class extension, outside of the main MasterViewController
class:
extension MasterViewController: UISearchResultsUpdating { func updateSearchResultsForSearchController(searchController: UISearchController) { filterContentForSearchText(searchController.searchBar.text!) } } |
The updateSearchResultsForSearchController(_:)
method is the one and only method that your class must implement to conform to the UISearchResultsUpdating
protocol.
Now, whenever the user adds or removes text in the search bar, the UISearchController
will inform the MasterViewController
class of the change via this method. The method itself simply calls a helper method (which you’ll define soon) with the text currently in the search bar.
filter()
takes a closure of type (candy: Candy) -> Bool
. It then loops over all the elements of the array, and calls the closure, passing in the current element, for every one of the elements.
You can use this to determine whether a candy should be part of the search results that are presented to the user. To do so, you need to return true
if the current candy is to be included in the filtered array, or false
if not.
To determine this, containsString(_:)
is used to check to see if the name of the candy contains searchText
. But before doing the comparison, you convert both strings to their lowercase equivalents using the lowercaseString
method.
Build and run the app now; you’ll notice that there is now a Search Bar above the table.
However, if you enter some search text you still don’t see any filtered results. What gives? This is simply because you haven’t yet written the code to let the table view know when to use the filtered results.
Back in MasterViewController.swift, replace tableView(_:numberOfRowsInSection:)
with the following:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if searchController.active && searchController.searchBar.text != "" { return filteredCandies.count } return candies.count } |
Not much has changed here, you simply check whether the user is searching or not, and use either the filtered or normal candies as a data source for the table.
Next, replace tableView(_:cellForRowAtIndexPath:)
with:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) let candy: Candy if searchController.active && searchController.searchBar.text != "" { candy = filteredCandies[indexPath.row] } else { candy = candies[indexPath.row] } cell.textLabel?.text = candy.name cell.detailTextLabel?.text = candy.category return cell } |
Both methods now refer to the active
property of searchController
to determine which array to display.
When the user clicks in the search field of the Search Bar, active
will automatically be set to true
. If the search controller is active, you then see if the user has actually typed something into the search field. If they have, the data returned is taken from the filteredCandies
array. Otherwise, the data comes from the full list of items.
Recall that the search controller automatically handles showing and hiding the results table, so all your code has to do is provide the correct data (filtered or non-filtered) depending on the state of the controller and whether the user has searched for anything.
Build and run the app. You’ve got a functioning Search Bar that filters the rows of the main table. Huzzah!
Play with the app for a bit to see how you can search for various candies.
There’s still one more problem. When you select a row from the search results list, you may notice that the detail view shown can be of the wrong candy! Time to fix that.
When sending information to a detail view controller, you need to ensure that the view controller knows which context the user is working with: the full table list, or the search results. Still in MasterViewController.swift, in prepareForSegue(_:sender:)
, find the following code:
let candy = candies[indexPath.row] |
Then replace it with the following:
let candy: Candy if searchController.active && searchController.searchBar.text != "" { candy = filteredCandies[indexPath.row] } else { candy = candies[indexPath.row] } |
Here you performed the same check that tableView(_:numberOfRowsInSection:)
and tableView(_:cellForRowAtIndexPath:)
do, but now you’re providing the proper candy object when performing a segue to the detail view controller.
Build and run the code at this point and see how the app now navigates correctly to the Detail View from either the main table or the search table with ease.
If you wish to give your users another way to filter their results, you can add a Scope Bar in conjunction with your search bar in order to filter out items by their category. The categories you will filter on are the ones you assigned to the Candy
object when you created the candies array: Chocolate, Hard, and Other.
First, you have to create a scope bar in MasterViewController
. The scope bar is a segmented control that narrows down a search by only searching in certain scopes. A scope is really what you define it as. In this case it’s a candy’s category, but scopes could also be types, ranges, or something completely different.
Using the scope bar is as easy as implementing one additional delegate method.
In MasterViewController.swift, you’ll need to add another extension that conforms to UISearchBarDelegate
. Add the following after your UISearchResultsUpdating
extension:
extension MasterViewController: UISearchBarDelegate { func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope]) } } |
This delegate methods gets called when the user switches the scope in the scope bar. When that happens, you want to redo the filtering, so you call filterContentForSearchText(_:scope:)
with the new scope.
Now modify filterContentForSearchText(_:scope:)
to take the supplied scope into account:
func filterContentForSearchText(searchText: String, scope: String = "All") { filteredCandies = candies.filter { candy in let categoryMatch = (scope == "All") || (candy.category == scope) return categoryMatch && candy.name.lowercaseString.containsString(searchText.lowercaseString) } tableView.reloadData() } |
This now checks to see if the scope provided is either set to “All” or it matches the category of the candy. Only when this is the case is the candy name tested to see if the candy should be added to the filteredCandies
.
You’re almost there, but the scope filtering mechanism doesn’t quite work yet. You’ll need to modify updateSearchResultsForSearchController(_:)
in the first class extension you created to send the currently selected scope:
func updateSearchResultsForSearchController(searchController: UISearchController) { let searchBar = searchController.searchBar let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex] filterContentForSearchText(searchController.searchBar.text!, scope: scope) } |
The only problem left is that the user doesn’t actually see a scope bar yet! To add the scope bar, navigate to MasterViewController.swift, after the search controller setup in viewDidLoad()
, and add the following code:
searchController.searchBar.scopeButtonTitles = ["All", "Chocolate", "Hard", "Other"] searchController.searchBar.delegate = self |
This will add a scope bar to the search bar, with the titles that match the categories you assigned to your candy objects. You also include a catch-all category called “All” that you will use to ignore the candy category in the search altogether.
Now, when you type, the selected scope button will be used in conjunction with the search text.
Build and run your app. Try entering some search text, and changing the scope.
Type in “caramel” with the scope set to All. It shows up in the list, but when you change the scope to Chocolate, “caramel” disappears because it’s not a chocolate.
Congratulations – you now have a working app that allows you to search directly from the main table view. Here is a downloadable sample project with all of the code from the above tutorial.
Table views are used in all kinds of apps, and offering a search option is a nice touch for usability. With UISearchBar
and the UISearchController
, iOS provides much of the functionality out of the box so there’s no excuse for not using it. The ability to search a large table view is something that today’s users expect; when they find it isn’t present, they won’t be happy campers.
I hope to see you adding search capabilities to your table view apps in the future. If you have any questions or comments, please join the forum discussion below.
The post UISearchController Tutorial: Getting Started appeared first on Ray Wenderlich.
Learn how to add banner and interstitial ads to your app to display ads from the Google AdMob network.
The post Video Tutorial: Monetizing Your App with iAd Part 4: Integrating Google AdMob appeared first on Ray Wenderlich.
If you are not already familiar with the prerequisites for this video series, please check out our Auto Layout and Stack Views video series first before watching this video tutorial series.
The post Video Tutorial: Adaptive Layout: Series Introduction appeared first on Ray Wenderlich.
Christmas is near, but we have one final feast for you before the end of the year!
This site has become well known for iOS tutorials, but this week we are turning to the dark side. Enter the Android Feast!
Every day this week they will be releasing at least one new free Android tutorial on this site, written by our new Android team.
If you’ve ever wanted to get into Android development, now’s your chance! Here’s a peek at the menu for the upcoming week:
Let’s take a closer look at what’s inside!
If you’re completely new to Android, don’t worry – we have a tutorial for you!
The first course of the Android Feast is a two-part introduction to Android for complete beginners. It covers how to download and install Android Studio, and then move on and build your very first Android app.
Along the way you’ll discover how to build layouts, run your app on a real device and how to debug it when it all goes wrong. Get ready to go Android!
Once you get a basic “Hello, Android” app under your belt, it’s time to look more into the language you use to write apps on Android – Java.
In the second course of the Android feast, you’ll learn the basics of Java, and in particular the quirks of using Java for Android development.
This is definitely worth sticking around for—there’s even a take-home cheat-sheet coming your way!
Following on smoothly from the Java language tutorial, the third course takes a look at how you can apply your new-found knowledge to construct the logic of your Android app.
Whenever you write software, you’ll find yourself using the same techniques and approaches over and over again. These are called design patterns—and in this tutorial you’ll learn some of the important ones commonly used in Android app development.
You’re now fully equipped to take on the world of Android app development, so it’s that point in the meal where the action really begins.
Activities are integral to an Android app—they’re responsible for presenting the UI to the user and handling interactions. The first tutorial briefly touches on activities, but here’s where you can really sink your teeth into them.
The fourth course in the Android Feast is a crash-course into what activities are capable of, and how you can use them. You’ll discover how to navigate between activities, manage data associated with activities and about the activity lifecycle, as you build yourself a handy todo app.
One of the common complaints towards Android is that the device market is severely fragmented—with a huge range of screen sizes and pixel densities. Designing the UI for an app which runs on all these devices might seem like a daunting task.
However, Android has a solution for you. In the fifth course of the Android Feast, you’ll get to grips with building UI that automatically adapts to the different screen sizes that it finds itself on.
By the time you’ve finished this you’ll no longer be worrying how your app will look great on the many millions of devices it’ll be running on!
You’re probably feeling pretty full by now, but I hope you’ve left some room for the sixth and final course?
Fragments are incredibly useful components that can manage some of the responsibilities of an activity. They promote reusability, composition and adaptability as they are used together as part of an activity.
In the sixth and final course of the Android Feast, you’ll learn how to create fragments, how to use them within an activity, and how fragments can communicate with their parent activity as you build yourself a super-cool rage encyclopedia!
If you’ve been wanting to learn Android development but haven’t gotten around to it, this is a great opportunity – all of these tutorials are fresh off the presses and a great way to get started.
The Android team and I hope you enjoy this free set of tutorials, and we’ll be releasing many more in the months to come! :]
The post Introducing the Android Feast! appeared first on Ray Wenderlich.
Update Note: This beginning Android developmen tutorial is now up to date with the latest version of Android Studio. Updates by Megha Bambra. Original tutorial by Matt Luedke. Previous updates by Darryl Bayliss.
Clearly there’s a demand for Android app development since there are over one billion active devices around the globe. To say that it’s an exciting platform and space to make apps for is an understatement.
There aren’t any prerequisites for this beginning Android development tutorial, other than a willing mind and a Mac — you can certainly develop for Android on PC, but these instructions are tooled for Mac-based developers.
You’ll learn how to set up all the tools you need and will walk away as an Android developer-in-training. Here’s what you’ll do in this beginning Android development tutorial:
One of the most important parts of getting started with any new platform is setting up your environment, and it’s no different with Android.
It’s important to take your time and follow each step methodically. Even if you follow the steps perfectly, you may have to troubleshoot a small issue or few. Your system configuration or product versions can make for unexpected results.
With all of this in mind, let’s quickly check that you have the Java Development Kit (JDK) installed. To check, you’ll use trusty old Terminal.
Note: You’ll learn the essential steps for this tutorial in the next few paragraphs, but if you’d like to deepen your knowledge of Terminal, you’ll find a good introductory tutorial about it in this blog from teamtreehouse.com.
In a nutshell, using Terminal is kind of like looking under your car’s hood. It’s how you really get to know the machine face-to-face, without any complex graphical interface to interfere.
You can find the Terminal app quite easily on a Mac: open Launchpad and type terminal into the search at the top of the screen and select Terminal when it shows up.
Once you have the Terminal open, type in java -version
. You should see some output that mentions a version number, like below.
If that’s not what you see, then you don’t have the JDK installed. Terminal might tell you -bash: java: command not found
, or it could say No Java runtime present, requesting install.
and trigger a pop up that will lead you down the yellow brick road…to Oracle’s website.
You can either click More Info… or head over to Oracle to download the JDK from Oracle.
Install the JDK if needed, and once you’re done, head over to the Android Studio page and click the Download Android Studio button.
Google constantly updates this page, so the version you see may very well be newer than the screenshot above. Once you click the button, you’ll see a request to agree to the terms and conditions.
After reading these carefully (everybody takes the time to fully read these, right?) accept and click the blue button underneath titled Download Android Studio. Once the download is complete, you can install Android Studio similar to how you install any other program.
The download page will redirect to a page that contains installation instructions for OS X, Windows and Linux Operating Systems. If the instructions don’t appear, then you can view them here.
Once installation wraps itself up, go ahead and launch Android Studio!
The setup wizard will greet you the first time it loads.
Click Next to move to the Install Type screen. This whole process will probably take several minutes.
Check the box for Standard and click Next.
On the Verify Settings window, you’ll have an opportunity to confirm your setup. Click Finish to start downloading the SDK components. Once everything downloads, click Finish.
After a few minutes, you’ll have the welcome screen, which serves as your gateway to building all things Android.
Even though you just downloaded it, it’s possible that it’s not the latest version. Check whether any updates are available by clicking check for updates at the bottom of the welcome screen. If an update is available, a window like the screenshot below will appear. Select Update Now and let it do its thing.
Each version of Android has its own SDK (Software Development Kit) that enables you to create applications for the Android platform. Since you just went through the setup wizard, you’ll already have the latest version of the SDK available to you.
However, it’s useful to know how to install additional versions of the SDK so that you can develop for all supported versions of Android.
SDKs also allow you to create AVDs (Android Virtual Devices) that are customized to your personal configuration for the purpose of testing your app.
From the Android Studio welcome screen, click Configure.
The menu will slide across and present the Configure menu. Select the SDK Manager option.
Once it launches, you’ll see a window like the one below:
The first tab of this window, SDK Platforms, lists the Android SDK platform available for download.
Enable the Show Package Details option to see individual SDK components, such as the platform itself and the sources pertaining to the API level like system image. Take note of the checkbox next to the SDK platform; it will be pre-selected if an update is available.
By default, the SDK Manager installs the latest packages and tools. Select the SDKs as shown in the screenshot above. If you wish to install other SDKs, just select them for installation.
The SDK Tools tab lists developer tools and documentation along with the latest versions. Similar to the first tab, checking the Show Package Details will display available version of SDK tools.
The first three components in this list, for example, are Android SDK Build Tools, Android SDK Tools and Android SDK Platform-Tools. Each contains components that are designed to assist in the development of Android and work across multiple SDKs. Go with the default selection on this tab.
The SDK Update Sites tab displays the update sites for Android SDK tools and add-ons. You’re not limited to what’s listed because you can add other sites that host their own Android SDK add-ons, and then download them from those sites.
For the purpose of setting up correctly, select the options that are checked in the screenshot above. Click Apply at the bottom if it’s active. You’ll be presented with a confirmation dialog for the chosen packages; accept and continue. Click OK to close out the window.
The window will disappear and the SDK Manager will download and install the selected items. Once it’s done, click Finish. You’ll be directed back to the SDK Manager window where clicking OK will take you back to the Welcome to Android Studio.
Now the fun begins!
Android Studio has a nice little step-by-step tool to help you create your project. Click Start a new Android Studio Project from the Welcome to Android Studio screen:
Note: If you currently have an Android Studio project open and can’t see the welcome screen, select File\New Project from the menu to create a new project.
Android Studio will present you with a project creation screen:
Enter OMG Android in Application name as shown above. Feel free to put your own name in the Company Domain text field. As you type, you’ll notice the Package Name automatically changes to create a reverse domain style name based on your entries.
The Package Name is used to uniquely identify your app so that any work performed by a device is always properly attributed to the source, thus preventing confusion between apps.
You can set the Project location to any location on your hard drive — keep the default if you don’t have a preference. Click Next at the bottom of the window.
The next screen is the Target Android Devices window. This is where you select device types and operating systems to target.
The Minimum SDK drop-down menu sets the minimum version of Android required to run your app. The newer the SDK, the more features you’ll have at your disposal; however, newer SDKs support fewer devices.
Selecting this value is simply a matter of balancing the capabilities you want and the devices you want to support. This is where developing for Android can get a little tricky.
If you really want to get into the details of what Minimum SDK version is best for your App, let Android Studio help you out.
As you change the Minimum SDK in the drop down menu, the percentage in the text underneath reflects what percentage of devices currently run that version of Android.
Click Help me choose underneath the drop down list to learn more about each SDK’s set of features.
For more information on API versions and their uses, check out the Android Dashboards, which are updated every few days.
For now, you just want an App that works on an Android Phone, and that is what you’ll see by default, alongside the default Minimum SDK. For this project, select SDK of API 16: Android 4.1 (Jelly Bean).
After you choose the SDK, you choose a default activity
for your app.
Think of an activity as a window within your app that displays content with which the user can interact. An activity can take up the entire screen or it could be a simple pop-up.
Your options on this particular template range from a blank activity with an Action Bar right up to an Activity with an embedded MapView
.
You’ll make a lot of activities as you develop apps, so get to know them and know them well.
Select the Blank Activity option and click Next.
To speed this part up a little bit you’ll use the pre-populated default values, but what is actually done with these values?
Click Finish.
Android Studio takes this as a cue to go do a bunch of behind-the-scenes operations and create your project. As it shoots out descriptions of what it’s doing, you may notice it says something like this:
You see your project name, which is familiar. But then there is this Gradle word, and then a mention of Maven in the URL.
The benefit of having a modern IDE like Android Studio is that it handles a lot for you. But, as you’re learning how to use the software, it’s good to have a general sense ofwhat it’s doing for you.
Gradle
Gradle is a relatively new build tool that’s easy to use, and if you investigate further, you’ll find it contains advanced options. It takes your Java code, XML layouts and the latest Android build tools to create the app package file, also known as an APK file.
You can customize your configurations to have development or production versions of the app that behave differently, or you can add dependencies for third-party libraries.
Maven
Maven is another project build tool, and it can also refer to the Maven Central repository of java libraries.
It’s absurdly easy to use Gradle and Maven Central in concert with Android Studio to incorporate all sorts of functionality from the Android development community. If you’re familiar with iOS, then know that these give you cool developer superpowers much as CocoaPods does.
After a brief moment, Android Studio will finish building your project. The project is pretty empty, of course, but it has everything it needs set up so that it can be launched on an Android device or emulator. You’ll be dropped off in this spot:
And that’s all it takes to create a Hello World project on Android Studio! Now to dress it up and work through building and running this project on an emulator.
You’ve got Android Studio and you’ve created an app. So how do you run it?
Android Studio comes with the ability to set up a software-based Android device on your computer and run apps on it, browse websites, debug and everything you would expect from a simulator. This capability is known as the Android Emulator.
You can set up multiple emulators and set the screen size and platform version for each to whatever you like. Good thing, too. You’d need a whole room dedicated to storing devices for testing because there are so many out there — okay, maybe that’s an exaggeration, but you get the idea. :]
If you ran through the setup wizard earlier using the standard installation, then you’ll already have an emulator set up and ready for you. Android Studio makes use of some useful software developed by Intel to ensure your emulator runs quickly.
Up until recently, your computer would have to emulate everything an Android device would try to do, right down to its hardware, which runs an ARM-based processor. Most computers make use of x86-based processors, meaning your computer would have to do computationally intense tasks that take a significant amount of time just to test your app.
You still have the option to create an emulator that is as close to an actual device as you can, but be aware that the initial load times can drag a bit and have put off many an Android developer from using the emulator at all.
With all of that being said…let’s set up an emulator anyway, because you do need to know how!
Click AVD Manager. It’s a button near the right side of the toolbar that shows an Android popping its head up next to a device with a purple display:
Once AVD Manager opens, you’ll probably see the default emulator and a few details about it, notably what type of emulator it is, what API it’s using, and what CPU instruction set it uses.
Alternatively, if the default device hasn’t been created for you, you’ll just see an option to create a new device. Either way, work through the process of creating a new AVD so you know how to do it. Click Create Virtual Device… in the bottom left to begin configuring a new virtual device.
The first decision you need to make is what type of device. The Category list on the left shows all the types of devices you can emulate. In the middle, you see a list of specific devices. Take a moment to explore.
For now, you just want to emulate a phone-sized device, but if you wanted to emulate an Android Wear watch or an Android TV then you have options to do so here.
Select Nexus S in the list of devices available to you from the phone category and click Next.
Now you’re at the step where you decide what version of Android the virtual device will run. You’ll already have one or two available to you thanks to the setup wizard, so lets use one of them.
Select Lollipop and make sure the one selected has the value x86 in the API column so the emulator runs as fast as possible on your x86 computer.
Click Next once you’re done to advance to the final screen.
Note: If you can’t click Next, then you probably need to download some archives. Go back to your selection and click Download. When it’s done, you’ll be able to move forward.
The last screen lets you confirm your choices and gives options to configure some other properties such as device name, startup orientation and RAM size. For now, use the defaults and click Finish.
Close the AVD Manager to go back to Android Studio’s main view. Now that you’ve configured everything, click the Run button.
A new window will appear, asking you to choose the device you wish to test your App on. You currently have no devices running, so select the Nexus S you just created.
Ensure the Launch Emulator radio button is checked and that your AVD is selected in the drop down menu then click OK.
Note: If you get an error that says This AVD’s configuration is missing a kernel file!!, check to make sure that you don’t have the ANDROID_SDK_ROOT environment variable set from a previous installation of the Android SDK. See this thread on Stack Overflow for more troubleshooting tips.
In the event that it doesn’t work the first time or takes several minutes for the emulator to fire up correctly, don’t worry, that’s not entirely unexpected. Stick with it. Once it’s ready, you should see something like this:
Whoa. You just made your first Android app.
If you have an Android device and want to run your app on it, follow the animated GIF on the right. It demonstrates how to enable developer mode on your device.
Here are the step-by-step instructions to enable Developer Mode on an Android device:
Now that you’ve configured your device, click the Run button.
Next, you’ll get a prompt from the Device Chooser dialog. The device you enabled the developer mode should now appear in this dialog. Select it and click OK.
Ahh…isn’t it rewarding to see the app on your device? Go ahead and show it off to your friends. :]
During your Android app-making journey, you’ll find times where you need to import existing projects. The steps below will guide you through how to import a project:
It’s build and run time! Click the run button in the toolbar and select either the emulator or device you’ve already set up.
You’ve covered a lot of ground in this beginning Android development tutorial: from downloading and installing Android Studio, through creating your first “Hello World!” app, to deploying it on a physical device!
Keep reading for the next part of the series, where you’ll take a tour of Android Studio.
In the meantime, here are some extra tips to help you get up to speed with Android:
The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.
I hope you enjoyed this beginning Android development tutorial — you’ve successfully installed Android Studio and are now ready to take on the world of Android development. If you have any questions or comments, please join the discussion in the comments below.
The post Beginning Android Development Tutorial: Installing Android Studio appeared first on Ray Wenderlich.
Android Studio is an IntelliJ IDEA based IDE and declared by Google as the official IDE for Android application development.
In this beginning Android development tutorial, you’ll create a simple fortune-telling app and learn to use Android Studio’s key features by:
Note: This beginning Android development tutorial is assumes that you’ve already installed Android Studio and have set up an emulator or a device configured for testing. If you haven’t, please refer to our previous tutorial about installing Android Studio to get up and running in no time!
You’ll start by creating a brand new Android app that you’ll use to explore Android Studio and learn about its capabilities and interface.
For bonus points, you’ll also walk away as a bonafide fortune teller — or something to that effect. At least you’ll have a fun app to play around with!
Fire up Android Studio and in the Android Studio Setup Wizard window, select Start a new Android Studio project.
In the Create New Project window, set the Application Name as Fortune Ball, enter a Company Domain of your choosing, and select a convenient location to host your application in the Project location field. Click Next.
Now you’re looking at the Target Android Devices window. Check the Phone and Tablet box and specify API 15 as the Minimum SDK. Click Next.
From the Add an activity to Mobile window, select Blank Activity. Take a half minute here to look at all your options; this window gives you an overview of the layout template. In this case, it’ll be a blank activity with a toolbar at the top and a floating action button at the bottom. Click Next to proceed.
In the Customize the Activity window, which is shown in the screenshot below, you’ll have the option to change Activity Name, Layout Name, Title and Menu Resource Name. For the purposes of this tutorial, keep it simple and accept the default values by clicking Finish.
Within a short amount of time (hopefully seconds!), you’ll land on a screen that looks similar to this:
Build and run your application and you should see a similar screen on your device or emulator. Note that the emulator acts like a device, so it will need time to boot and load.
Voila. That’s an app! There’s not much to it, but it’s enough to dive into the next section.
For this portion of the tutorial, your focus will be on the highlighted section of the screenshot below. This window shows the project files of your application. By default, the files are filtered to show Android project files.
When you select the file dropdown menu as illustrated in the screenshot below, you’ll see several options to filter the files. The key filters here are Project and Android.
The Project filter will show you all the application modules — there is a minimum of one application module in every project.
Other types of modules include third-party library modules or other Android application modules. Each module has its own complete source sets, including a gradle file, resources and source files, e.g. java files.
Note: If you don’t see the project view open, you can click on the Project tab on the left side panel as indicated in the screenshot above.
The default filter is Android and you’ll see the following folders at the very top level:
You’ll take a deeper dive into each of these folders, starting with the manifests in the next section.
Every Android application contains the AndroidManifest.xml file found in the manifests folder. This XML file informs your system of the app’s requirements and must be present in order for the Android system to build your app.
Go to the app’s manifests folder and expand to select AndroidManifest.xml. Double click on the file to open.
The manifest
and application
tags are required in the manifest file and must only appear once.
In addition to the element name, each tag also defines a set of attributes. For example, some of the many attributes in the application
tag are: android:icon
, android:label
and android:theme
.
Other common elements that can appear in the manifest include:
uses-permission
: requests a special permission that must be granted to the application in order for it to operate correctly. For example, an app must request permission from the user in order to access the Internet—in this case you must specify the android.permission.INTERNET
permission.activity
: declares an activity that implements part of the application’s visual user interface and logic. Every activity that your app uses must appear in the manifest—undeclared activities won’t be seen by the system and sadly, they’ll never run.service
: declares a service that you’re going to use to implement long-running background operations or a rich communications API that can be called by other applications. An example includes a network call to fetch data for your application. Unlike activities, services have no user interface.receiver
: declares a broadcast receiver that enables applications to receive intents broadcast by the system or by other applications, even when other components of the application are not running. One example of a broadcast receiver would be when the battery is low and you get a system notification within your app, allowing you to write logic to respond.You can find a full list of tags allowed in the manifest file here on the Android Developer site.
You’re currently looking at an excellent example of a framework, but a terrible fortune teller; you’re here because you want to learn how to play around on Android. That’s rather convenient because the manifest needs some changes so you can look into the future.
Under activity
, add the following attribute: android:screenOrientation="portrait"
. to restrict the screen to portrait mode only. If it’s absent, the screen will transform to landscape or portrait mode depending on the device’s orientation. After adding this attribute, your manifest file should look like the screenshot below:
You’ll build and run the app. If you’re testing on your device, rotate your phone. You’ll notice that the screen doesn’t transform into landscape mode as you have restricted this capability in the AndroidManifest file.
Let’s shift gears to Gradle. In a nutshell, it’s a build system that’s utilized by Android. It takes the Android project and builds/compiles it into an installable APK that in turn can be installed on devices.
As shown below, you can find the build.gradle file, located under Gradle scripts, in your project at two levels: project level and application level. Most of the time, you’ll edit this file at the application or module level.
Open up the build.gradle (Module:app) file. You’ll see the default gradle setup:
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.raywenderlich.fortuneball" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.0' compile 'com.android.support:design:23.1.0' } |
Let’s step through the major components:
apply plugin: 'com.android.application'
applies the Android plugin at the parent level and makes available the top level build tasks required to build an Android app.android{...}
section, you get configuration options such as targetSdkVersion
. In this case, the target SDK for your application is 23. Another important component is the minSDKVersion
which defines the minimum SDK version a device should have installed in order to run your application. For example, if your device’s SDK version was 14, then this app won’t be able to run on that device since here the minimum supported version is 15.dependencies{...}
that lets you to add dependencies quite effortlessly. The important ones to note are the compile 'com.android.support:appcompat-v7:23.1.0'
and compile 'com.android.support:design:23.1.0'
. They provide support and compatibility of the new features from API 23 for the older APIs.In addition to Android compatibility libraries, you can also add other third party libraries here. You’ll add an animation library where you’ll be able to add some cool effects to user interface elements in your application. Find dependencies
, and update it to match the following:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.0' compile 'com.android.support:design:23.1.0' compile 'com.nineoldandroids:library:2.4.0' //Add this dependency compile 'com.daimajia.easing:library:1.0.1@aar' //Add this dependency compile 'com.daimajia.androidanimations:library:1.1.3@aar' //Add this dependency } |
Here you added three new third-party dependencies that will help you make FortuneBall shine. These libraries can be automatically downloaded and integrated by Android Studio.
In fact, once you add these dependencies, Android Studio realises that it needs to download them and tells you as much. Look for a bar across the top of the build.gradle file as shown the next screenshot. Click Sync Now to integrate these dependencies in your app.
Syncing takes couple of seconds. You can monitor the Gradle file update in the Messages tab in the bottom panel. Look for a success message in that panel as shown in the screenshot below.
Alright, that’s all the config you need to do to Gradle for now. The whole point of this was so that you’re setup to add some fancy animations to your application, which you’ll do in a bit.
An important part of making an Android app involves integrating other resources such as images, custom fonts, sounds, videos etc. These resources have to be imported into Android Studio and must be placed in appropriate folders. This allows the Android operating system to pick the correct resource for your app.
For Fortune Ball, you’ll be importing image assets and will place them in drawable folders. Drawable folders can hold images or custom XML drawables (i.e. you can draw shapes via XML code and use them in your app’s layouts).
To get started, download the image assets here, then unzip the contents and save them where they can be easily accessed.
Back to the project in Android Studio, switch the view from Android to Project. Open the res folder under app > src > main. Right click on the res folder, select New > Android resource directory.
You’ll get a window titled New Resource Directory. From the Resource type dropdown select drawable option. In the Available qualifiers list, select Density and click the button highlighted in the screenshot below:
In the subsequent window, select XX-High Density from the Density dropdown. Click OK.
Repeat the same process and create drawable-xhdpi, drawable-hdpi and drawable-mdpi folders by selecting X-High, high, and medium density respectively from the Density dropdown.
Each drawable folder that has a density qualifier (i.e. xxhdpi, xhdpi, hdpi), houses images corresponding to that particular density or resolution. For example, the folder drawable-xxhdpi contains the image that is extra high density, meaning an Android device with a high resolution screen will pick the image from this folder. This allows your app to look great on all Android devices, irrespective of the screen quality.
After creating all the drawable folders, go back to the unzipped contents in the finder, and copy (cmd + C) the image from each folder and paste (cmd + V) it into the corresponding folder in Android Studio.
When you paste the files, you’ll be presented with the Copy window. Select OK.
You’ve just put the ball in Fortune Ball and know how to import things now. Looks like you just checked another feature off your to-learn list!
An incredibly important part of building an Android application is creating a layout that the users of the application interact with. In Android Studio, you do this task in the layout editor. Open up content_main.xml file from res / layout folder. You’ll initially land on the Design tab of the layout editor. In this tab, you can drag user interface elements like buttons, text fields etc. in the editor.
On the right hand side of the Design tab is the Text tab. Switching to this view allows you to edit the XML that makes up the layout directly.
In both tabs, you’ll be able to preview the layout in the device as you build. Choose the Text tab to start building the layout for Fortune Ball.
Before you start building the view, you need to define some values. Open up strings.xml under the res \ values folder and add the following:
<string name="fortune_description">Suggest the question, which you can answer “yes” or “no”, then click on the magic ball.</string> |
strings.xml contains all the user-facing strings that appear in your app. Splitting the strings out into their own file makes internationalization a breeze, as you just provide a strings file for each language you wish to support. Although you might not want to translate your app right away, it’s considered a best-practice to use a strings file.
Next, open dimens.xml under values folder and add the following:
<dimen name="description_text_size">15sp</dimen> <dimen name="fortune_text_size">20sp</dimen> |
dimens.xml contains all the dimensions values such as margin spacing for your layouts, sizes of text etc. Again, it’s a good practice to keep the dimensions in this file so that they can be re-used in constructing layouts.
Head back to content_main.xml and replace the entire content of the file with the code below.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/activity_main" tools:context=".MainActivity"> <TextView android:id="@+id/descriptionText" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/fortune_description" android:gravity="center" android:textSize="@dimen/description_text_size"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/fortunateImage" android:src="@drawable/img_crystal" android:layout_centerHorizontal="true" android:layout_below="@id/descriptionText" android:layout_marginTop="10dp"/> <TextView android:id="@+id/fortuneText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/fortunateImage" android:gravity="center" android:layout_marginTop="20dp" android:textSize="@dimen/fortune_text_size" android:textStyle="bold" android:textColor="@android:color/holo_red_dark"/> <Button android:id="@+id/fortuneButton" android:layout_width="match_parent" android:layout_height="50dp" android:layout_below="@id/fortuneText" android:text="What's my fortune?" android:layout_centerHorizontal="true" android:layout_marginTop="10dp"/> </RelativeLayout> |
This rather large chunk of XML creates the layout of FortuneBall. At the top level you’ve added a RelativeLayout, whose job it is to layout its contents. It is stretched to match the size of its parent (i.e. the full activity).
Within the relative layout you added two pieces of text, an image and a button. These will appear within the container in the order that you added them, and their content is read from the strings.xml in the case of the text views, and from the drawable you added in the case of the image.
As you’re updating content_main.xml, notice how the Preview window updates the UI:
Note: If you can’t see the preview window, then click on the Preview button on the right-hand side panel of the layout editor while you’re still in the Text tab.
Build and run.
Congrats! You’ve designed your app’s layout. However, it’s only a pretty picture at this point — clicking on that button doesn’t do anything. Ready to play around with activities?
You use the java files located in app / src / main / java to implement your app’s logic.
Open MainActivity.java and add the following inside the MainActivity
class:
String fortuneList[] = {"Don’t count on it","Ask again later","You may rely on it","Without a doubt","Outlook not so good","It's decidedly so","Signs point to yes","Yes definitely","Yes","My sources say NO"}; TextView mFortuneText; Button mGenerateFortuneButton; ImageView mFortuneBallImage; |
In this small chunk of code you’ve declared 4 member variables for the activity. The first is an array of strings that represent the possible fortunes, and the remaining three represent the UI elements you created in the layout.
Next, replace the content of the onCreate()
method with the following:
// 1: super.onCreate(savedInstanceState); // 2: setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // 3: mFortuneText = (TextView) findViewById(R.id.fortuneText); mFortuneBallImage = (ImageView) findViewById(R.id.fortunateImage); mGenerateFortuneButton = (Button) findViewById(R.id.fortuneButton); // 4: mGenerateFortuneButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 5: int index = new Random().nextInt(fortuneList.length); mFortuneText.setText(fortuneList[index]); // 6: YoYo.with(Techniques.Swing) .duration(500) .playOn(mFortuneBallImage); } }); |
Taking the numbered sections one-by-one:
findViewById
method. The id
value is the same as the one you provided in the XML layout.OnClickListener
to the button. This is a simple class that encapsulates the functionality you’d like to perform when the button is pressed.fortuneList
array, and update the fortune text to show itOK—that wasn’t too bad right? Build and run, and hit the button to test out your fortune-telling powers.
You’re almost done. But before you start planning your release party, you have some clean up ahead, like getting rid of that floating button. Head to res / layout and open activity_main.xml.
This layout file contains a reference to content_main.xml that you previously edited. It wraps the content with the default toolbar and floating action button. However, Fortune Ball doesn’t need a floating action button, so remove the following code block from this xml file:
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_dialog_email"/> |
Build and run. You won’t be seeing that the floating button on the bottom right-hand corner around here anymore:
Ask a question, click or tap on What’s my fortune? and let your future unfold before your eyes!
DDMS is short for Dalvik Debug Monitor Server and is integrated into Android Studio. It provides services like simulating phone calls, taking screenshots, checking memory status, logging output, and it helps you dig into errors.
This is a modest overview of the functionality of DDMS. To dig into more details, read up on the documentation on the Android developer’s site.
To fire up DDMS, click the Android Device Monitor button on the Android Studio toolbar.
Once the DDMS launches, you’ll be greeted with the DDMS window:
DDMS presents many panels through which you get a window to peek inside your device or emulator and access a specific application for the purposes of debugging.
On the Devices panel on the left top hand side, you’ll see a list of devices and/or emulators depending on which is connected. If you expand the device or emulator, you’ll get a list of running applications.
On the left of the Devices panel, you’ll see a few panels.
Here are some details about what each panel does:
One of the most commonly used features of DDMS is Logcat, which you’ll find located at the bottom of the window.
Logcat gives you a detailed view into your device’s system messages with the ability to drill down into a specific application. To demonstrate this, set up a filter for your app. Click the + icon on the Logcat panel.
You’ll be presented with a window to where you populate specific information about the filters you need:
Enter Fortune Telling App in the Filter Name field and com.raywenderlich.fortuneball in the by Application Name filtering field. Hit OK and you’ll notice that Logcat is now filtering messages:
Here you’ll also be able to see your own logged messages you defined in the code. Oh, what? You’ve not added any messages for yourself?
Head to MainActivity.java and add the following to the end of onCreate()
:
Log.v("FORTUNE APP TAG","onCreateCalled"); |
The Log.v
calls for two parameters — a tag and a message. In this case, you’ve defined the tag as "FORTUNE APP TAG"
and the message as "onCreateCalled"
.
Build and run the app so you can see this log message in the Logcat panel.
Another very useful utility of logcat is the ability to see stacktrace or error messages from app crashes and exceptions. You’ll add a bug to your perfectly working app to see how that works.
Go to MainActivity.java and comment out the following line in onCreate()
:
//mFortuneText = (TextView) findViewById(R.id.fortuneText); |
Build and run the application. Once it launches click the What’s My Fortune? button on the screen. Oh no! It crashed.
How would you go about fixing this if you hadn’t put the bug in on purpose? Logcat to the rescue!
Head back to the Logcat panel — it’ll look something like this:
That sure is a lot of red text, and it’s exactly where to go sniffing around for clues. You’re looking for an exception somewhere. In this case, it’s line 58 in the MainActivity.java file.
By commenting out the mFortuneText = (TextView) findViewById(R.id.fortuneText)
, you created a variable but didn’t assign it a value — hence the null pointer exception.
Go ahead and uncomment that code and build and run the application. This time there’s no crash!
Logcat is a powerful tool that lets you debug your application errors and exception.
You can download the final project here.
Practice makes perfect! You’ve learned your way around and can now create a project, play around with Gradle, import assets, set up layouts and do some testing.
There’s a lot of cool stuff to learn about Android, and I suggest you start with these next steps:
This is it folks! Give yourself a round of applause and stay tuned for more awesome tutorials from your Android team. :]
The post Beginning Android Development Tutorial: Introduction to Android Studio appeared first on Ray Wenderlich.
Learn about Size Classes, the core concept of Adaptive Layout, and learn the basics of working with Size Classes in Interface Builder.
The post Video Tutorial: Adaptive Layout Part 1: Size Classes appeared first on Ray Wenderlich.
A fragment can be many things, but for the purposes of this tutorial, it is a module of code that holds part of the behavior and/or UI of an activity. As the name intimates, fragments are not independent entities, but are subservient to a single activity.
In many ways, they resemble and echo the functionality of activities.
Imagine for a moment that you’re an activity. You have a lot to do, so you’d employ a few mini-me’s to run around and do your laundry and taxes in exchange for lodging and food. That’s kind of like the relationship between activities and fragments.
And just like you don’t actually need a few minions to do your bidding, you don’t have to use fragments. However, if you do use them and use them well, they can provide:
In this introduction to Android fragments tutorial, you’ll build a mini encyclopedia of Rage Comics. The app will display a list of Rage Comics arranged in a grid. When a Rage Comic is selected, the app displays information about it. In this tutorial, you’ll learn:
Note: This tutorial assumes you’re comfortable the basics of Android programming and understand what activity lifecycle means. If you’re brand new to Android, you should work through both the Android Tutorial for Beginners and the Introduction to Activities first.
The time has come to release the fragments!
Download the starter project and start Android Studio.
In the Welcome to Android Studio dialog, select Import project (Eclipse ADT, Gradle, etc.).
Choose the top-level directory of the starter project, and click OK.
Check out the project, and you’ll find some resources like Strings and Drawables, XML layouts and an activity. They provide some boilerplate, layouts for your fragments, non-fragment code you’ll need, and a fragment class you’ll use later to write your own.
The MainActivity
will host all your wee fragments, and RageComicListFragment
contains code to display a list of the Rage Comic content so you can focus on fragments.
Build and run the project. You’ll see that it’s pretty quiet in there.
You’ll fix that…
Like an activity, a fragment has a full lifecycle, complete with events that occur at different points throughout. For instance, an event happens when the fragment becomes visible and active or when the fragment becomes unused and is removed.
Here’s a fragment lifecycle diagram from the official Android Developer documentation.
The following lifecycle events come into play when you add a fragment:
There’s more. These lifecycle events happen when you remove a fragment:
As you can see, the fragment’s lifecycle is intertwined with the activity’s lifecycle, but it has extra events that are particular to the fragment’s view hierarchy, state and attachment to its activity.
Fragments were introduced as part of the oft-forgotten, tablet-targeted Honeycomb release for creating device-specific layouts for a single app.
The v4 Support Library provides a fragment implementation for devices running Android below 3.0, specifically under android.support.v4.app.Fragment.
Even if your app is running on 4.0+, you should probably use support fragments.
It’s not just developers that depend on the Support Library. Other libraries also need it, like the v7 AppCompat Library, which holds the AppCompatActivity
and other back-porting of API 21 functionality. AppCompatActivity
is a subclass of the v4 FragmentActivity
.
So, if you want to get that sweet Lollipop functionality, you’ll need to take the same road as v4.
Eventually, All the Rages will show a list of Rage Comics on launch, and tapping on any of the items will display details about that particular comic. To start, you’ll work backwards and first create the detail page.
Open the starter project in Android Studio and find fragment_rage_comic_details.xml; this XML file lays out the comic detail display. It also displays one of the Drawables and the associated string resources.
Select Android Studio’s Project tab and right-click the com.raywenderlich.alltherages package. In the context menu, select New\Java Class, name it RageComicDetailsFragment and select OK.
This class will be responsible for displaying details for a selected comic.
In RageComicDetailsFragment.java, replace the code underneath the imports with the following:
import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class RageComicDetailsFragment extends Fragment { public static RageComicDetailsFragment newInstance() { return new RageComicDetailsFragment(); } public RageComicDetailsFragment() { } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_rage_comic_details, container, false); } } |
Activities always use setContentView()
to specify an XML file to use for their layouts, but fragments create their view hierarchy in onCreateView()
.
The third parameter of inflate
specifies whether the inflated fragment should be added to the container
. You should always set this to false
: the FragmentManager will take care of adding the fragment to the container.
There’s a new kid in town here: FragmentManager
. Each activity has a FragmentManager that, as its name implies, manages an activity’s fragments. It also provides an interface for you to access, add and remove those fragments.
You’ll notice that RageComicDetailsFragment
has a factory instance method, newInstance()
, as well as an empty public constructor.
Wait, but why do you need both of those methods? The newInstance
just calls the constructor.
Fragment subclasses require an empty default constructor. If you don’t provide one but specify a non-empty constructor, Lint will give you an error:
Oddly enough it will compile, but when you run your application, you’ll get an even nastier exception.
You probably know that Android may destroy and later re-create an activity and all its associated fragments when the app goes into the background. When the activity comes back, its FragmentManager
starts re-creating fragments by using the empty default constructor. If it cannot find one, you get an exception.
Wait, what if you need to pass information or data to a Fragment? Hold on tight: you’ll get the answer to that later.
Here’s where you get to add your own shiny new fragment, and the way you’ll do it is the simplest approach: add it to the activity’s XML layout.
To do this, open activity_main.xml and add the following inside of the root FrameLayout:
<fragment android:id="@+id/details_fragment" class="com.raywenderlich.alltherages.RageComicDetailsFragment" android:layout_width="match_parent" android:layout_height="match_parent"/> |
Here you’re placing a <fragment>
tag inside of the activity layout and specifying the type of fragment the class
attribute should inflate. The view ID of the <fragment>
is required by the FragmentManager
.
Build and run. You will see the fragment:
First, open activity_main.xml again and remove the <fragment>
you just placed. (Yes, I know, you just put it there — sorry.) You’ll replace it with the list of Rage Comics.
Open RageComicListFragment.java, which has all the lovely list code. You can see that the RageComicListFragment
has the empty default constructor and a newInstance()
.
The list code in RageComicListFragment depends on some resources. You have to ensure that the fragment has a valid reference to a Context
for accessing those resources. That’s where onAttach()
comes into play.
Open RageComicListFragment.java, and add these imports directly below the existing imports:
import android.content.res.Resources; import android.content.res.TypedArray; import android.support.annotation.Nullable; import android.os.Bundle; import android.support.v7.widget.GridLayoutManager; import android.app.Activity; |
This simply imports the required resources.
Inside of RageComicListFragment.java, add the following two methods above the definition of the RageComicAdapter
:
@Override public void onAttach(Context context) { super.onAttach(context); // Get rage face names and descriptions. final Resources resources = context.getResources(); mNames = resources.getStringArray(R.array.names); mDescriptions = resources.getStringArray(R.array.descriptions); mUrls = resources.getStringArray(R.array.urls); // Get rage face images. final TypedArray typedArray = resources.obtainTypedArray(R.array.images); final int imageCount = mNames.length; mImageResIds = new int[imageCount]; for (int i = 0; i < imageCount; i++) { mImageResIds[i] = typedArray.getResourceId(i, 0); } typedArray.recycle(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_rage_comic_list, container, false); final Activity activity = getActivity(); final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new GridLayoutManager(activity, 2)); recyclerView.setAdapter(new RageComicAdapter(activity)); return view; } |
onAttach()
contains code that accesses the resources you need via the Context
to which the fragment has attached. Because the code is in onAttach()
, you can rest assured that the fragment has valid Context
.
In onCreateView()
, you inflate the view hierarchy of RageComicListFragment
and perform some setup. A RecyclerView
is an efficient means of displaying multiple items on-screen that would require the user to scroll up and down.
It is more efficient than traditional methods of displaying items in a list or grid, because as the user scrolls a currently visible item out of view, that item is “recycled” and refused for any new item that becomes visible.
Generally, if you have some poking and prodding to do on the fragment’s view, onCreateView()
is a good place to start because you have the view right there.
Next you need to get RageComicListFragment
into MainActivity
. You will ask your new friend, FragmentManager
, to add it.
Open MainActivity.java and add the following to onCreate()
:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .add(R.id.root_layout, RageComicListFragment.newInstance(), "rageComicList") .commit(); } } |
Build, run and you’ll see a Rage-filled list once the app launches:
FragmentManager
helped achieve this awesomeness through FragmentTransactions
, which are basically fragment operations such as, add, remove, etc.
First you grab the FragmentManager
by calling getSupportFragmentManager()
, as opposed to getFragmentManager
since you are using support fragments.
Then you ask that FragmentManager
to start a new transaction by calling beginTransaction()
— you probably figured that out yourself. Next you specify the operation that you want by calling replace
and passing in:
activity_main.xml
, you’ll find @+id/root_layout
.
FragmentManager
to later retrieve the fragment for you.
Finally, you ask the FragmentManager
to execute the transaction by calling commit()
.
And with that, the fragment is added!
An if
block contains the code that displays the fragment and checks that the activity doesn’t have saved state. When an activity is saved, all of its active fragments are also saved. If you don’t perform this check, this could happen:
And you would be like this:
The lesson: Always keep in mind how the saved state affects your fragments.
Even though fragments are attached to an activity, they don’t necessarily all talk to one another without some further “encouragement” from you.
For All the Rages, you’ll need RageComicListFragment
to let MainActivity
know when the user has made a selection so that RageComicDetailsFragment
can display the selection.
To start, open RageComicListFragment.java and add the following Java interface at the bottom:
public interface OnRageComicSelected { void onRageComicSelected(int imageResId, String name, String description, String url); } |
This defines a listener interface for the activity to listen to the fragment. The activity will implement this interface, and the fragment will invoke the onRageComicSelected()
when an item is selected, passing the selection to the activity.
Add this new field below the existing ones:
private OnRageComicSelected mListener; |
This field is a reference to the fragment listener, which will be the activity.
In onAttach()
, add the following just below super.onAttach(context);
:
if (context instanceof OnRageComicSelected) { mListener = (OnRageComicSelected) context; } else { throw new ClassCastException(context.toString() + " must implement OnRageComicSelected."); } |
This initializes the listener reference. You wait until onAttach()
to ensure that the fragment actually attached itself. Then you verify that the activity implements the OnRageComicSelected
interface via instanceof
.
If it doesn’t, it throws an exception since you can’t proceed. If it does, then you set the activity as the listener
for RageComicListFragment
.
In the onBindViewHolder()
method, add this code to the bottom — okay, I fibbed a little; the RageComicAdapter
doesn’t have everything you need):
viewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mListener.onRageComicSelected(imageResId, name, description, url); } }); |
This adds a View.OnClickListener
to each Rage Comic so that it invokes the callback on the listener (the activity) to pass along the selection.
Open MainActivity.java and update the class definition to following:
public class MainActivity extends AppCompatActivity implements RageComicListFragment.OnRageComicSelected { |
This specifies that MainActivity
is an implementation of the OnRageComicSelected
interface.
For now, you’ll just show a toast to verify that the code works. Add the following import below the existing imports so that you can use toasts:
import android.widget.Toast; |
And then add the following method below onCreate()
:
@Override public void onRageComicSelected(int imageResId, String name, String description, String url) { Toast.makeText(this, "Hey, you selected " + name + "!", Toast.LENGTH_SHORT).show(); } |
Build and run. Once the app launches, click one of the Rage Comics. You should see a toast message naming the clicked item:
You got the activity and its fragments talking. You’re like a master digital diplomat.
Currently, RageComicDetailsFragment
displays a static Drawable
and set of strings
, but you want it to display the user’s selection.
Open RageComicDetailsFragment.java and add the following constants at the top of the class definition:
private static final String ARGUMENT_IMAGE_RES_ID = "imageResId"; private static final String ARGUMENT_NAME = "name"; private static final String ARGUMENT_DESCRIPTION = "description"; private static final String ARGUMENT_URL = "url"; |
Replace newInstance()
with the code shown below:
public static RageComicDetailsFragment newInstance(int imageResId, String name, String description, String url) { final Bundle args = new Bundle(); args.putInt(ARGUMENT_IMAGE_RES_ID, imageResId); args.putString(ARGUMENT_NAME, name); args.putString(ARGUMENT_DESCRIPTION, description); args.putString(ARGUMENT_URL, url); final RageComicDetailsFragment fragment = new RageComicDetailsFragment(); fragment.setArguments(args); return fragment; } |
A fragment can take initialization parameters through its arguments, which you access via getArguments()
and setArguments()
. The arguments are actually a Bundle
that stores them as key-value pairs, just like the Bundle
in Activity.onSaveInstanceState
.
You create and populate the arguments’ Bundle
, call setArguments
, and when you need the values later, you call getArguments
to retrieve them.
As you learned earlier, when a fragment is re-created, the default empty constructor is used — no parameters for you.
Because the fragment can recall initial parameters from its persisted arguments, you can utilize them in the re-creation. The above code also stores information about the selected Rage Comic in the RageComicDetailsFragment
arguments.
Add the following imports to the top of RageComicDetailsFragment.java:
import android.widget.ImageView; import android.widget.TextView; |
Now, add the following to onCreateView():
@Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_rage_comic_details, container, false); final ImageView imageView = (ImageView) view.findViewById(R.id.comic_image); final TextView nameTextView = (TextView) view.findViewById(R.id.name); final TextView descriptionTextView = (TextView) view.findViewById(R.id.description); final Bundle args = getArguments(); imageView.setImageResource(args.getInt(ARGUMENT_IMAGE_RES_ID)); nameTextView.setText(args.getString(ARGUMENT_NAME)); final String text = String.format(getString(R.string.description_format), args.getString (ARGUMENT_DESCRIPTION), args.getString(ARGUMENT_URL)); descriptionTextView.setText(text); return view; } |
Since you want to dynamically populate the UI of the RageComicDetailsFragment
with the selection, you grab references to the ImageView
and TextViews
in the fragment view in onCreateView
. Then you populate them with the image and text you passed to RageComicDetailsFragment
, using them as arguments.
Finally, you need to create and display a RageComicDetailsFragment
when a user clicks an item, instead of just showing a dinky little toast. Open MainActivity and replace the logic inside onRageComicSelected
with:
@Override public void onRageComicSelected(int imageResId, String name, String description, String url) { final RageComicDetailsFragment detailsFragment = RageComicDetailsFragment.newInstance(imageResId, name, description, url); getSupportFragmentManager() .beginTransaction() .replace(R.id.root_layout, detailsFragment, "rageComicDetails") .addToBackStack(null) .commit(); } |
The code includes some classes you haven’t used previously, so you need to fire off a couple of option + enter sequences to import the missing classes.
You’ll find that this code is similar to your first transaction that added the list to MainActivity
, but there are some notable differences.
replace()
, instead of add
, which removes the fragment currently in the container and then adds the new Fragment.
addToBackStack()
of FragmentTransaction
. Fragments have a back stack, or history, just like activities.
The fragment back stack is not independent of the activity back stack. Think of it as an extra stack of history on top of that of the host activity.
When you navigate between activities, each one gets placed on the activity back stack. Whenever you commit a FragmentTransaction
, you have the option to add that transaction to the back stack.
So what does addToBackStack()
do? It adds the replace()
to the back stack so that when the user hits the device’s back button it undoes the transaction. In this case, hitting the back button sends the user back to the full list.
The add()
transaction for the list omits calling addToBackStack()
. This means that the transaction is part of the same history entry as the entire activity. If the user hits the back button from the list, it backs the user out of the app.
Guess what? That’s all the code, so build and run.
There won’t be too much difference at first; it’s still the same ol’ list. This time, however, if you click on a Rage Comic, you should see the details for that comic instead of a dinky little toast:
Woo hoo! Your app is now officially All the Rages, and you have a nice understanding of fragments.
You can download the final project here.
There is a lot more to learn and do with fragments. Like any kind of tool or feature, consider whether fragments fit your app’s needs and if they do, try to follow best practices and conventions.
To take your skills to the next level, here are some things to explore:
ViewPager
: many apps, including the Play Store, utilize a swipeable, tabbed content structure via ViewPagers
.
DialogFragment
instead of a plain old dialog or AlertDialog
.
We hope you enjoyed this introduction to Android fragments tutorial, and if you have any questions or comments, please join the forum discussion below!
The post Introduction to Android Fragments Tutorial appeared first on Ray Wenderlich.
Learn how Adaptive Layout lets you specify different values for images and fonts based on the Size Class so that as things are scaled down the user can still read the font.
The post Video Tutorial: Adaptive Layout Part 4: Fonts & Images appeared first on Ray Wenderlich.
As you probably know, the team and I are running an iOS conference next March focused on hands-on, high quality tutorials: RWDevCon.
Recently we put out a poll to all attendees, and had everyone vote on their favorite topics – we chose the top-voted topics, and built a schedule around that.
Today, we are happy to announce the schedule! The conference is split into 3 tracks of tutorials – beginner, intermediate, and advanced – and you can switch freely between the tracks at any time.
The conference also includes inspiration talks; short 18-minute non-technical talks with the goal of giving you a new idea, some battle-won advice, and leaving you excited and energized.
Let’s take a quick peek at what’s in store this March!
Swift is Apple’s new programming language. Released in 2014 and open sourced in 2015, it is the new hotness that everyone in the iOS and Mac programming world is talking about.
In this session, you’ll take a tour of the language right from the basics. At the end you’ll be comfortable reading and writing Swift, and will be ready for the rest of the conference!
Time to start developing for iOS!
In this session, you’ll build an app from scratch and learn to assemble your user interface with Storyboards and Auto Layout, display large datasets with Collection Views, and fetch images and JSON data from a server. These core skills will help no matter what path your iOS development takes!
Over the past few years you’ve probably come to love Auto Layout; it makes device agnostic layouts a breeze. But you probably find yourself making the same patterns of constraints over and over again.
Well, the layout gods have heard your cries for help, and in iOS 9 furnished you with UIStackView. In this session you’ll build your first stack view and learn the true power of easily constructing realistic layouts without Auto Layout constraints.
Want to increase your app’s exposure and enhance the user’s experience at the same time? Then check out iOS 9’s new App Search APIs.
App Search gives you the tools needed to index app content and make it searchable in Spotlight. Also, the look and functionality of Spotlight search results can be customized to better fit the content of the app.
Have your users ever complained that your app is too slow or crashes all the time? Have you spent hours trying to track down a bug?
In this session, you will learn how to use Xcode Instruments to perform analysis and diagnostics to pinpoint exactly where in your code the problem occurs. By incorporating Instruments into your development cycle, you will become a better developer.
The Apple Watch – ‘nuf said! Learn how to adapt an iOS app with Watch-appropriate layout and UI controls, to create a Watch app you’ll be proud to show off.
Warning: both versions of the sample app have been proven by user testing to be highly addictive. ;]
Apple TV is finally here! Now all it needs are some apps. Don’t miss out on an opportunity to learn how to create a traditional (native) tvOS app.
The sharks are hungry, and this session will help get your hook in the water.
You’ve learned how to make tvOS apps using traditional techniques you’re familiar with, but the Apple has provided a second way to make tvOS apps as well: TVML apps.
In this session, you’ll learn what this is and how it works as you make a basic TVML app – and although JavaScript isn’t what the typical iOS developer might sign up for, you’ll be pleasantly surprised at the benefits it can offer.
Tired of reinventing the wheel? In this session, you’ll learn about iOS design patterns: reusable solutions to commonly occurring problems.
You’ll also learn new and impressive terms — “loose coupling,” “composition over creation,” “polymorphic design” and more sure to impress your friends and loved ones.
Are you just getting started with Swift and need some style tips? Or are you learning Swift, but find yourself writing Swift code with an Objective-C accent?
In this session, you’ll see contrasts between the old Objective-C ways and the new Swifty ways, and you’ll pick up some tips on how to make your types take full advantage of all that Swift has to offer.
Ever wonder how the controls provided by UIKit are engineered? Well wonder no more, as this session will walk you through everything you need to know.
You’ll learn how to create a unique custom control by compositing existing views and layers, before making it Interface Builder friendly so you can customize the control at design time and have it appear exactly as it would at run time.
Having created a great looking control in part 1, you’ll be wondering how you can make it fully interactive, embracing the unique opportunity that touch-based devices offer.
You’ll start out by creating a custom gesture recognizer, before stepping back to a more technical level and discovering how to use iOS frameworks to make a reusable, distributable package that you can share with all your friends!
Add some personality to your apps by moving beyond the iOS standards and adding a pinch of surprise and delight to your apps.
In this session, you’ll learn the principles behind custom refresh controls and transitions, and how to make your app stand out from the crowd.
Ever wanted to take your app to the next dimension? The latest iPhones now have support for capacitive touch, which can tell how hard you are pressing on the screen.
New to iOS 9, the 3D Touch APIs let you add home screen shortcuts, peek and pop, and even your own custom gestures! This tutorial will teach you all about these new APIs and show you just how easy they are to add to your apps.
In Xcode 7, Apple introduced the ability to UI test your application without any third-party dependencies using XCUI tests.
Learn how to take advantage of the UI test recording feature, how to use accessibility features to verify your application works for your users as expected, and how to pass information into your test target even though it’s running in a totally separate process.
Have you ever spent time traversing Xcode’s interface searching for that one file or feature you want? This session will introduce you to several Xcode tips including hotkeys, behaviors, code snippets, and more!
Watch as Xcode does exactly what you want it to at the touch of a button, or even better — without touching anything at all.
iPhone 4S, 5, 6, 6 Plus, iPad, and iPad Pro, oh my!
In this session, you’ll learn advanced techniques for adapting your layout to multiple screen sizes with minimal effort. And you’ll see how to make layouts work with auto-sizing UICollectionView cells and UIScrollView.
With LLDB, you are a puppet master in a vast expanse of memory.
Learn how to impress/scare your coworkers with your newfound debugging knowledge and gain insight into how Apple solves the same problems you’re up against.
Swift 2 protocols can do things impossible in Objective C, so much that Apple has even argued for “Protocol-Oriented Programming” as an alternative to object-oriented programming. What does this really mean and when is it wise?
This talk will walk through a detailed example, illustrating the pros and cons of OOP and protocol-based modeling, to show where Swift protocols are strictly superior, and also where their new benefits introduce fundamental new tradeoffs.
You’ve heard the term, learnt the theory, and even worked through some high level examples. This session takes Protocol-Oriented Programming out of the classroom and into the real world.
Learn how to use this new paradigm to solve everyday coding problems by working through realistic examples, and leave with the knowledge and experience you’ll need to apply POP to your own codebases.
Is your codebase resilient to change? Can you and your team build features and fix bugs swiftly and easily without inadvertently introducing regressions?
In this tutorial, you’ll learn practical core architectural concepts while getting hands on experience writing flexible code. From encapsulating user story logic into asynchronous NSOperations to injecting dependencies, you’ll walk out of here architecting apps like a pro!
Sick of giant view controllers? Not sure where to put your app’s business logic? Wanting to write tests but can’t for the life of you get started?
Lucky for you, the Model-View-ViewModel pattern is the answer you’ve been looking for! In this session you’ll get hands on experience organizing your code into easy-to-understand testable components using MVVM.
Developing for iOS used to be as straightforward as just targeting an iPhone, iPad, or (if you were feeling adventurous) both!
In the past year, however, Apple has introduced a watch, a new Apple TV, and bigger iPads that can run your app in a multitude of size modes. You will learn how to create a single project that shares styling, frameworks, and code across
a family of targets including iPhone, iPad, Apple TV, Apple Watch, and a Today Extension.
You (hopefully) love using Core Data in your apps but what’s the best way to pull data o the Internet and synchronize it with your app?
In this tutorial, you’ll learn how to structure your Core Data calls into services which makes synchronization easier for both unit testing and implementation.
How do I get TechCrunch or Apple to feature my Apps? How do I market an app to the top of the App Store? It’s not just luck — there is a method to the madness.
Apple Design Award winner Jeremy Olson shares how he made the journey from app maker to app marketer and reveals the secrets he has learned marketing Tapity’s top apps Grades, Languages, and Hours.
When was the last time you wrote some code, designed an interaction, shipped an app, or otherwise did work that you know was your absolute best?
In this talk, come hear about how to better operate in the always- on, noti cation- lled workplace to produce the best work of your career.
We all enjoy writing code, but sometimes your career as a coder can benefit from stepping away from the keyboard.
Drawing on personal stories from twenty years in technology, James’ examples serve as a guide showing how your own outside interests and experiences can make a difference in your life as a coder in unexpected ways. It may just change the way you look at the time you spend AFK.
We, as a society, fear failure. We are terriffed of losing that job that we hate because we’re afraid we won’t find another one. We stay in a relationship with someone we don’t like because we’re afraid of never finding anyone else. We fear change. But what if we didn’t?
This talk is about learning to embrace your failures to make a life for yourself that you want to live.
Smaller homes require less heating. Smaller apps require less time to get acquainted with.
In this talk, you will learn about the many advantages of a small team, a small product, a small market and a small launch.
The team and I are really excited about the schedule – we think it has something for everyone, from complete beginners to iOS gurus.
And that’s not all! In addition to the above, the conference includes a James Dempsey and the Breakpoints concert and party, Couchbase and Firebase labs, and much more!
You can download a PDF version of the schedule on the RWDevCon website. If you’re thinking of getting a ticket, you should probably do so soon; there are only 10 tickets left.
Happy holidays – we can’t wait to see you in March! :]
The post RWDevCon 2016 Schedule Now Available! appeared first on Ray Wenderlich.
Learn about trait collections and trait environments, the class and protocol you use to detect the current Size Class from code to further customize your layouts.
The post Video Tutorial: Adaptive Layout Part 5: Trait Collections appeared first on Ray Wenderlich.
Swift’s grand entrance to the programming world at WWDC in 2014 was much more than just an introduction of a new language. It brings a number of new approaches to software development for the iOS and OS X platforms.
This tutorial focuses on just one of these methodologies, FP (Functional Programming), and you’ll get an introduction to a broad range of functional ideas and techniques.
Create a new playground in Xcode so you can investigate FP in Swift by select File \ New \ Playground… and name it IntroFunctionalProgramming and leave the default platform as iOS. Click Next to choose a location to save it, then Create to save the file.
Keep in mind that this tutorial will cover FP at a high level, so thinking about the concepts through the filter of a real world situation is helpful. In this case, imagine you’re building an app for an amusement park, and that the park’s ride data is provided by an API on a remote server.
Start by replacing the playground boilerplate code with the following:
enum RideType { case Family case Kids case Thrill case Scary case Relaxing case Water } struct Ride { let name: String let types: Set<RideType> let waitTime: Double } |
Here you’ve created a Swift enum named RideType
that provides a set of types for rides. You also have a Ride
struct that contains the name
of a ride, a Set
for ride types, and a double
to hold rides’ current wait time.
Now you’re probably thinking about loop-de-loops and the thrill of feeling g-force on a wild ride, but trust me, there’s no ride quite like making the shift from other programming styles to FP! :]
At the highest-level, the distinction between FP and other software writing approaches is most simply described as the difference between imperative and declarative thinking.
FP encourages a declarative approach to solving problems via functions. You’ll see more distinction between imperative and declarative programming as you go through this tutorial.
In this section, you’ll get an introduction to a number of key concepts in FP. Many treatises that discuss FP single out immutable state and lack of side effects as the most important aspects of FP, so why not start there?
No matter what programming language you learned first, it’s likely that one of the initial concepts you learned was that a variable represents data. If you step back for a moment to really think about the idea, variables can seem quite odd.
Add the following seemingly reasonable and common lines of code to your playground:
var x = 3 // other stuff... x = 4 |
As a developer that’s trained in procedural programming and/or OOP, you wouldn’t give this a second thought. But how exactly could a quantity be equal to 3 and then later be 4?!
The term variable implies a quantity that varies. Thinking of the quantity x from a mathematical perspective, you’ve essentially introduced time as a key parameter in how your software behaves. By changing the variable, you create mutable state.
By itself or in a relatively simple system, a mutable state is not terribly problematic. However, when connecting many objects together, such as in a large OOP system, a mutable state can produce many headaches.
For instance, when two or more threads access the same variable concurrently, they may modify or access it out of order, leading to unexpected behaviour. This includes race conditions, dead locks and many other problems.
Imagine if you could write code where the state never mutated. A whole slew of issues that occur in concurrent systems would simply vanish – poof! You can do this by creating an immutable property, or data that is not allowed to change over the course of a program.
This was possible in Objective-C by creating a constant, but your default mode for properties was to create mutable properties. In Swift, the default mode is immutable.
The key benefit of using immutable data is that units of code that use it would be free of side effects, meaning that the functions in your code don’t alter elements outside of themselves, and no spooky effects can happen when function calls occur.
See what happens when you make the primary data you’ll work with in this tutorial an immutable Swift constant by adding the following array to your playground below the Ride
struct:
let parkRides = [ Ride(name: "Raging Rapids", types: [.Family, .Thrill, .Water], waitTime: 45.0), Ride(name: "Crazy Funhouse", types: [.Family], waitTime: 10.0), Ride(name: "Spinning Tea Cups", types: [.Kids], waitTime: 15.0), Ride(name: "Spooky Hollow", types: [.Scary], waitTime: 30.0), Ride(name: "Thunder Coaster", types: [.Family, .Thrill], waitTime: 60.0), Ride(name: "Grand Carousel", types: [.Family, .Kids], waitTime: 15.0), Ride(name: "Bumper Boats", types: [.Family, .Water], waitTime: 25.0), Ride(name: "Mountain Railroad", types: [.Family, .Relaxing], waitTime: 0.0) ] |
Since you create parkRides
with let
instead of var
, both the array and its contents are immutable. Trying to modify one of the items in the array, via
parkRides[0] = Ride(name: "Functional Programming", types: [.Thrill], waitTime: 5.0) |
Changing one of the items produces a compiler error. Go ahead and try to change those rides! No way, buster. :]
You’ve reached the part where you’ll add your first function, and you’ll need to use some NSString
methods on a Swift String
, so import the Foundation framework by putting this at the very top of your playground:
import Foundation |
Suppose you need an alphabetical list of all the rides’ names. You are going to start out doing this imperatively. Add the following function to the bottom of the playground:
func sortedNames(rides: [Ride]) -> [String] { var sortedRides = rides var i, j : Int var key: Ride // 1 for (i = 0; i < sortedRides.count; i++) { key = sortedRides[i] // 2 for (j = i; j > -1; j--) { if key.name.localizedCompare(sortedRides[j].name) == .OrderedAscending { sortedRides.removeAtIndex(j + 1) sortedRides.insert(key, atIndex: j) } } } // 3 var sortedNames = [String]() for ride in sortedRides { sortedNames.append(ride.name) } print(sortedRides) return sortedNames } |
Here you are:
From the perspective of a caller to sortedNames(:)
, it provides a list of rides, and then outputs the list of sorted names. Nothing outside of sortedNames(:)
has been affected. To prove this, first print out the output of a call to sorted names:
print(sortedNames(parkRides)) |
Now gather the names of the list of rides that were passed as a parameter and print them:
var originalNames = [String]() for ride in parkRides { originalNames.append(ride.name) } print(originalNames) |
In the assistant editor, you’ll see that sorting rides inside of sortedNames(:)
didn’t affect the list that was passed in. The modular function you’ve created could be considered quasi-functional with all that imperative code. The logic of sorting rides by name has been captured in a single, testable modular and reusable function.
Still, the imperative code made for a pretty long and unwieldy function. Wouldn’t it be nice if there were techniques to simplify the code within a function like sortedNames(:)
even further?
Another characteristic of FP languages is that functions are first-class citizens, meaning that functions can be assigned to values and passed in and out of other functions. There’s a higher level yet: higher-order functions. These accept functions as parameters or return other functions.
In this section, you’ll work with some of the most common higher-order functions in FP languages, namely filter, map and reduce.
In Swift, filter(:)
function is a method on CollectionType
values, such as Swift arrays, and it accepts another function as its single parameter as it maps the type of values in the array to a Swift Bool
.
filter(:)
applies the input function to each element of the calling array and returns another array that has only the array elements for which the parameter function returns true
.
This back to your list of actions that sortedNames
did:
Think about this declaratively rather than imperatively.
Start by commenting out sortedNames
because you are going to write this much more efficiently. Create a function for Ride
values to be used as a function parameter at the bottom of the playground:
func waitTimeIsShort(ride: Ride) -> Bool { return ride.waitTime < 15.0 } |
The function waitTimeIsShort(:)
accepts a ride and returns true
if the ride wait time is less than 15 minutes, otherwise it returns false
.
Call filter
on your park rides and pass in the new function you just created:
var shortWaitTimeRides = parkRides.filter(waitTimeIsShort) print(shortWaitTimeRides) |
You defined shortWaitTimeRides
as a var
only because you’ll reuse it in the playground. You’ll repeat that approach elsewhere. In the playground output, you only see Crazy Funhouse and Mountain Railroad in the call to filter’s output, which is correct.
Since Swift functions are just named closures, you can produce the same result by passing a trailing closure to filter
and using closure-syntax:
shortWaitTimeRides = parkRides.filter { $0.waitTime < 15.0 } print(shortWaitTimeRides) |
Here, .filter() is taking every ride in the parkRides array ($0), looking at its waitTime property, and gauging if it is less than 15.0. You are being declarative and telling the program what you want it to do instead of how it is done. This can look rather cryptic the first few times you work with it.
The CollectionType
method map(:)
also accepts a single function as a parameter, and in turn, it produces an array of the same length after being applied to each element of the collection. The return type of the mapped function does not have to be the same type as the collection elements.
Apply map
to the elements of your parkRides
array to get a list of all the ride names as strings:
let rideNames = parkRides.map { $0.name } print(rideNames) |
You can also sort and print the ride names as shown below, when you use the (non-mutating) sort
method on MutableCollectionType
to perform the sorting:
print(rideNames.sort(<)) |
Your sortedNames(:)
method from before is now just two lines thanks to map(:)
, sort(:)
and use of a closure!
The CollectionType
method reduce(:,:)
takes two parameters. The first is a starting value of a generic type T
, and the second is a function that combines a value of type T
with an element in the collection to produce another value of type T
. The input function applies to each element of the calling collection, one-by-one, until it reaches the end of the collection and produces a final accumulated value of type T
.
Suppose you want to know the average wait time of all the rides in your park.
Pass in a starting value of 0.0 into reduce
and use a trailing-closure syntax to add in the contribution of each ride to the average, which is its wait time divided by the total number of rides.
let averageWaitTime = parkRides.reduce(0.0) { (average, ride) in average + (ride.waitTime/Double(parkRides.count)) } print(averageWaitTime) |
In this example, the first two iterations look like the following:
| iteration | average | ride.waitTime / Double(parkRides.count) | resulting average ||—|—|—|—||1|0.0|45.0 / 8.0 = 5.625 | 0.0 + 5.625 = 5.625||2|5.625|10.0 / 8.0 = 1.25 | 5.625 + 1.25 = 6.875|
As you can see, the resulting average carries over as the average for the following iteration. This continues until reduce
iterates through every Ride
in parkRides
. This allows you to get the average with just one line of code!
One of the more advanced techniques in FP is currying, named after mathematician Haskell Curry. Currying essentially means thinking of multi-parameter functions as a sequence of function applications for each parameter, one-by-one.
Just to be clear, in this tutorial, Curry is not a delicious, spicy meal that elicits tears of joy, but you can demonstrate currying by creating a two-parameter function that separates the two parameters via parentheses:
func rideTypeFilter(type: RideType)(fromRides rides: [Ride]) -> [Ride] { return rides.filter { $0.types.contains(type) } } |
Here, rideTypeFilter(:)(:)
accepts a RideType
as its first parameter and an array of rides as its second.
One of the best uses of currying is to make other functions. By calling your curried function with just one parameter, you make another function that will only require one parameter when called.
Try it for yourself by adding the following RideType
filter factory to your playground:
func createRideTypeFilter(type: RideType) -> [Ride] -> [Ride] { return rideTypeFilter(type) } |
Notice that createRideTypeFilter
returns a function that maps an array of rides to an array of rides. It does so by calling rideTypeFilter
and passing in only the first parameter. Use createRideTypeFilter
to create a filter for kid rides, and then apply that filter to your list of park rides, like so:
let kidRideFilter = createRideTypeFilter(.Kids) print(kidRideFilter(parkRides)) |
In the playground output, you see that kidRideFilter
is a function that filters out all non-kid rides.
One of the primary concepts in FP that leads to the ability to reason consistently about program structure, as well as confidently test program results, is the idea of a pure function.
A function can be considered pure if it meets two criteria:
A pure function’s existence is closely tied to the usage of immutable states.
Add the following pure function to your playground:
func ridesWithWaitTimeUnder(waitTime: Double, fromRides rides: [Ride]) -> [Ride] { return rides.filter { $0.waitTime < waitTime } } |
ridesWithWaitTimeUnder(:fromRides:)
is a pure function because its output is always the same when given the same wait time threshold and list of rides as input.
With a pure function, it’s simple to write a good unit test against the function. To simulate a unit test, add the following assert into your playground:
var shortWaitRides = ridesWithWaitTimeUnder(15.0, fromRides: parkRides) assert(shortWaitRides.count == 2, "Count of short wait rides should be 2") print(shortWaitRides) |
Here you’re testing if you always get a count of two rides when given the fixed input parkRides
and a wait time threshold of 15.0.
Pure functions are closely related to the concept of referential transparency. An element of a program is referentially transparent if you can replace it with its definition and always produce the same result. It makes for predictable code and allows the compiler to perform optimizations.
Pure functions satisfy this condition. If you’re familiar with Objective-C, then you’ll find that pre-processor #define
constants and macros are familiar examples of referentially transparent code items.
Check if the pure function ridesWithWaitTimeUnder
is referentially transparent by replacing its use with its function body:
shortWaitRides = parkRides.filter { $0.waitTime < 15.0 } assert(shortWaitRides.count == 2, "Count of short wait rides should be 2") print(shortWaitRides) |
The final concept to discuss is function recursion, which occurs whenever a function calls itself as part of its function body. In functional languages, recursion effectively replaces looping constructs that are used in imperative languages.
When the function’s input leads to the function being called again, it’s considered the recursive case. In order to avoid an infinite stack of function calls, recursive functions need a base case to end them.
Add a recursive sorting function for your rides by first making the Ride
struct conform to the Comparable
protocol by placing a Ride
extension in your playground:
extension Ride: Comparable { } func <(lhs: Ride, rhs: Ride) -> Bool { return lhs.waitTime < rhs.waitTime } func ==(lhs: Ride, rhs: Ride) -> Bool { return lhs.name == rhs.name } |
You’ve added the required Comparable
operator methods outside the extension, per the requirements of Swift operator-overloading. You’re calling one ride less than another ride if the wait time is less, and calling them equal if the rides have the same name.
Now, add a recursive quicksort
method to your playground:
func quicksort<T: Comparable>(var elements: [T]) -> [T] { if elements.count > 1 { let pivot = elements.removeAtIndex(0) return quicksort(elements.filter { $0 <= pivot }) + [pivot] + quicksort(elements.filter { $0 > pivot }) } return elements } |
quicksort(:)
is a generic function that takes an array of Comparable
values and sorts them by picking a pivot element, then it calls itself for elements before and after the pivot .
Add this to check the sorting of a list of rides:
print(quicksort(parkRides)) print(parkRides) |
The second line confirms that quicksort(:)
didn’t modify the immutable array that was passed in.
By considering the following problem, you can combine much of what you’ve learned here on FP and get a clear demonstration of the differences between imperative and functional programming:
Momentarily ignore all you’ve learned about FP so far and think about how you would solve this problem with an algorithm. You would probably:
.Family
Step-by-step, this is how you’d answer the problem statement. Here it is as an algorithm implementation, please add it to your playground:
var ridesOfInterest = [Ride]() for ride in parkRides { var typeMatch = false for type in ride.types { if type == .Family { typeMatch = true break } } if typeMatch && ride.waitTime < 20.0 { ridesOfInterest.append(ride) } } var sortedRidesOfInterest = quicksort(ridesOfInterest) print(sortedRidesOfInterest) |
You should see that Mountain Railroad, Crazy Funhouse and Grand Carousel are the best bets, and they’re listed in order of increasing wait time. You’ve taken advantage of the quicksort(:)
function written above to do the sorting.
As written, the imperative code is acceptable, but a quick glance does not give a clear, immediate idea of what it’s doing. You have to pause to look at the algorithm in detail to grasp it. No big deal you say? What if you’re coming back to do some maintenance, debugging or you’re handing it off to a new developer? As they say on the Internet, “You’re doing it wrong!”
FP can do better. Add the following one-liner to your playground:
sortedRidesOfInterest = parkRides.filter({ $0.types.contains(.Family) && $0.waitTime < 20.0 }).sort(<) |
You’ve used the familiar methods filter
and contains
on Set
to find matching rides, as well as sort
from before.
Verify that this single line produces the same output as the imperative code by adding:
print(sortedRidesOfInterest) |
There you have it! In one line, you’ve told Swift what to calculate; you want to filter your parkRides
to .Family
rides with wait times less than 20 minutes and then sort them. That precisely – and elegantly – solves the problem statement.
FP not only makes your code more concise, but also makes what it does self-evident. I dare you to find a side effect!
The above one-line solution could be considered slightly opaque, but a modified variation can take advantage of the curried filter factory method you made earlier to create a family ride filter:
let familyRideFilter = createRideTypeFilter(.Family) sortedRidesOfInterest = ridesWithWaitTimeUnder(20.0, fromRides: familyRideFilter(parkRides)).sort(<) print(sortedRidesOfInterest) |
Here you produce the same result in a slightly more human-friendly form. Once you have the family ride filter, you can state the solution to the problem in one line of code again.
Swift is not a purely functional language, but it does combine multiple programming methodologies to give you flexibility for application development.
A great place to start working with FP techniques is in your Model layer, your ViewModel layer, and anywhere that your application’s business logic appears.
For user interfaces, how to use FP techniques is a little less clear. FRP (Functional Reactive Programming) is an example of an approach to FP for UI development. For example, Reactive Cocoa is an FRP library for iOS and OS X programming.
In terms of why to use FP, hopefully this tutorial has given you some good reasons and ideas for use.
By taking a functional, declarative approach, your code can be more concise and clear. As if you need another reason to work with FP, keep in mind that your code easier to test when isolated into modular functions that are free from side effects.
Finally, as multi-processing cores become the norm for both CPUs and GPUs, minimizing side effects and issues from concurrency will become increasingly important, and FP will one of the most important ways to achieve smooth performance!
You can download the complete playground with all the code in this tutorial here.
While you’ve reviewed many of the important concepts of FP in this tutorial, you’ve only scratched the surface of what FP is and what it can do. Once you have some experience with these basic FP ideas, take the plunge into the heart of FP:
You’ll get great insights into various aspects of Swift by studying both of the above. You might be able to do things you never dreamed possible with your new-found knowledge.
Also, I highly recommended checking out:
Functional Programming: The Failure of State, a talk given by Robert C. Martin in 2014
Learning Functional Programming without Growing a Neckbeard, a talk by Kelsey Innis. It’s focused on Scala but has lots of good info.
The post Introduction to Functional Programming in Swift appeared first on Ray Wenderlich.
This is just a quick update that RWDevCon 2016 is now sold out!
I am amazed and overjoyed by the enthusiastic response from the community for this conference – we are set to have 250 attendees at the conference this year.
The team and I are hard at work on the conference – we’ll make this an event to remember!
If you didn’t manage to get a ticket:
For the attendees and sponsors of RWDevCon – thanks so much for being a part of the conference, and we can’t wait to see you in DC! :]
The post RWDevCon 2016 Sold Out appeared first on Ray Wenderlich.
Learn about UIAppearance and how to use the appearance proxy with trait collections to customize the look of things in your app by Size Class.
The post Video Tutorial: Adaptive Layout Part 6: Appearance Proxy appeared first on Ray Wenderlich.
Welcome to our fourth annual Readers’ Apps Awards! We want to recognize a few of the best apps built by the raywenderlich.com community this year.
You guys submitted over 600 apps this year, so I enlisted some help to pick our winners. We had a team of judges review the top submissions including Ray Wenderlich, Michael Briscoe, Joshua Greene, Luke Parham, Jeff Rames, and myself.
I’ve tallied up our votes for the best apps in 7 familiar categories:
We also chose 2 grand prize winners:
And now for the apps you’ve been waiting for, I present the best readers’ apps of 2015!
Lettercraft
Lettercraft is another great game from the team that brought you GREG that makes word searches fun again.
Lettercraft has a time sensitivity that makes word searches unique. Every few seconds the board advances a level of heat. When tiles get too hot they burn up and can’t be used. Each time you use a letter to make a word, that tile drops a level of heat.
In addition to survival mode, Lettercraft has 70 levels of challenging constraints to keep things interesting. Things like only making words that contain the letter A or 5 letter words only.
“This is the kinda game you don’t want to put down. It’s so much fun to frantically come up with words while trying to avoid letters from being destroyed. A lot of polish and love was put into this game!” – Ray Wenderlich
“The best kind of addictive game – it’s fun while helping you exercise your gray matter with challengingly timed gameplay.” – Jeff Rames
“I don’t normally play word games, but when I do I play Lettercraft.” – Michael Briscoe
“I usually don’t like letter games like Words With Friends or Scrabble, but the pressure and pace of this game made it super hard to put down!” – Luke Parham
“Don’t burn, don’t burn, don’t burn! Oh noes, I needed that letter. I’d write a great review for this game, but I can’t stop playing it long enough to do so!” – Joshua Greene
Barcodas
Barcodes are all around us. We use them to track inventory, identify things, and more. But have you ever used a barcode for music? Barcodas is here to change just that.
With Barcodas, simply scan any barcode and you’ll immediately hear it converted to a musical tune. You can tweak pitches, speed, and scales to customize your new barcode melody. Its really cool! Each barcode becomes its own rhythmic synthesizer allowing you to create unique music in just a snap of your camera.
“After downloading this app I spent the next half hour running around my house and scanning barcodes with delight – I think Vicki thought I’d gone crazy.” – Ray Wenderlich
“It’s not only an amusing concept, but it’s very well done – the generated tunes were pleasing and had me searching for barcodes.” – Jeff Rames
“Great idea! Some of the music sounded eerily appropriate to what I scanned.” – Michael Briscoe
“This app is great, but it presents a problem: you’ll want to compare products by which bar code sounds better!” – Joshua Greene
Don’t Crack
Don’t Crack is a unique, fun game centered around not breaking vases as they come off the assembly line.
As the vases roll off the assembly line one at a time, you control the packing peanut release. Vases are in a crate and you must fill it with enough packing peanuts to make sure it won’t break during shipping. But you’ll have to be sparing, there are a limited amount of packing peanuts and you only get filled up every so often.
“Who thought packing boxes could be so much fun?! 5/5 would pack vases again.” – Ray Wenderlich
“A simple and goofy premise, but it’s a fun use of the physics system that makes for an addicting (though frustratingly difficult) game.” – Jeff Rames
“Good graphics, and convincing physics kept me playing for far too long.” – Michael Briscoe
“This was another really addicting game. It was just subtle enough that I had to sit there and think of how much foam to use to keep myself going.” – Luke Parham
Creature
Creature is a new tool made by professional animator with experience animating hit movies such as Wall-e, Up, TS3, Brave.
Creature is designed to make complex and gorgeous animations out of static 2D images. With skeletal animations you can give your character life and generate an entire range of movement. With physics simulations those skeletons become even more real as your tails bounce and your arms swing.
Creature also overs a variety of mesh deformations allowing even static images to flex and breath. Creature even supports motion capture from your camera to allow you to record your own movements!
“A physics and procedural animation engine isn’t the kind of thing you imagine coming from a small studio. This app does not only that, but adds all the bells and whistles to make this a powerful tool for game designers.” – Jeff Rames
“I’ll have to use Creature in my next game. Graphic sorcery!” – Michael Briscoe
Comic Battle
Comic Battle will lead you through an intergalactic battle. You can choose your team and train them with any combination of over 60 abilities. Build strategies and characters. Challenge players around the world in real time battles. After each battle a unique comic is created that shows the battle. Share your victories in comic form!
“The combat animations are right out of the pages of a comic and make for uniquely entertaining gameplay.” – Jeff Rames
“Beautifully illustrated and engaging artwork.” – Michael Briscoe
“This game was really pretty and there was clearly a lot of attention paid to the menu system and battles themselves.” – Luke Parham
“Ever wanted to battle comic book characters? With Comic Battle, you can! This game is really beautiful, just like playing a comic come to life.” – Joshua Greene
I saved the best for last. These two were selected by our judges as the best app and game this year. I present our two Grand Prize Winners!
Lastronaut
Lastronaut is one of the last humans on earth, running for their life to board the final rocket leaving the planet. And its simply beautiful.
Running in an adrenaline fueled haze with rockets and shotguns abound, you’ve got to dodge everything the sentinel army can throw at you. New weapons fall from the sky as gifts from the heavens to help you fight your way to the last rocket.
“This game is super fun and reminds me of Jetpack Joyride on steroids. I’m a big fan of pixel art, slow motion effects, overpowered weapons, and special effect “juice” and this game has it in spades!” – Ray Wenderlich
“Beautiful graphics, an array of awesome weapons, and addictive side scrolling gameplay make this game a lot of fun. Slow motion explosives make it epic – if you’re the type of person that looks at explosions.” – Jeff Rames
“Retro, faced paced, and beautiful, packed with lot’s of juice. I love this game!” – Michael Briscoe
“This was easily the most fun game. It also just had the best look and feel. A lot of games have a cheesy or half done UI but this game felt like something straight from my Super Nintendo.” – Luke Parham
“Astronauts battling flying tanks using shotguns, rocket launchers, and flamethrowers? What more do you need!
The graphics are absolutely beautiful and the game play is really well done. Did I mention laser guns?!” – Joshua Greene
Paste
Copy and Paste as a longtime feature on Macs hasn’t changed much over the years. Paste is a powerful clipboard manager for your Mac that teaches your computer some fancy new tricks.
Paste saves a history of all the things you’ve copied for later. A simple keystroke shows everything you’ve copied in a beautiful fullscreen interface. You can select individual copies to paste them or even share them with other applications You can search your history by content, type, and application it was copied in.
“This is the best clipboard utility I’ve used – it’s integrated and simple while streamlining the task of finding what you need in a large clipboard history.” – Jeff Rames
“This app solve’s a problem I only had a notion was there. Being able to look back through your copy history any time you want is awesome and stops me from having to flip back and forth through windows when looking for something that used to be in my clipboard.” – Luke Parham
“Don’t you hate it when you’re editing something, frequently copying-and-pasting, and accidentally replace a copy you needed? This won’t ever happen again with Paste: get as many copy-and-paste slots as you need. The interface is easy to use and searchable history is a nice feature.” – Joshua Greene
Congratulations to all our winners! All winners get a free PDF of their choice from our site – we’ll be in touch to arrange delivery.
I’d like to thank all our judges who helped me make these difficult picks. And of course thanks to all our readers. We couldn’t have built this amazing community without all of you. Your awesome apps are why we write reviews at all. Looking forward to your apps in 2016!
Don’t forget, if you’ve made an app or game using our tutorials, we’d love to review your app too! Please submit it for next month.
As always, it was a load of fun playing with your apps and getting to see our tutorials in action! Its been another terrific year for apps and we can’t wait to see what you submit next year. Have a Merry Christmas!
The post Winners – Readers’ App Awards 2015! appeared first on Ray Wenderlich.
Learn about Adaptive Presentation and how a view controller can control its own presentation style at run time based on the current trait collection or any other conditions.
The post Video Tutorial: Adaptive Layout Part 7: Adaptive Presentation appeared first on Ray Wenderlich.
Review what you learned in our Adaptive Layout video tutorial series and find out what other resources you should check out.
The post Video Tutorial: Adaptive Layout Part 8: Conclusion appeared first on Ray Wenderlich.