In Apple’s own words, “Swift is an innovative new
programming language for Cocoa and Cocoa Touch”. From what I have seen so far I would agree
with Apple’s description. In this post I
will show how easy it is to access a REST based Web Service with Swift by
writing a simple iTunes search API.
So
lets get started. You will need XCode
beta 6 to work with Swift so if you have not already downloaded/installed it
you will need to do so now. Once you
have XCode 6 beta on your machine, start a new project and select the Single
View Application template.
On
the next option screen, enter the name of your project and then select Swift as
the language.
Finally
select the location of your project and now you are ready to start.
We
will begin by creating a ITunesSearchAPI class that will be used to make requests to Apple’s
iTunes search API. In this class we will
define a Protocol that will be used to return the results of our search. Lets start by defining our protocol:
protocol
ITunesSearchAPIProtocol {
func
didRecieveResponse(results: NSDictionary)
}
The ITunesSearchAPIProtocol
contains one function. This function
will receive an NSDictionary object that
contains the results of our request to iTunes.
Now lets start the ITunesSearchAPI
class. We will start off by defining two
properties like this
class
ITunesSearchAPI: NSObject {
var data: NSMutableData = NSMutableData()
var delegate: ITunesSearchAPIProtocol?
}
These properties are the data
property used to store the data coming back from the server and the delegate
property that is used to define our ITunesSearchAPIProtocol. Now lets create a searchItunesFor function that we will call to perform our search (Note this code has been updated for Swift 1.2, I have noted where changes were made form the original code).
func searchItunesFor(searchTerm: String) {
var request: NSURLRequest = NSURLRequest(URL: url)
connection.start()
}
}
}
}
//Clean up the search terms by replacing spaces with +
var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ",withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch,range: nil)
// Changed for Swift 1.2
// var escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
var urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music"
// Changed with Swift 1.2
// var url: NSURL = NSURL(string: urlPath)
if let url = NSURL(string: urlPath) {var request: NSURLRequest = NSURLRequest(URL: url)
//changed with Swift 1.2
// var connection: NSURLConnection = NSURLConnection(request: request,delegate: self,startImmediately: false)
if let connection = NSURLConnection(request: request,delegate: self,startImmediately: false) {
println("Search iTunes API at URL \(url)")
}
}
}
}
The first two lines clean up the search term string that was
passed into the searchItunesFor function
so it can be used to create our search URL.
We then use this URL to create a NSURL
object. That NSURL object is used to create an NSURLRequest
object and finally the NSURLRequest is
used to create a NSURLConnection object. Notice we set the startImmediately parameter, of the NSURLConnection,
to false, this prevents the request from starting immediately. If there is no additional code needed, you
can set this to True to make the request run immediately but I like setting it
to false and manually starting the connection.
This allows me to initialize anything needed prior to the request being
made. The last line is what manually
starts the NSURLConnection.
Now lets create the NSURLConnectionDataDelegate
functions. Here is the first function
which is called if the connection fails:
//NSURLConnection Connection failed
func connection(connection:
NSURLConnection!, didFailWithError error: NSError!) {
println("Failed with
error:\(error.localizedDescription)")
}
This next function is called when a new connection is
established. In this function we simply
clear the data property.
//New request so we need to clear the data
object
func connection(didReceiveResponse:
NSURLConnection!, didReceiveResponse response:NSURLResponse!) {
self.data = NSMutableData()
}
This function is called each time data is received from the
server. In this function we append the
new data to our data property.
//Append incoming data
func connection(connection:
NSURLConnection!, didReceiveData data: NSData!) {
self.data.appendData(data)
}
This last function is called after all information is
received from the server. In this
function we convert the data received from the server to a NSDictionary object. We then call the delegate’s didReceiveResponse function with the NSDictionay object. (Note this code has been updated for Swift 1.2, I have noted where changes were made form the original code)
//NSURLConnection delegate function
func connectionDidFinishLoading(connection: NSURLConnection!) {
//Finished receiving data and convert it to a JSON object
//Changed with Swift 1.2
// var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,options:NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary}
Now lets look at how we would use the ITunesSearchAPI class. The first thing we need to do is to add the ITunesSearchAPIProtocol to the list of protocols
that the ViewController adopts. To do
this, simply add the ITunesSearchAPIProtocol
to the comma-separated list following the name of the class’s super class like
this:
class
ViewController: UIViewController, ITunesSearchAPIProtocol
In the apps ViewController.swift file add one property like
this:
var api:
ITunesSearchAPI = ITunesSearchAPI()
This will set the api
property to our ITunesSearchAPI type and
initiate it. We then want to add the
following two lines to the viewDidLoad
function.
api.delegate = self;
api.searchItunesFor("Jimmy
Buffett")
These two lines sets the delegate of our api property to this object and then calls the searchItunesFor function passing it the string
“Jimmy Buffett” as the search term.
Finally add our delegate function like this:
func
didRecieveResponse(results: NSDictionary) {
println(results)
}
This function simply logs the response to the console. In future posts we will expand this function
to actually do something with the data.
The full ITunesSearchAPI.swift file should look like this:
import
UIKit
protocol
ITunesSearchAPIProtocol {
func didRecieveResponse(results: NSDictionary)
}
class
ITunesSearchAPI: NSObject {
var data: NSMutableData = NSMutableData()
var delegate: ITunesSearchAPIProtocol?
//Search iTunes
func searchItunesFor(searchTerm: String) {
//Clean up the search terms by
replacing spaces with +
var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString("
", withString: "+",
options: NSStringCompareOptions.CaseInsensitiveSearch,
range: nil)
var escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
var urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music"
var url: NSURL = NSURL(string: urlPath)
var request: NSURLRequest = NSURLRequest(URL:
url)
var connection: NSURLConnection = NSURLConnection(request:
request, delegate: self,
startImmediately: false)
println("Search iTunes API at URL \(url)")
connection.start()
}
//NSURLConnection delegate method
func connection(connection: NSURLConnection!,
didFailWithError error: NSError!) {
println("Failed with error:\(error.localizedDescription)")
}
//NSURLConnection delegate method
func connection(didReceiveResponse: NSURLConnection!,
didReceiveResponse response: NSURLResponse!) {
//New request so we need to clear the
data object
self.data = NSMutableData()
}
//NSURLConnection delegate method
func connection(connection: NSURLConnection!,
didReceiveData data: NSData!) {
//Append incoming data
self.data.appendData(data)
}
//NSURLConnection delegate method
func connectionDidFinishLoading(connection:
NSURLConnection!) {
//Finished receiving data and convert
it to a JSON object
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,
options:NSJSONReadingOptions.MutableContainers,
error: nil) as NSDictionary
delegate?.didRecieveResponse(jsonResult)
}
}
The full ViewController.swift file should look like this:
import
UIKit
class
ViewController: UIViewController, ITunesSearchAPIProtocol{
var api: ITunesSearchAPI =
ITunesSearchAPI()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after
loading the view, typically from a nib.
api.delegate = self;
api.searchItunesFor("Jimmy
Buffett")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be
recreated.
}
func didRecieveResponse(results:
NSDictionary) {
// Store the results in our table data
array
println(results)
}
}
Part 2: In part 2 we will expand on the code presented here to display the results, with album covers, in a UITableView. We will have two requirements for loading images, they must be loaded in the background so the UI does not freeze while the images load and to create an image cache so we do not reload images we already have. To read part 2, follow this link Access REST Web Service with Apple’s new Swift Language - Part 2
Hi Jon,
ReplyDeleteThanks for this post.
Can you please help in integrating Reachability class with Swift programming for network check.
I have seen a number of different Reachability class for iOS/OS X written in Objective-C. There is even a Reachability class in my book. I would recommend integrating one of them with Mix and Match like I documented in this post about using the RNCryptor library with Swift.
DeleteHi Jon,
ReplyDeleteIs there something missing from this half of the tutorial? I don't think tableData has been populated with the returned results. In my code I added this to my didReceiveResponse method: tableData = results["results"] as NSArray
I still have something else wrong, with my IB connections, since my table protocol methods aren't getting called to populate the table.
Usually the main reason for table protocol methods not being call is when I forget to connect the tableview to the dataSource and delegate. Are these set similar to this: http://i.stack.imgur.com/f4t7Y.png
DeleteI did not walk though how to connect these, I just said " In the view for the ViewController add a UITableView and then set the dataSource and delegate like you did with Objective-C." so my first thought is you may of missed this.
I have the same issue as Timothy. I've connected the tableview to the dataSource and delegate as you do in your image. I notice that the function returning tableData.count is called before the didReceiveResponse function. At that point tableData is empty so it returns 0. Is that a problem?
DeleteNone of the other table protocol methods are being called.
I believe I found the issue and I updated part 2 of the tutorial with the fix. I forgot to mention that we needed to update the didRecieveResponse function. It should look like this:
Deletefunc didRecieveResponse(results: NSDictionary) {
// Store the results in our table data array
println("Received results")
if results.count>0 {
self.tableData = results["results"] as NSArray
self.appsTableView.reloadData()
}
}
Sorry for the confusion.
Thanks, Jon! I was missing the reloadData call. And I had to connect the appsTableView outlet to the TableView in the .xib file
DeleteThanks Jon. Your change to didRecieveResponse was the last piece in my puzzle. I did get better results by changing the search from Jimmy Buffet to "Weird Al" Yankovic. ;)
DeleteYou know the app wsbroadcast.com ? You create your services , entities, and it creates the WS interfaces automatically.
ReplyDeleteworks fine
DeleteHi Jon,
ReplyDeleteCongratulations for your tutorial is very interesting and easy to understanding, especially for users like me who are starting in IOS world. Sorry about my ignorance, I am using XCode version 6.3.2 and I am getting some sintax errors how you can see in my shared image:
https://goo.gl/photos/uLppVkib99CKypky7
Do you have any ideia whats is going on?
Thks in advance
I have updated the examples on the github page to reflex the changes in Swift 1.2 but I forgot to make the changes here. Please have a look at this github page for now and will will update the tutorial in the next couple of days: https://github.com/hoffmanjon/RSNetworking
ReplyDeleteSorry for the confusion
This comment has been removed by the author.
DeleteThanks for your prompt response and sharing your knowledge with us!
DeleteI checkout the github project and works fine. The source code of new version is very different from this version. I will wait for your update in blog to understanding the code behavior.
Regards
You are welcome, I updated the code for Swift 1.2 and also noted were the changes occurred. Please let me know if you have any questions.
Delete