Using threads for a JSON request
Make sure you have JSON Framework installed first.
This is a continuation of Post JSON to a webservice
So if you are writting your webservice application and you have to wait a few seconds to make the request and get the response you will notice that your main application gets locked up. This is due to having to wait for the request to complete and nothing else can work till that request is completed.
So here is where we introduce threads. So the idea is this. We will click on a cell and load the view. It will be a blank view and then we will launch a thread to grab the information. Once we have that information the thread will complete and we can load the table.
So we will start off using the following source code. This is code from the UIImageView in a custom cell example.So for here we need to change our PHP JSON endpoint. We want to simulate the wait so we will just toss in a sleep(5); or such. So the endpoint now looks like this
header('Content-type: application/x-json');
$id = (int)$_GET['id'];
$array = array(
1 => array(
'id' => 1,
'title' => 'Brown Dog',
'img' => 'http://iphone.zcentric.com/files/1.jpg',
),
2 => array(
'id' => 2,
'title' => 'Black Dog',
'img' => 'http://iphone.zcentric.com/files/2.jpg',
),
3 => array(
'id' => 3,
'title' => 'Two Dogs',
'img' => 'http://iphone.zcentric.com/files/3.jpg',
),
4 => array(
'id' => 4,
'title' => 'Girl',
'img' => 'http://iphone.zcentric.com/files/4.jpg',
),
);
sleep(5);
echo json_encode($array[$id]);
So lets change the endpoint in the code now. Open up JSONViewController.m and in the viewDidLoad method change the NSURL line to look like.
NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get2.php?id=%@", self.itemID, nil]];
You can see the file use to be called test-json-get.php, I just made it test-json-get2.php. The new file has our sleep in it. If you click build and go now, you will notice that if you click on a cell to view it you will experience some delay and the UI is in a locked state till the 5 seconds are up. Once the 5 seconds are up the new view is displayed.
So our goal is to first load that new view and then display like a activity indicator in the upper right. Now you might be saying.. why use threads? Just display the indicator before making the JSON call. Well the reason is while we make the request is that the UI is locked and the indicator will stop so we want to do the request in a background thread so our main UI will continue to work.
So the first thing we want to do is move the JSON code from viewDidLoad into a new method. Lets call it getJSON. So here is what that function should look like
- (void)getJSON {
NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get2.php?id=%@", self.itemID, nil]];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
else {
self.jsonItem = [jsonData JSONValue];
// setting up the title
self.jsonLabel.text = [self.jsonItem objectForKey:@"title"];
// setting up the image now
self.jsonImage.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [self.jsonItem objectForKey:@"img"]]]];
}
}
Your viewDidLoad should now be an empty method. So now lets put the activity indicator in there. So now the function should look like
- (void)viewDidLoad {
CGRect frame = CGRectMake(0.0, 0.0, 25.0, 25.0);
UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[loading startAnimating];
[loading sizeToFit];
loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
// initing the bar button
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:loading];
[loading release];
loadingView.target = self;
self.navigationItem.rightBarButtonItem = loadingView;
}
So you can see we are initing a new UIActivityIndicatorView and then creating a new bar button item and using our new indicator view as the bar item’s view. Then we are placing that item in the right hand side of the navigation bar.
So if we click build and go we will get something like this when we click on a cell.
So now we need to do the threading. So our JSON request got moved into a new function so that is where we will do the bulk of the request. So lets set that up.
At the top of the getJSON method you want to put the following code.
NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];
At the bottom of that method you want to put the following
[pool release];
So we are creating an auto release pool. Any items that we are set to autorelease in-between the two lines above won’t be released until we call [pool release]
So now we just need to init the thread. So at the bottom of viewDidLoad method add the following
[NSThread detachNewThreadSelector: @selector(getJSON) toTarget:self withObject:nil];
So that will call the method getJSON in a new thread. Now when you click build and go and click a cell and wait 5 seconds you will see the following.
You will notice that our indicator is still there, so we should remove that. So at the bottom of getJSON we want to remove it once the request is done. So add the following line
self.navigationItem.rightBarButtonItem = nil;
That is all you need for threads. Very simple huh? As always you can grab the code here.
Post JSON to a webservice
Make sure you have JSON Framework installed first.
This is a continuation of UIImageView in a custom cell
Here is a simple little code snippet on how to post some JSON to a webservice.
NSString *requestString = [NSString stringWithFormat:@"json=%@", [loginDict JSONFragment], nil];
NSData *requestData = [NSData dataWithBytes: [requestString UTF8String] length: [requestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: @"http://domain.com/api/login/"]];
[request setHTTPMethod: @"POST"];
[request setHTTPBody: requestData];
NSData *returnData = [ NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil ];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding: NSUTF8StringEncoding];
// decoding the json
NSDictionary *loginArray = [NSDictionary alloc];
loginArray = [returnString JSONValue];
So below is what each line does.
- First we format the request string. What I am doing there is taking a whole dictionary and making it a JSON object. I then assign that to the json variable to post in. So in PHP land that would come in as $_POST['json'] and I could do like json_decode($_POST['json']) and turn it into an array I can work with in PHP
- The next line turns the request into a data object so we know how big the string is.
- Now we setup the URL to post the request to
- We are setting the method to a post in html land it is like <form method=”POST”>
- Now are are setting up the body of the post with our data
- We now make the connection to the server and then get a data string back
- We decode the data stream into a NSString object
- Since you send in JSON I expect you get JSON back so we take the contents of the NSString and turn it into a NSDictionary
Pretty simple. That is all there is to it.
UIBarButtonItem with a custom view
The main project I am doing for Photoblog is an application to show all the latest entries and such. So I call a web service using JSON and it gives me the data I need. Now there might be a few second delay in sending the request and getting the request back. Once I get the request back I open my new view. So one way to show the user that activity is going on is creating a activity indicator.
Apple has UIActivityIndicatorView which we will use. Now I want to put this in the top navigation bar but by default you cannot. So we have to set a UIBarButtonItem then use a custom view option. So here is the function I created to do this
-(void)showLoading {
// initing the loading view
CGRect frame = CGRectMake(0.0, 0.0, 25.0, 25.0);
UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithFrame:frame];
[loading startAnimating];
[loading sizeToFit];
loading.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin);
// initing the bar button
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:loading];
[loading release];
loadingView.target = self;
self.navigationItem.rightBarButtonItem = loadingView;
}
This isn’t anything major. It will create the UIActivityIndicatorView with a width and height of 25. It will start is animating and make it fit into the frame we created.
Then I init a UIBarButtonItem and use a initWithCustomView method to set it to use our activity indicator view. I then release the indicator and set the click target to self. I then place that button item on the right side of the bar.
Now I do this right before I begin the process of sending a request and getting the response. The problem is that I never see the animation due to the request and responsive locking the main thread.
So when I have threads figured out we will do the JSON request in a thread so the main thread isn’t locked.
Append to a NSString
I was working on appending to some strings and there is a way to do it with NSString but you are better off working with NSMutableString instead. I have a feeling doing it with NSString, if not done correctly, will leak memory.
NSMutableString *hello = [[NSMutableString alloc] initWithString:@"Hello"];
[hello appendString:@" World"];
UIImageView in a custom cell
Make sure you have JSON Framework installed first.
This is a continuation of Click a Cell
So there has been some requests to do this. If you want to display a image in a custom cell it is pretty easy. It is about the same as doing the title and the URL to the image we did before. This will take over where click a cell left off. So you can grab the source code from there and start.
So we will be placing a UIImageView on the right part of the cell. To make it easy, we will still keep the title and the URL in the cell. So the first thing you want to do is open up ImageCell.h and we need to add the references to the new UIImageView we will use. I have commented the two lines I have added in this file
@interface ImageCell : UITableViewCell {
UILabel *titleLabel;
UILabel *urlLabel;
NSInteger *itemID;
UIImageView *imageView; // added this
}
// these are the functions we will create in the .m file
-(void)setData:(NSDictionary *)dict;
-(NSInteger)getID;
-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;
@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;
@property (nonatomic, assign) NSInteger *itemID;
@property (nonatomic, retain) UIImageView *imageView; // added this
@end
Now we want to create a temporary image. When doing a webservice application it is key to “die” gracefully. For example if the service is alive put the image is not there, we want to show a no picture image so something is displayed. So I am using the following image (nopic-small.png).
Save this image and then drag it into your project so Xcode knows you are using this image.
Ok now lets init the UIImageView, so open up ImageCell.m and we need to synethsize our new image object.
@synthesize imageView;
Now look for the initWithFrame method. You want to add in the following
UIImage *noPicImageSmall = [[UIImage imageNamed:@"nopic-small.png"] retain];
self.imageView = [[UIImageView alloc] initWithImage:noPicImageSmall];
[myContentView addSubview:self.imageView];
[self.imageView release];
So the above code we are creating a new UIImage that contains the our no-pic image. Then we init our image view with the contents of our no-pic UIImage. We then add it to the sub-view and release the image.
So if we click build and go right now we should get something that looks like this
You will see the no-pic images are placed in the upper left corner of the cell and way too big, so lets draw them in the correct place and the correct size.
So no look for the layoutSubviews method and add in the following right below where we drew the titleLabel and urlLabel.
self.imageView.frame = CGRectMake(boundsX + 200, 5, 30, 30);
The boundsX + 200 is the spacing on the left
The 5 is the spacing from the top
The first 30 is the width of the view
The second 30 is the height of the view
You will notice in the line above that I switched from 2 lines of code to 1. This is actually better coding practices then above if you just need the CGRectMake once.
So if you click build and go now you will get something that looks like this
Now we need to populate that UIImageView with our image in the URL for the cell. We will do that in the setData method. So that should now look like
-(void)setData:(NSDictionary *)dict {
self.titleLabel.text = [dict objectForKey:@"title"];
self.urlLabel.text = [dict objectForKey:@"img"];
self.itemID = (NSInteger)[dict objectForKey:@"id"];
// setting up the imageView now
self.imageView.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [dict objectForKey:@"img"]]]];
}
So you can see we added in the self.imageView.image = line. We are setting it to a new UIImage and loading it with data from the contents of a URL. You have to pass in a NSURL object to that, I don’t think it will accept a string. Pretty much you turn the string into a URL object and tell UIImage to grab that.
Don’t forget to dealloc the image view
- (void)dealloc {
// make sure you free the memory
[titleLabel dealloc];
[urlLabel dealloc];
[imageView dealloc];
[super dealloc];
}
Now when you click build and go, you will see the following
You can grab the source here.
Click a Cell
Make sure you have JSON Framework installed first.
This is a continuation of JSON Error
So here is a quick tutorial on how to click on a cell and load some JSON data for that cell. So the idea is you have a unique ID for each set of data you have.
So for that I am going to change the php code to add a ID
<php
header('Content-type: application/x-json');
$id = (int)$_GET['id'];
$array = array(
1 => array(
'id' => 1, // added this
'title' => 'Brown Dog',
'img' => 'http://iphone.zcentric.com/files/1.jpg',
),
array(
'id' => 2, // added this
'title' => 'Black Dog',
'img' => 'http://iphone.zcentric.com/files/2.jpg',
),
array(
'id' => 3, // added this
'title' => 'Two Dogs',
'img' => 'http://iphone.zcentric.com/files/3.jpg',
),
array(
'id' => 4, // added this
'title' => 'Girl',
'img' => 'http://iphone.zcentric.com/files/4.jpg',
),
);
echo json_encode($array);
?>
So now lets go back to the code. In ImageCell.h we want to add a new id for the cell that we will set in our setData function and also we want to create a getID function so we know what ID we clicked on.
So here is what the .h file should look something like now
#import
@interface ImageCell : UITableViewCell {
UILabel *titleLabel;
UILabel *urlLabel;
NSInteger *itemID; // added this
}
// these are the functions we will create in the .m file
-(void)setData:(NSDictionary *)dict;
-(NSInteger)getID; // added this
-(UILabel *)newLabelWithPrimaryColor:(UIColor *)primaryColor selectedColor:(UIColor *)selectedColor fontSize:(CGFloat)fontSize bold:(BOOL)bold;
@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *urlLabel;
@property (nonatomic, assign) NSInteger *itemID; // added this
@end
Now open up the ImageCell.m and add the following line into the initWithFrame function
self.itemID = 0;
Now you have to synthesize that itemID
@synthesize itemID;
Now scroll down to the setData function and we need to set up the itemID with what we pass in from RootViewController. So it looks like this
-(void)setData:(NSDictionary *)dict {
self.titleLabel.text = [dict objectForKey:@"title"];
self.urlLabel.text = [dict objectForKey:@"img"];
self.itemID = (int)[dict objectForKey:@"id"];
}
The reason we have to do the (int) in front of it is how objectForKey returns the data. We need to cast it into an int since we declared it as one.
Now lets create our getID function so add in the following
-(NSInteger)getID {
return self.itemID;
}
So since we set the itemID to 0 in the init later in the code we can always do some error checking. So if it is 0 there must be a problem, else grab the data.
Now save the file and we need to create a new view. Follow the following entry for how to do that in IB. Just follow the IB sets, you can ignore the code, we will fill that in later. Also don’t forget to follow the step for creating a new ViewController!. Call the new XIB from IB JSONView and the new controller JSONViewController.m.
Now we need to create some outlets in IB.
So click on the File’s Owner icon and you can to click on the Controller Identity Tab in the Inspector. (Tools -> Identity Inspector)
Click the + icon for class Outlets and you want it to look like the following. For my example I dragged a UILabel and a UIImageView onto the main view so we need to tell the application that these will be usable variables in the code.
Now that we have the items on the view and the outlets set, we have to tell IB what the outlets will attach to. So open the Connections Inspector tab (Tools -> Connection Inspector) and you should see the new outlets there
They are jsonImage and jsonLabel. So now all you are doing is clicking on the circle to the right and dragging over to the UIImageView and releasing and that will setup the connection between the outlet and the item you placed. Do the same for the UILabel.
Now everything in IB is all setup, so lets get back to some code.
So we should already have the JSONViewController.h/.m file in place since we created the new view, so lets make it work. So open up JSONViewController.h and you want it to look like
#import
@interface JSONViewController : UIViewController {
IBOutlet UILabel *jsonLabel;
IBOutlet UIImageView *jsonImage;
NSDictionary *jsonItem;
NSInteger *itemID;
}
-(void)setID:(NSInteger)val;
@property (nonatomic, retain) UILabel *jsonLabel;
@property (nonatomic, retain) UIImageView *jsonImage;
@property (nonatomic, retain) NSDictionary *jsonItem;
@property (nonatomic, assign) NSInteger *itemID;
@end
So you can see one new thing. The IBOutlet in front of the UIImage and UIImageView. These is basically there to tell IB that these are outlets. So everything else is nothing new. The one thing you will see is the method setID, we will use that to get in the ID of the clicked on cell. We need to know the ID of the data set we want to grab right?
Here is what my JSONViewController.m looks like
#import "JSONViewController.h"
#import <JSON/JSON.h>
@implementation JSONViewController
@synthesize jsonLabel, jsonImage, jsonItem, itemID;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Initialization code
self.itemID = 0;
}
return self;
}
-(void)setID:(NSInteger)val {
self.itemID = (NSInteger *)val;
}
/*
Implement loadView if you want to create a view hierarchy programmatically
- (void)loadView {
}
*/
- (void)viewDidLoad {
// init the url
NSURL *jsonURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://iphone.zcentric.com/test-json-get.php?id=%@", self.itemID, nil]];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
else {
self.jsonItem = [jsonData JSONValue];
// setting up the title
self.jsonLabel.text = [self.jsonItem objectForKey:@"title"];
// setting up the image now
self.jsonImage.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [self.jsonItem objectForKey:@"img"]]]];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[jsonLabel dealloc];
[jsonImage dealloc];
[self.jsonItem dealloc];
[super dealloc];
}
@end
Now you will notice a few things
- I do not init the jsonLabel and jsonImage anywhere. IB does this all for me
- You can see I uncommented the viewDidLoad method and did the JSON call in there. There are a few reasons for that. First we want to make sure the class is fully inited before making the call. We do this since we want to pass in the ID of the data set.
- You can see how I am setting up the title and the imageview in the code.
Now save that file and open up RootViewController.m
We want to make the didSelectRowAtIndexPath method to look like
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// load the clicked cell
ImageCell *cell = (ImageCell *)[tableView cellForRowAtIndexPath:indexPath];
// init the controller
JSONViewController *controller = [[JSONViewController alloc] initWithNibName:@"JSONView" bundle:nil];
// set the ID and call JSON in the controller
[controller setID:[cell getID]];
// show the view
[self.navigationController pushViewController:controller animated:YES];
}
The one odd thing is we are calling cellForRowAtIndexPath again when we click. This is because the cells are cached and we want to return that cell again if it already created.
That is all there is to it. If it works the view should look something like this if you click the same cell I did
As always you can grab the code here.
Using NSUserDefaults
This will go over a quick run through of using NSUserDefaults. This is the class to use to save user preferences for example. It is pretty straight forward.
Saving Data
This is how you save data.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving a string
[prefs setObject:@"test" forKey:@"stringVal"];
// saving a int
[prefs setObject::23 forKey:@"intVal"];
// saving it all
[prefs synchronize];
Loading Data
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting the string
stringVal = [prefs stringForKey:@"stringVal"];
// getting the int
intVal = [prefs integerForKey:@"intVal"];
That is it.. pretty simple.
Create a text input
So here is a quick no real code tutorial. This is another thing that took me a bit to figure out.
For this example, I am putting a text input in a TableView. So here is my class declaration.
@interface OptionsViewController : UITableViewController <UITextFieldDelegate> { }
You need to put the UITextFieldDelegate as a reference class. Now I am going to create the text input in the .m file of the same class
// creating the input
self.usernameInput = [[UITextField alloc] initWithFrame:frame];
// setting it to a rounded border
self.usernameInput.borderStyle = UITextBorderStyleRoundedRect;
// text color is black
self.usernameInput.textColor = [UIColor blackColor];
// font size
self.usernameInput.font = [UIFont systemFontOfSize:17.0];
// the default text in light gray
self.usernameInput.placeholder = @"Username";
// background color of the box
self.usernameInput.backgroundColor = [UIColor whiteColor];
// sets up auto correction in case of spelling errors
self.usernameInput.autocorrectionType = UITextAutocorrectionTypeYes;
// shows the done key in the keyboard
self.usernameInput.returnKeyType = UIReturnKeyDone;
// shows the X button in the text input if a user wants to clear it quickly
self.usernameInput.clearButtonMode = UITextFieldViewModeWhileEditing;
// sets the delegate to the controller when we press the done so the controller knows to take control
self.usernameInput.delegate = self;
// adding it to my view
[MyContentView addSubview:self.usernameInput];
// releasing the memory
[self.usernameInput release];
That is about it!
Add a navigation button
This will pickup where Create a view left off. So you can pick it up from there and download the source. You probably don’t even need the source but grab it if you wish
Now when you create a new view, in that view you might want to place a button in the navigation bar. This is pretty easy to do. So you want to open up the sub-view controller. In my example I called it BrowseViewController. So I will open up BrowseViewController.m and look for the loadView function. In my example you will see it commented ( /* */ ) out. For example.
/*
Implement loadView if you want to create a view hierarchy programmatically
- (void)loadView {
}
*/
You will notice it is all green. To make it that function work just remove the /* and */ (don’t forget the comment text also) around it so now it looks like
- (void)loadView {
}
Now we want to add a button. So we just add in some code.
UIBarButtonItem *addButton = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Something", @"") style:UIBarButtonItemStyleBordered target:self action:@selector(doSomething)] autorelease]; self.navigationItem.rightBarButtonItem = addButton;
So you see we are creating a button with text Something. We are also setting the style as a button with a border. the target is the object it will call on button press and the action is the function. We are auto releasing it also so we don’t have to do a [addButton release]; If we were to add that the application would seg fault.
Now you have to create the function doSomething
- (void)doSomething {
}
There ya go.. all done. The final product should look like this.
You can grab the source code here.
JSON Error
If you want to show an error if the host is down it is pretty easy. This thinks you have read the other JSON posts. So look at the viewDidLoad function.
This is a continuation of Custom UITableViewCell
We are just adding a if {} else {} and if the data is nil, show an error.
- (void)viewDidLoad {
// Add the following line if you want the list to be editable
// self.navigationItem.leftBarButtonItem = self.editButtonItem;
// init the url
NSURL *jsonURL = [NSURL URLWithString:@"http://iphone.zcentric.com/test-json2.php"];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Down" message:@"The webservice you are accessing is down. Please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
else {
// converting the json data into an array
self.jsonArray = [jsonData JSONValue];
}
// releasing the vars now
[jsonURL release];
[jsonData release];
}
You can test this by simply changing the hostname to like iphone2.zcentric.com and it will show an error like the following.











