How to put SOAP response into UITableView? - ios

I use SOAP webservices in my app. I make an request with the following statements (.m file):
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *soapMsg =
[NSString stringWithFormat:
#"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance>\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema>\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/>\"><soap:Body><retLoca xmlns=\"http://www.ctrlbase.nl/cbawebapp>\"><tcUser>112</tcUser></retLoca></soap:Body></soap:Envelope>"];
NSURL *url = [NSURL URLWithString:
#"http://www.ctrlbase.nl/cbawebapp/getLoca.asmx>"];
NSMutableURLRequest *req =
[NSMutableURLRequest requestWithURL:url];
NSString *msgLength =
[NSString stringWithFormat:#"%d", [soapMsg length]];
[req addValue:#"text/xml; charset=utf-8"
forHTTPHeaderField:#"Content-Type"];
[req addValue:#"http://www.ctrlbase.nl/cbawebapp/retLoca>"
forHTTPHeaderField:#"SOAPAction"];
[req addValue:msgLength
forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.dimBackground = YES;
HUD.labelText = #"Ophalen van gegevens";
HUD.delegate = self;
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if( theConnection )
{
webData = [NSMutableData data];
}
else
{
NSLog(#"theConnection is NULL");
}
}
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response {
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data {
[webData appendData:data];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc]
initWithBytes: [webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
//---shows the XML---
NSLog(theXML);
if( xmlParser )
{
}
xmlParser = [[NSXMLParser alloc] initWithData:webData];
[xmlParser setDelegate:self];
[xmlParser setShouldResolveExternalEntities:YES];
[xmlParser parse];
[HUD hide:YES];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ( [elementName isEqualToString:#"retLocaResponse"] ) {
NSLog(#"Log Output%#",[attributeDict objectForKey:#"retLocaResult"]);
NSString *strValue= [attributeDict objectForKey:#"retLocaResult"];
if(strValue != (NSString*)[NSNull null])
{
[chunks addObject:[attributeDict objectForKey:#"retLocaResult"]];
}
else
{
}
recordResults = YES;
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:
(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ( [elementName isEqualToString:#"retLocaResponse"] ) {
NSLog(#"Log array%#",chunks);
[[self tableView]reloadData];
recordResults = NO;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [chunks count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
cell.textLabel.text =[chunks objectAtIndex:indexPath.row];
return cell;
}
But I don't get the Location names into an UITableView. What do I wrong or what can I do to let this work. Please help me so I can finish my application.
Thanks a lot!
EDIT for Samkit Jain: Crashes with the 'init' for the array.
LOG:
2013-08-09 00:12:31.631 Ctrlbase[776:c07] request finished
2013-08-09 00:12:31.904 Ctrlbase[776:c07] DONE. Received Bytes: 381
2013-08-09 00:12:31.904 Ctrlbase[776:c07] 0640. Home5 Cash & Carry
2013-08-09 00:12:32.028 Ctrlbase[776:c07] Log Output(null)
2013-08-09 00:12:32.159 Ctrlbase[776:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
-
Server request:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<retLoca xmlns="http://www.ctrlbase.nl/cbawebapp">
<tcUser>string</tcUser>
</retLoca>
</soap:Body>
</soap:Envelope>
Server response:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<retLocaResponse xmlns="http://www.ctrlbase.nl/cbawebapp">
<retLocaResult>string</retLocaResult>
</retLocaResponse>
</soap:Body>
</soap:Envelope>

Initialize chunks in viewDidLoad
- (void)viewDidLoad
{
chunks = [[NSMutableArray alloc] init];
//same code here
}
If this does not work.
Give me the complete log of your data coming from server and data from xml also.
Edit for Rick de Jong:
There is some problem in your getting data from server.
If you are not getting data from server, data can't be saved in array.
So, get your data properly from server.
When data will come properly from server :
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc]
initWithBytes: [webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
NSLog(#"Xml is :-----------%#",theXML); // This line should print your xml coming from from server
}
Give me that output, I will surely give you solution after that.
One more thing, when you are parsing data. your required data is coming in tags not as attribute of xml, as per your Soap. So you should use :
if ( [elementName isEqualToString:#"retLocaResult"] ) {
// code to save data and add to array
}
in didStartElement and didEndElement to get proper result.
One more error in your code remove >
in following code :
NSURL *url = [NSURL URLWithString:
#"http://www.ctrlbase.nl/cbawebapp/getLoca.asmx"];
Finally I got the output :
Use this :
.h file
{
NSMutableData *webData;
NSXMLParser *xmlParser;
NSMutableArray *chunks;
BOOL recordResults;
NSMutableString *strCaptured;
}
.m file
- (void)viewDidLoad
{
[super viewDidLoad];
chunks = [[NSMutableArray alloc] init];
NSString *temp = #"112";
NSString *soapMsg = [NSString stringWithFormat: #"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><retLoca xmlns=\"http://www.ctrlbase.nl/cbawebapp\"><tcUser>%#</tcUser></retLoca></soap:Body></soap:Envelope>",temp];
// NSString *soapMsg = [NSString stringWithFormat: #"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance>\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema>\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/>\"><soap:Body><retLoca xmlns=\"http://www.ctrlbase.nl/cbawebapp\"><tcUser>112</tcUser></retLoca></soap:Body></soap:Envelope>"];
NSURL *url = [NSURL URLWithString: #"http://www.ctrlbase.nl/cbawebapp/getLoca.asmx"];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] initWithURL:url];
NSString *msgLength = [[NSString alloc ]initWithFormat:#"%d",[soapMsg length]];
[theRequest addValue:#"text/xml" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
//[theRequest addValue:#"http://www.ctrlbase.nl/cbawebapp/retLoca>" forHTTPHeaderField:#"SOAPAction"];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if( theConnection )
{
webData = [[NSMutableData alloc] init];
}
else
{
NSLog(#"theConnection is NULL");
}
}
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response {
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data {
[webData appendData:data];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc]
initWithBytes: [webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
//---shows the XML---
NSLog(#"Xml is :-----------%#",theXML);
if( xmlParser )
{
}
xmlParser = [[NSXMLParser alloc] initWithData:webData];
xmlParser.delegate = self;
[xmlParser setShouldResolveExternalEntities:YES];
[xmlParser parse];
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
strCaptured = [[NSMutableString alloc]init];
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[self.tableView reloadData];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
recordResults = false;
[strCaptured setString:#""];
if ([elementName isEqualToString:#"retLocaResult"]) {
recordResults = true;
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
recordResults = false;
if ([elementName isEqualToString:#"retLocaResult"]){
[chunks addObject:strCaptured];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (recordResults
) {
[strCaptured appendString:string];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [chunks count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
cell.textLabel.text =[chunks objectAtIndex:indexPath.row];
return cell;
}

Related

IOS How to display NSXMLparser with NSMutable array into tableview?

I have a NSXML parser that is willing to retrieve data out and put it into NSMutable array. It works when i NSLog the array out. But when i display the array into the table view it fails. Is good if can tell me more specific on which error that i have because there is no error when running.
This is my ViewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
// output = [[NSMutableArray alloc]initWithObjects:#"red",#"green", nil];
NSLog(#"Response recieved");
output= [[NSMutableArray alloc] init];
feeds = [[NSMutableArray alloc] init];
// NSString *severity = #"Informational";
NSString *soapMessage = #"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
"<soap:Body>"
" <IncidentGetList xmlns=\"https://www.monitoredsecurity.com/\">"
"<Severity></Severity>"
"<SourceOrganization></SourceOrganization>"
"<DestinationOrganization></DestinationOrganization>"
"<MaxIncidents></MaxIncidents>"
"<SourceIP></SourceIP>"
"<Category></Category>"
"<ExcludeCategory></ExcludeCategory>"
"<StartTimeStampGMT></StartTimeStampGMT>"
"<EndTimeStampGMT></EndTimeStampGMT>"
"<CustomerSeverity></CustomerSeverity>"
"</IncidentGetList>"
"</soap:Body>"
"</soap:Envelope>";
NSURL *url = [NSURL URLWithString:#"https://api.monitoredsecurity.com/SWS/incidents.asmx"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapMessage length]];
[theRequest addValue: #"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue: #"https://www.monitoredsecurity.com/IncidentGetList" forHTTPHeaderField:#"SOAPAction"];
[theRequest addValue: msgLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
[connection start];
if(connection)
{
webResponseData = [NSMutableData data] ;
}
else
{
NSLog(#"Connection is NULL");
}
}
This is my NSXMLparser
//Implement the NSXmlParserDelegate methods
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:
(NSString *)qName attributes:(NSDictionary *)attributeDict
{
element = elementName;
if ([element isEqualToString:#"SecurityIncidentSummary"]) {
IncidentGetList = [[NSMutableDictionary alloc] init];
Severity = [[NSMutableString alloc] init];
} //NSLog(#"current element: ", elementName);
}
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"File found and parsing started");
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([element isEqualToString:#"Severity"]) {
[Severity appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"SecurityIncidentSummary"]) {
[IncidentGetList setObject:Severity forKey:#"Severity"];
[feeds addObject:[IncidentGetList copy]];
}
[self.datalist reloadData];
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"Final Feed : %#",feeds);
//[self.datalist reloadData];
}
This is my tableview
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
return [feeds count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"UITableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if(cell == nil){
cell= [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
cell.textLabel.text= [[feeds objectAtIndex:indexPath.row]objectForKey:#"Severity"];
//cell.textLabel.text= output[indexPath.row];
return cell;
}
Check out this XMLParser example which I have done.
In your .h file:
#interface RssViewController : UIViewController<NSXMLParserDelegate,UITableViewDataSource,UITableViewDelegate>
{
NSString *element;
NSXMLParser *xml;
NSMutableDictionary *item;
NSMutableString *title;
NSMutableString *link;
NSMutableString *description;
NSMutableArray *arrmute;
}
#property (weak, nonatomic) IBOutlet UITableView *tview;
In .m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
arrmute=[[NSMutableArray alloc]init];
NSURL *url=[[NSURL alloc]initWithString:#"http://news.google.co.in/news?cf=all&hl=en&pz=1&ned=in&topic=tc&output=rss"];
xml=[[NSXMLParser alloc]initWithContentsOfURL:url];
xml.delegate=self;
xml.shouldResolveExternalEntities=YES;
[xml parse];
}
NSXMLParser delegates methods :-
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
element = elementName;
if ([element isEqualToString:#"item"])
{
item=[[NSMutableDictionary alloc] init];
title=[[NSMutableString alloc] init];
link=[[NSMutableString alloc] init];
description=[[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([element isEqualToString:#"title"])
{
[title appendString:string];
}
else if ([element isEqualToString:#"link"])
{
[link appendString:string];
}
else if ([element isEqualToString:#"description"])
{
[description appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"item"]) {
[item setObject:title forKey:#"title"];
[item setObject:link forKey:#"link"];
[item setObject:description forKey:#"description"];
[arrmute addObject:[item copy]];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[self.tview reloadData];
}
TableView Methods:-
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return arrmute.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=[[arrmute objectAtIndex:indexPath.row] objectForKey:#"title"];
cell.textLabel.numberOfLines = 0;
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
return cell;
}
Hope this will help you...

IOS How to parse the xml retrieve the element to put in an array and display in UITableView

- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Response recieved");
output= [[NSMutableArray alloc] init];
// NSString *severity = #"Informational";
NSString *soapMessage = #"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
"<soap:Body>"
" <IncidentGetList xmlns=\"https://www.monitoredsecurity.com/\">"
"<Severity></Severity>"
"<SourceOrganization></SourceOrganization>"
"<DestinationOrganization></DestinationOrganization>"
"<MaxIncidents></MaxIncidents>"
"<SourceIP></SourceIP>"
"<Category></Category>"
"<ExcludeCategory></ExcludeCategory>"
"<StartTimeStampGMT></StartTimeStampGMT>"
"<EndTimeStampGMT></EndTimeStampGMT>"
"<CustomerSeverity></CustomerSeverity>"
"</IncidentGetList>"
"</soap:Body>"
"</soap:Envelope>";
NSURL *url = [NSURL URLWithString:#"https://api.monitoredsecurity.com/SWS/incidents.asmx"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapMessage length]];
[theRequest addValue: #"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue: #"https://www.monitoredsecurity.com/IncidentGetList" forHTTPHeaderField:#"SOAPAction"];
[theRequest addValue: msgLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
[connection start];
if(connection)
{
webResponseData = [NSMutableData data] ;
}
else
{
NSLog(#"Connection is NULL");
}
// Menu View
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
// Insert Navigation Bar
// [self insertNavBarWithScreenName:SCREEN_INCIDENT];
//
// CGRect frm = btnDownArrow.frame;
// frm.origin.x = 185;
// frm.origin.y = 42;
// [btnDownArrow setFrame:frm];
// [self.navBarView addSubview:btnDownArrow];
//
//
//
// [self addGestureRecognizersToPiece:self.view];
}
- (void)connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Response recieved");
[self.webResponseData setLength:0];
}
- (void)connection:(NSURLConnection*) connection didReceiveData:(NSData *)data
{
NSLog(#"Data recieved");
// NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self.webResponseData appendData:data];
// NSLog(responseString);
// [response setText:responseString];
//[status setText:#"Response retrieved async"];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Received %lu Bytes", (unsigned long)[webResponseData length]);
NSString *theXML = [[NSString alloc] initWithBytes:
[webResponseData mutableBytes] length:[webResponseData length] encoding:NSUTF8StringEncoding];
NSLog(#"%#",theXML);
//now parsing the xml
NSData *myData = [theXML dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:myData];
//setting delegate of XML parser to self
xmlParser.delegate = self;
[xmlParser parse];
// Run the parser
#try{
BOOL parsingResult = [xmlParser parse];
NSLog(#"parsing result = %hhd",parsingResult);
}
#catch (NSException* exception)
{
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:#"Server Error" message:[exception reason] delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
return;
}
}
//Implement the NSXmlParserDelegate methods
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:
(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//currentElement = elementName;
currentElement = [elementName copy];
strCaptured = [[NSMutableString alloc] init];
if ([currentElement isEqualToString:#"Severity"]) {
item = [[NSMutableDictionary alloc] init];
NSLog(#"Log Output%#",[item objectForKey:#"Severity"]);
}
currentElement = elementName;
//NSLog(#"current element: ", elementName);
}
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"File found and parsing started");
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[strCaptured appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([currentElement isEqualToString:#"Severity "]) {
[output addObject:[item copy]];
} else {
[item setObject:strCaptured forKey:elementName];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
if (errorParsing == NO)
{
NSLog(#"XML processing done!");
} else {
NSLog(#"Error occurred during XML processing");
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
return [output count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if(cell == nil){
cell= [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
cell.textLabel.text= [output objectAtIndex:indexPath.row];
return cell;
}
Make an file in NSObject then use Methods in Parsing
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
}
- (void) parser: (NSXMLParser *) parser parseErrorOccurred: (NSError *) parseError {
}
- (void) parser: (NSXMLParser *) parser didStartElement: (NSString *) elementName
namespaceURI: (NSString *) namespaceURI qualifiedName: (NSString *) qName
attributes: (NSDictionary *) attributeDict{
}
- (void) parser: (NSXMLParser *) parser didEndElement: (NSString *) elementName namespaceURI: (NSString *) namespaceURI
qualifiedName: (NSString *) qName{
if ([elementName isEqualToString:#"webResponseData"])
{
[communication addObject:[currentElement stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
currentElement = nil;
}
- (void) parser: (NSXMLParser *) parser foundCharacters: (NSString *) string {
if(!currentElement)
currentElement = [[NSMutableString alloc] initWithString:string];
else
[currentElement appendString:string];
}
Then in connectiondidfinishloading in viewcontroller import parser file and make object of file
example :-
NSString *responseStringCommunication=[[NSString alloc]initWithData:MutableData encoding:NSUTF8StringEncoding];
Parser *parser=[[Parser alloc]init];
NSMutableArray *array=[parser parse:responseStringCommunication];

When iOS GCD threading is applied, parsing of xml stops working

I'm working on an app where user sends data to a central database using a web service. The web service sends back an XML file with the primary key from the central database so the local database on the app is updated with this primary key. When I don't use GCD, everything works perfectly. As soon as I introduce threading, I don't seem to get the XML file to parse. It seems like the code sends the XML to the web service, but nothing happens thereafter. Is there something wrong with the way i implement threading? Here's the code sample:
-(void) viewDidLoad
{
dispatch_queue_t saveCentralDB = dispatch_queue_create("Writing Database", NULL);
dispatch_async(saveCentralDB, ^ {
NSLog(#"Created NEW THREAD to send info to CENTRAL DB");
NSString *soapMsg = [NSString stringWithFormat:
#"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap12:Envelope "
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
"xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">"
"<soap12:Body>"
"<InsertPurchase xmlns=\"http://tempuri.org/\">"
"<RequestObject xsi:type = \"SpazaPurchaseRequest\">"
"<PurchaseID>%#</PurchaseID>"
"<RemoteSpazaPurchaseID>%#</RemoteSpazaPurchaseID>"
"<UserID>%d</UserID>"
"<RetailerID>%#</RetailerID>"
"<ItemID>%#</ItemID>"
"<CostPrice>%#</CostPrice>"
"<Longitude>%#</Longitude>"
"<Latitude>%#</Latitude>"
"<DatePurchased>%#</DatePurchased>"
"<Barcode>%#</Barcode>"
"<BasketID>%#</BasketID>"
"</RequestObject>"
"</InsertPurchase>"
"</soap12:Body>"
"</soap12:Envelope>",#"0",pklPurchaseID1,fklUserID,fklRetailerID1,fklItemID1, lCostPrice1, sLongitude1, sLatitude1,dtPurchase1,sBarcode1,fklBasketID1];
//---print of the XML to examine---
NSLog(#"%#", soapMsg);
NSURL *url = [NSURL URLWithString:#"http://www.myapp.com/purchases/ProviderWS.asmx?op=InsertPurchase"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapMsg length]];
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [NSMutableData data];
}
});
}
I then implement the following methods to deal with the response from the Web Service.
/************************Processing the feedback XML returned by webservice*****************/
-(void) connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *) response{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *)connection
didReceiveData:(NSData *) data {
[webData appendData:data];
}
-(void) connection:(NSURLConnection *)connection
didFailWithError:(NSError *) error {
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
//---prints the XML received---
NSLog(#"%#", theXML);
xmlParser = [[NSXMLParser alloc] initWithData: webData];
[xmlParser setDelegate: self];
[xmlParser setShouldResolveExternalEntities: YES];
[xmlParser parse];
}
The normal delegate methods are then implemented:
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(#"XML Parsing Method: didStartElement");
//This is the first node that we search for. The information we want is contained within this node.
if ([elementname isEqualToString:#"ResponseMessage"])
{
currentCentralDBPurchase = [parsingCentralDBPurchaseXML alloc];
//Flag to indicate that we are within the ResponseMessage node/tag.
isStatus = YES;
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"XML Parsing Method: foundCharacters");
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if (isStatus)
{
if ([elementname isEqualToString:#"PurchaseID"])
{
currentCentralDBPurchase.centralPurchaseID = currentNodeContent;
}
if ([elementname isEqualToString:#"RemotePurchaseID"])
{
currentCentralDBPurchase.localPurchaseID = currentNodeContent;
}
}
if ([elementname isEqualToString:#"ResponseMessage"])
{
//Update local database with the PurchaseID from the central database. This is how we will identify records that must still be sent to the central database.
//Now update the local database with purchases that have been sent to central database
// Get the DBAccess object;
DBAccess *dbAccess = [[DBAccess alloc] init];
[dbAccess UpdateCentralPurchaseID: [currentCentralDBPurchase.localPurchaseID integerValue] :[currentCentralDBPurchase.centralPurchaseID integerValue] ];
// Close the database because we are finished with it
[dbAccess closeDatabase];
currentCentralDBPurchase = nil;
//Clear the currentNodeContent node so we are ready to process the next one.
currentNodeContent = nil;
}
}
NSURLConnection initWithRequest: should be called on the main thread in this situation.
dispatch_async(dispatch_get_main_queue(), ^{
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [NSMutableData data];
}
});
It relies on RunLoop and Runloop is automatically working on the main thread. You can call initWithRequest on the other thread but you should execute RunLoop on the thread. However it is kind of difficult on a dispatch queue, thus using the main queue is helpful for the situation.

Calling NSXMLParser from static method does not parse

I'm trying to build a nice little class with NSXMLParser for my needs.
I'm calling the class this way:
ViewController.m:
[IDAN_XML_Parser setXmlString:soapMsg];
[IDAN_XML_Parser setStringToFind:#"userID"];
[IDAN_XML_Parser setHeader:kAPP_PASSWORD andHeaderValue:kAPP_HEADER];
[IDAN_XML_Parser setSetupXml:kWEB_SERVICE_URL];
[IDAN_XML_Parser prepareParsingAndGetResponse];
NSString *answer = [IDAN_XML_Parser getResponse];
NSLog(#"Response: %#", answer);
And my class is this:
.h file:
#import <Foundation/Foundation.h>
#interface IDAN_XML_Parser : NSObject <NSXMLParserDelegate, NSURLConnectionDelegate>
+ (void)setXmlString:(NSString *)theXML;
+ (void)setStringToFind:(NSString *)stringToFind;
+ (void)setSetupXml:(NSString *)webServicesURL;
+ (void)setHeader:(NSString *)headerKey andHeaderValue:(NSString *)headerValue;
+ (BOOL)prepareParsingAndGetResponse;
- (NSInteger)startParsing;
+ (BOOL)isParsingOK;
+ (IDAN_XML_Parser *)sharedXML;
+ (NSString *)getXMLString;
+ (NSString *)getResponse;
#end
.m file
#import "IDAN_XML_Parser.h"
#implementation IDAN_XML_Parser
NSString *matchingElement;
NSString *xmlString;
NSMutableData *webData;
NSURLConnection *conn;
NSString *soapHeader_key;
NSString *soapHeader_value;
NSURL *url;
BOOL isHeader = NO;
NSInteger didGetError = 0;
BOOL elementFound;
NSMutableString *soapResults;
NSString *returnedString;
NSXMLParser *xmlParser;
NSString *exceptionReason;
NSString *resultXML;
#pragma mark - Setup Parsing
+ (IDAN_XML_Parser *)sharedXML
{
static IDAN_XML_Parser *theParser;
#synchronized(self) {
if (!theParser)
theParser = [[self alloc] init];
}
return theParser;
}
+ (void)setXmlString:(NSString *)theXML
{
xmlString = theXML;
NSLog(#"\n\nXML TO SEND: %#", xmlString);
}
+ (void)setStringToFind:(NSString *)stringToFind
{
matchingElement = stringToFind;
}
+ (void)setHeader:(NSString *)headerKey andHeaderValue:(NSString *)headerValue
{
isHeader = YES;
soapHeader_key = headerKey;
soapHeader_value = headerValue;
}
+ (void)setSetupXml:(NSString *)webServicesURL
{
url = [NSURL URLWithString:webServicesURL];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [xmlString length]];
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [xmlString dataUsingEncoding:NSUTF8StringEncoding]];
if (isHeader) {
[req setValue:soapHeader_value forHTTPHeaderField:soapHeader_key];
}
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webData = [NSMutableData data];
}
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *) response
{
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *) data
{
[webData appendData:data];
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *) error
{
didGetError = 1;
NSLog(#"\n\nConnection Error: %#", error);
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
resultXML = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
didGetError = 0;
NSLog(#"\n\nRESPONSE XML: %#", resultXML);
}
+ (BOOL)prepareParsingAndGetResponse
{
NSInteger isParsingOK;
isParsingOK = [[self sharedXML] startParsing];
if (isParsingOK == 1) {
return YES;
} else {
return NO;
}
}
- (NSInteger)startParsing
{
#try {
xmlParser = [[NSXMLParser alloc] initWithData:webData];
[xmlParser setDelegate:self];
[xmlParser setShouldResolveExternalEntities:YES];
[xmlParser parse];
}
#catch (NSException *exception) {
didGetError = 1;
exceptionReason = exception.reason;
NSLog(#"Exception Reason: %#", exception.reason);
}
if (didGetError != 1) {
didGetError = 0;
}
return didGetError;
}
+ (NSString *)getXMLString
{
return resultXML;
}
+ (BOOL)isParsingOK
{
if (didGetError == 1) { // We have error
return 1;
} else { // We don't have error
return 0;
}
}
+ (NSString *)getResponse
{
return returnedString;
}
#pragma mark - Start Parsing
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(#"START PARSING!");
if ([elementName isEqualToString:matchingElement]) {
if (!soapResults) {
soapResults = [[NSMutableString alloc] init];
}
elementFound = YES;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (elementFound) {
[soapResults appendString:string];
NSLog(#"\n\nsoapResults: %#", soapResults);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:matchingElement]) {
returnedString = soapResults; // Save the element we found
elementFound = NO;
}
}
#end
I manage to send and get back the correct xml but it's actually doesn't start the parsing.
Any idea how can I solve it?
You are initiating an asynchronous NSURLConnection and then immediately starting the parsing process. You need to defer the initiation of the parsing until connectionDidFinishLoading, or you should do a synchronous NSURLConnection (but obviously, not from the main queue). Thus, while you're eventually getting a response, the parsing is undoubtedly initiated before the response has been received.

call SOAP request in iOS [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I want to call SOAP request in iphone. But I do not know how to do it. Please give me some code of calling SOAP request in iphone as below code?
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<wsse:Security
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" soap:mustUnderstand="1">
<wsse:UsernameToken
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Username>cbrown</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">welcome</wsse:Password></wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body xmlns:ns1="http://xmlns.oracle.com/bpel/aubi/mobile/Worklist">
<ns1:WorklistRetrievalREQ>
<ns1:WorklistType>HR_OFFER</ns1:WorklistType>
<ns1:Status>TODO</ns1:Status>
<ns1:Mode/>
</ns1:WorklistRetrievalREQ>
</soap:Body>
</soap:Envelope>
This is how you can call SOAP web services :
In your .h file declare :
NSMutableData *webPortFolio;
NSMutableString *soapResultsPortFolio;
NSURLConnection *conn;
//---xml parsing---
NSXMLParser *xmlParserPortFolio;
BOOL elementFoundPortFolio;
NSMutableURLRequest *req;
NSString *theXMLPortFolio;
NSString *strSoapMsg;
UIAlertView *alertView;
In your .m file use the following code:
-(void)loadPortfolioData
{
strSoapMsg = [[NSString alloc] initWithFormat:
#"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">"
"<soap12:Body>"
"<GetPortfolioList xmlns=\"http://tempuri.org/\">"
"<EmailID>%#</EmailID>"
"<Password>%#</Password>"
"<TradingGameID>%d</TradingGameID>"
"</GetPortfolioList>"
"</soap12:Body>"
"</soap12:Envelope>",gameUserName,gamePassword,gameid];
//---print it to the Debugger Console for verification---
NSLog(#"soapMsg..........%#",strSoapMsg);
NSURL *url = [NSURL URLWithString:#"http://www.abc.sirus/Process/process.asmx"];
req = [NSMutableURLRequest requestWithURL:url];
//---set the headers---
NSString *msgLength = [NSString stringWithFormat:#"%d",[strSoapMsg length]];
[req addValue:#"application/soap+xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:#"http://tempuri.org/GetPortfolioList" forHTTPHeaderField:#"SOAPAction"];
[req addValue:msgLength forHTTPHeaderField:#"Content-Length"];
//---set the HTTP method and body---
[req setHTTPMethod:#"POST"];
[req setHTTPBody: [strSoapMsg dataUsingEncoding:NSUTF8StringEncoding]];
// [activityIndicator startAnimating];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn)
{
webPortFolio = [[NSMutableData data] retain];
}
}
And to handle the response you can use following functions :
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webPortFolio setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webPortFolio appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
NSLog(#"error...................%#",[error description]);
[webPortFolio release];
[connection release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
//Check the request and returns the response.
NSLog(#"DONE. Received Bytes: %d", [webPortFolio length]);
theXMLPortFolio = [[NSString alloc]
initWithBytes: [webPortFolio mutableBytes]
length:[webPortFolio length]
encoding:NSUTF8StringEncoding];
//---shows the XML---
NSLog(#"shows the XML %#",theXMLPortFolio);
[theXMLPortFolio release];
if(xmlParserPortFolio)
{
[xmlParserPortFolio release];
}
xmlParserPortFolio = [[NSXMLParser alloc] initWithData: webPortFolio];
[xmlParserPortFolio setDelegate: self];
[xmlParserPortFolio setShouldResolveExternalEntities:YES];
[xmlParserPortFolio parse];
[webPortFolio release];
[connection release];
}
//---when the start of an element is found---
-(void) parser:(NSXMLParser *) parser
didStartElement:(NSString *) elementName
namespaceURI:(NSString *) namespaceURI
qualifiedName:(NSString *) qName
attributes:(NSDictionary *) attributeDict
{
if( [elementName isEqualToString:#"GetPortfolioListResult"])
{
if (!soapResultsPortFolio)
{
soapResultsPortFolio = [[NSMutableString alloc] init];
}
elementFoundPortFolio = TRUE;
NSLog(#"Registration...%#",soapResultsPortFolio);
}
else if([elementName isEqualToString:#"your_tag_name"])
{
elementFoundPortFolio = TRUE;
}
else if([elementName isEqualToString:#"your_tag_name"])
{
elementFoundPortFolio = TRUE;
}
else if([elementName isEqualToString:#"your_tag_name"])
{
elementFoundPortFolio = TRUE;
}
}
-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string
{
if (elementFoundPortFolio)
{
[soapResultsPortFolio appendString: string];
}
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(#"Parser error %# ",[parseError description]);
}
//---when the end of element is found---
-(void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"GetPortfolioListResult"])
{
NSLog(#"display the soap results%#",soapResultsPortFolio);
}
else if([elementName isEqualToString:#"your_tag_name"])
{
//Perform required action
}
else if([elementName isEqualToString:#"your_tag_name"])
{
//Perform required action
}
else if([elementName isEqualToString:#"your_tag_name"])
{
//Perform required action
}
[soapResultsPortFolio setString:#""];
elementFoundPortFolio = FALSE;
}
If the number of soap calls in your project increases, I can recommend using the tool wsdl2objc. It will generate an Objective C service plus model definition. The tools is not updated recently and it does not use Arc, but it's still ok. http://code.google.com/p/wsdl2objc/wiki/UsageInstructions

Resources