Using .NET Web Services And Datasets with iPhone Revision 2!
***Updated to work with iPhone iOS4.0.1 There seems to be a problem with the old code on version 4.0+ where the last record crashes the app in the XMLDataSetParser.m class Thanks to Douwe Querty for submitting a fix!
Well I must apologize for the amount of time it has taken me to post this, I have been finished this version for quite a while however our company has been extremely busy and I have not had time to post this! But fret no more for here it is! the second version of the iPhone Dataset Parser! I will now call this project the NETDataset Project.
Let me re-iterate before we begin that I am a self taught developer coming from .NET to iphone development, and while I am pretty well versed in objective C now, I am by no means an expert. If you see something that needs to be fixed such as memory leaks proper usage of functions etc, please contact me and tell me how to fix those problems instead of just spamming in the comments about how its full of memory leaks or how I am not coding this properly.
(by the way I discovered analyzer and its tantalizingness since the last version so this version is now memory leak free!)
Whats New in this version?
- A Sample project has been provided to make things easier to understand and implement
- Using Asynchronous calls now shown in sample project
- Connecting Data to your UITableView in sample project
- Better table structure, can now reference data by using column names as the key.
- Sorting Data Alphabetically
- Performance increases
- Memory Leak fixes
If you are updating from the old code you will find that almost everything relating to the querying/dataset functions no longer work the same so be aware if this is an upgrade you will need to make some changes to the way you handle the data. (Which is far far easier now by the way)
The biggest difference is the way the data is now returned from the parser. We now have a structure that looks like this
Dataset
NSDictionary(Tables) key=tablename value=arrayofTables
----------NSDictionary (Table) key=rowid value=arrayofrows
------------------------------------NSDictionary(Rows)key=columnName value=columnvalue
this allows us to ask for a column value using the column name!!! so much nicer!
for example to get a column value all we do is call
[row objectForKey:@"Column1"]
Lets start again with the essential files
Download the new version and sample project from github here
add these files to your project including the example cacheDBCommands files
Simply drag the NETDataset folder from the sample project to your own project the folder should contain the same 6 files
To follow along this example you should ALSO copy the CacheDBCommands.h and .m files you should now have all these files in your project
DataSet.h
DataSet.m
XMLDataSetParser.h
XMLDataSetParser.m
WebServiceHelper.h
WebServiceHelper.m
CacheDBCommands.h
CacheDBCommands.m
1. First go to your view controller and include the CacheDBCommands.h file to your viewController.h file
#import "CacheDBCommands.h"
2. Add the CacheDBDelegate to your view controllers header file
@interface DatasetParserViewController : UITableViewController <CacheDBDelegate> {
3. Create a property/Variable in your view controller for the cachedb class
CacheDBCommands *cacheDB;
@property(nonatomic, retain)CacheDBCommands *cacheDB;
4.Initialize cachedb and set delegate in your view controllers viewDidLoad event
cacheDB = [[CacheDBCommands alloc] init];
cacheDB.delegate = self;
5.Call your download function from the view controller
[cacheDB initDownloadMyDataset:@"param1Value" Param2:@"pram2Value"];
6. Modify the CacheDBCommands class to suit your needs
The initDownloadMyDataset function is simply a handler method that creates a new Asynchronous operation for your actual method call. This way your app doesnt hang waiting for a response. It creates an NSInvocationOperation which contains your actual working method and adds it to the NSOperationQueue. We pass any parameters along with the operation, since the NSOperation only accepts a single object for parameters, we need to create an array of parameters and pass the single array object along if we have more than one parameter for our worker method.
In the example the working operation method is called getMyDataset and it accepts the single array parameter that we passed to the NSInvocationOperation.
This method is where the actual connection to the server is made and the data passed to the web service.
Lets break it down! o/< o|< o\<
ok all dancing aside…
first we get our parameters out of the array
NSString *param1 = [params objectAtIndex:0];
NSString *param2 = [params objectAtIndex:1];
Next we create a WebServiceHelper to make our connection to the server
// Create an object to the class above which is the connection to the WCF Service
WebServiceHelper *DataCon = [[WebServiceHelper alloc] init];
Now we set the connection info which includes the XMLNamespace (which must match EXACTLY to what you have set in your web service)
//set up service method and urls
DataCon.XMLNameSpace = self.XMLNamespace;
DataCon.XMLURLAddress = self.ServerURL;
Next we need to specify which method in the web service we want to invoke. Now the most important thing to remember is the method name and its parameters must
also match EXACTLY to what you have in your web service.
//set up method and parameters
DataCon.MethodName = @"getMyDataset";
DataCon.MethodParameters = [[NSMutableDictionary alloc] init];
[DataCon.MethodParameters setObject:param1 forKey:@"param1"];
[DataCon.MethodParameters setObject:param2 forKey:@"param2"];
This next part simply initiates the call and downloads the response as raw data into the NSMutableData object
NSMutableData *retData;
retData = [DataCon initiateConnection];
Now that we have our data we want to make it into something useful, like a dataset! This is where the Dataset and XMLDatasetParser classes do their magic!
If you wanted to instead retrieve something else like a string or a boolean or an integer from your service method, you would need to write your own NSXMLParser class to handle those. I have not had a need for this yet as I am fine with just passing back datasets. If you do end up writing one email it to me and I will add it to the NETDataset project ![]()
If you uncomment the doDebug line you will get the XML returned from the soap service NSLogged to the console. This becomes very useful when debugging.
once parseXMLWithData is called the dataset object will now contain the tables and rows from your dataset.
//self.myDataset.doDebug = YES; //uncomment to print all data to NSLog
[self.myDataset parseXMLWithData:retData];
[DataCon release];
The last step in the cacheDbCommands class is to call our delegate function which lets the View Controller know we are done downloading the dataset and we have our data!
//call delegate to let view know we have finished downloading this
[delegate performSelectorOnMainThread:@selector(didFinishDownloading:)
withObject:self
waitUntilDone:NO];
7.Handle the delegate callback events
#pragma mark -
#pragma mark CacheDBCommands Delegate
- (void)willStartDownloading:(CacheDBCommands *)cacheDB
{
//NSLog(@"Delegate called start downloading", nil);
[self.act startAnimating];
self.act.hidden = NO;
}
-(void)didFinishDownloading:(CacheDBCommands *)acacheDB
{
//NSLog(@"Delegate called finish downloading", nil);
NSMutableDictionary *rows = [[NSMutableDictionary alloc] initWithDictionary:[cacheDB.myDataset getRowsForTable:@"Table1"]];
//each object in the rows dictionary contains a Key which is the rowid number and the value is a corresponding array of columns and values
NSArray *rowValues = [[NSMutableArray alloc] initWithArray:[rows allValues]];
//each object in the rowValues array is a dictionary object containing all of the columns and values
NSMutableArray *data = [[NSMutableArray alloc] initWithArray:rowValues];
[rows release];
[self.tableView reloadData];
[self.act stopAnimating];
}
8. Do what you want! Your Done!
You can also query the dataset for a value using the function getRowsForTableWhereColumnEquals I often use this to join two tables together or use the first table as a section header in the tableview and the second table as the data
This accepts 3 parameters TableName ColumnName and Searchstring
NSMutableArray *filteredData= [[NSMutableArray alloc] initWithArray:[cacheDB.myDataset getRowsForTableWhereColumnEquals:@"Table1" Column:@"Column1" Where:@"happyfish"]]];
Sorting Your Data
The NSXMLParser runs Asynchronously as it parses through the document, this is why even though a dataset is ordered sometimes your data is still not in the right order when you add the cells to your UITableview. Here is a utility function that I use to resort the order of an array of rows by column name.
*** updated now supports numerical ordering and duplicate entries since caseInsensitiveSearch does not order numbers properly in a string.
Now lets say I have a dataset containing multiple vehicles but I only want the cars and I want them sorted alphabetically.
NSMutableArray *myData= [[NSMutableArray alloc] initWithArray:[acacheDB.myDataset getRowsForTableWhereColumnEquals:@"Table1" Column:@"VehicleType" Where:@"Car"]];
[self sortArrayContainingDictionaryItemsByKey:@"VehicleType" ArrayToSort:myData isNumeric:NO];
Thats it! mydata should now be an array of rows sorted by the column!
*****EDIT Please note If you are using [rows allValues] and you want to sort on that Array you will not be able to since the array is Immutable, you will get a selector does not recognize instance error. You must convert to a mutable array with myMutableArray = [NSMutableArray arrayWithArray:[rows allValues]];
then to get the first row you just call
NSDictionary *firstRow = [myData objectAtIndex:0];
to get say the vehicle model I would add on
NSString *model = [firstRow objectForKey:@"VehicleModel"];
I hope this framework helps you along your way to creating some amazing apps! Please send me the links to your apps on the app store! I will post them here! As always if you have questions please ask in the comments I do my best to answer everyone!
am I the first to try this? I just downloaded and am off to try it out, I cant wait, thank you Kris!
Hi, i just implement the code it works great, but i have the dataset ordered and when i get the data on the iphone isnt orderder, what can i do??
thanks
Yes I believe the parser runs asynchronously and so sometimes the order is not preserved.
Since I think this will be helpful to everyone I have updated the above post with instructions on how to sort the data alphabetically. hope that helps!
With a little help from the author as I am only a 2month old Iphone programmer and only have 1 year of .net under my belt I can say this code set works great!! I am up and running with an example app customized for one of our inhouse web services…I just wanted to say how awesome Kris is and all the work they must of put into this. Clear Channel is very happy
Sorry I am slightly confused as I am new to iPhone programming but where do I set what method to call on the webservice as this does not apear to be shown?
Sorry for my confusion on this
The method is set in the CacheDBCommands class, I have updated the post above to explain the cacheDBCommands class in detail.
That is fantastic, thank you for doing this! The extra detail is excellent. Thanks again.
do you have an address for a web service to test the sample project?
No you will have to create your own web service. This takes about 5 minutes to do in .NET If you are trying to make this work with a different provider then you will probably need a different framework as this one is mainly used for working with datasets returned from a web service.
Ok thanks,
I’ve set up a simple web service that returns all of the cusomers from the sql server sample northwind db. Using the sample app, I can see the data returned as XML in debug mode. However the table count is 0 and the table view is not populated with data. How do i configure the table view for my dataset?
without seeing your code I cannot tell what may be going on. you can email me your code at o0JoeCool0o@gmail.com
Thanks for the example. I too am coming from the .Net world and it can be so frustrating knowing that it can be done, but just missing that one piece on how to actually do it with Objective-C and the iPhone.
Thanks alot for writing this tutorial !
I found how to show the indicatorview … You need to add the following code in the viewDidLoad of the DatasetParserViewController.
// Before retrieving data from the webservice init the activity indicator
act = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
act.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
act.center = self.view.center;
[self.view addSubview: act];
Hi, First Thanks! for doing this
& even though it look simple I am having troubles
Step #2: Error: “no type or storage class may be specified here before ‘interface’”
Do I have to delete my original ViewController?? [[for eg: “@interface testNetViewController : UIViewController {”
Step #3 & #4: Where will this go? in testNetViewController.h or testNetViewController.m ??
I’m sorry but I’m confused !?!
Please re read Step 2 I have added the actual code for adding a delegate, adding delegates is very easy you should read up on delegates and how they work and understand them. They will help you immensely. You do not need to delete your original viewController
Cool it works, Thanks for your help Kris. You’ve given me a great head start with objective c.
Okay so I got the web service working and using the NSLog I can see that I am returning data.
—————-
Table = {
0 = {
ID = US;
country = US;
level = 2;
score = 567894;
scoreDate = “2010-07-10T06:57:24-07:00″;
userName = JSHOE;
};
1 = {
ID = 8;
country = US;
level = 4;
score = 213123;
scoreDate = “2010-04-30T15:32:52-07:00″;
userName = Deb;
};
2 = {
ID = 6;
country = US;
level = 1;
score = 1000;
scoreDate = “2010-04-30T10:44:19-07:00″;
userName = Geo;
};
3 = {
ID = 5;
country = US;
level = 1;
score = 1000;
scoreDate = “”;
userName = Geo;
};
4 = {
ID = 1;
country = US;
level = 1;
score = 500;
scoreDate = “2010-04-10T19:00:00-07:00″;
userName = Me;
};
5 = {
ID = 9;
country = US;
level = 1;
score = 5;
scoreDate = “2010-04-30T15:42:49-07:00″;
userName = “George II”;
};
};
—————-
But in the didFinishDownloading:
NSMutableDictionary *rows = [[NSMutableDictionary alloc] initWithDictionary:[acacheDB.myDataset getRowsForTable:@"Table1"]];
NSLog(@”Rows Count: %d”, rows.count);
I am always getting a rows.count of Zero. What am I doing wrong?
Thanks in advance for any and all help. I truly appreciate your help.
Make sure your table is named “Table1″
I am getting this also.
Table = {
0 = {
…
The code works perfectly if the request is synchronous. Is it possible to call the web service asynchronously?
Thanks.
Thanks to Kris I did get this working. My only question now is: I attempted to use the Sort function as I am returning info for a gma and getting High Scores but also the xml shows the scores returned as int, they appear to be strings after converted to the nsMutableArray. If I then attmept ot sort on highScore, it returns as:
1000
1
100
20000
etc.. If I set the isNumeric to YES, it errors. Has anyone else gotten the Numeric sort to work?
Geo…
The example code is asynchrounous. It uses OperationQueues in CacheDBCommands class
Hi,
I am having a problem sending parameters. The web service works when I invoke it with parameters when I can the service from a browser. Using your service, I check the parameters in the service and they are blank. I checked the case sensitivity etc. Can you help me. Alan.
The XmlDataSetParser is all messed up
are you sure you have the correct files posted for download?
looks like currentData is not initialized…
kept getting *** -[CFString retain]: message sent to deallocated instance
this has been fixed you will have to download again.
Thanks
Hi,
Overall this is an excellent utility.
I am trying it now, and had a couple of issues..
First is, as most of the users have encountered, a warning that XmlDataSetParser does not implement NSXmlParserDelegate. All I needed to do is to add to the XmlDataSetParser declaration.
Second issue, I was getting an extra carriage return prefixed to various values.
My sample test data looks like below: (posted only a small data here…so you get an idea)
and the strings in the row dictionary would be “\nDivision 1″ (without quotes).
1
Division 1
true
2
Division 2
true
3
Division 3
true
…
I am not sure if there is a better way to avoid it. But, here’s what I did, introduced a BOOL member trimWhiteSpace in the XmlDataSetParser, and then modified the following method, by adding following line…
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(trimWhiteSpace)
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
…
This gets me correct data for now… I will keep checking to make sure it does not cause any other side effects. Please comment if any of you have a better suggestion.
Third issue, I am having is that my DataSet contains msData:rowOrder attribute. (See example above). Any idea how I can get the sorting by the rowOrder attribute of the element?
Let me try to post the XML snippet again..
<![CDATA[
1
Division 1
true
2
Division 2
true
3
Division 3
true
...
]]>
Argh…. one more time…
<NewDataSet xmlns=”">
<Table diffgr:id=”Table1″ msdata:rowOrder=”0″>
<ID>1</ID>
<Description>Division 1</Description>
<IsActive>true</IsActive>
</Table>
<Table diffgr:id=”Table2″ msdata:rowOrder=”1″>
<ID>2</ID>
<Description>Division 2</Description>
<IsActive>true</IsActive>
</Table>
<Table diffgr:id=”Table3″ msdata:rowOrder=”2″>
<ID>3</ID>
<Description>Division 3</Description>
<IsActive>true</IsActive>
</Table>
Hi,
i’m using xcode 3.2.3 and iOS SDK 4.0.1
last record crashes the app in the XMLDataSetParser.m class
problem resolved?
**i’m sorry I can’t speak English well
this should be resolved please download again.
soap:ClientServer did not recognize the value of HTTP Header SOAPAction: http://192.168.0.105/lws/LocusCognosWebService.asmx/getReportlist.pple.CommCenter2010-08-13 12:35:59.505 DatasetParser[782:1903] TableCount:0
when i call my webservice i got the data in NSMutableData like this what it mean please help me
thanks………
I am new to iOS dev. Can you please tell me what are the values and parameters must be passed in these methods (Below) any example will do it.
[cacheDB initDownloadMyDataset:@"param1Value" Param2:@"param2Value"];
[DataCon.MethodParameters setObject:param1 forKey:@"param1"];
[DataCon.MethodParameters setObject:param2 forKey:@"param2"];
Everytime I try to connect to the webservices this is what I get in return. 2010-08-17 18:19:55.925 DatasetParser[22035:5c03] TableCount:0
2010-08-17 18:19:55.926 DatasetParser[22035:5c03] response:
2010-08-17 18:19:55.928 DatasetParser[22035:5c03] Document Started Parsing…
2010-08-17 18:19:55.930 DatasetParser[22035:5c03] Document Ended Parsing…
Great set of code. I’m so close to having it work however, I used my own ViewController files (not the ones that you provided in the example) and my willStartDownloading and didFinishDownloading code never gets called. I’ve gone through the example and feel like the delegates are setup correctly.
Any ideas why these may not be getting called. I’m getting the NSLog information showing the data is being returned correctly from the web service.
Thanks
Someone can tell me what wrong I am doing, I am getting the following error :
////
WebService[5837:1903] Method: GetResultByName Parameter:Name Value:America
2010-08-31 14:34:21.723 WebService[5837:207] -[WebServiceViewController willStartDownloading:]: unrecognized selector sent to instance 0x5919b30
2010-08-31 14:34:21.762 WebService[5837:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[WebServiceViewController willStartDownloading:]: unrecognized selector sent to instance 0x5919b30′
/////
Web Service Method: GetResuleByName, Paramter to be passed is “string” as Name
you must create the willStartDownloading:(CacheDBCommands *) function in WebServiceViewController
Hi, I implement the code it works great, but when I call the getMyDataset method (CacheDBCommands class) directly without calling the initDownloadMyDataset method which creates async request of getMyDataset method from my viewController, the corresponding method in my webservice (in .net) calls 3 times. Please help me in resolving this issue, since I am new to iPhone development..
Hello! I’m trying to get this to work! The problem is, I’m receiving tables as : “…”. When the xmlParser recognizes the element it gets “Table” as currentTable and not “Table1″, so it never gets the real name of the table. How can I short this??
Thank you! Grat job this project!
it didnt get the xml, it was:
Table diffgr:id:”Table1″ msdata:rowOrder=”0″
I am facing same problem Can you tell me how you solved this
I have never heard of this problem, what version of .NET are you using? If you want you can email me a link to your soap service with debug/testforms turned on and I will try to see if it is a problem with your service. The parser has been tested using Visual studio 2008 with SOAP services, not the same as WCF service. WCF can be configured to issue as SOAP protocol.
First off – thanks for the wonderful bit of code. This has made my transition to iPhone dev from the .Net world a good deal easier for me. I’ve got everything working in my project except for the ability to sort numerically. If I set the parameter isNumeric to YES, I get the following error:
2010-09-29 08:35:08.340 Leaderboard[14812:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[NSCFString numericCompare:]: unrecognized selector sent to instance 0x6a14f30′
The column that I’m sorting on is listed as an int field in the XML from the web service. If I simply set the isNumeric field to NO, the code completes file (other than the rows not being in the correct order) I’m not sure if this is the same issue as Geo reported on July 16th on numeric sorts – but has anyone been able to get the numeric sorting to work?
Use “isNumeric:NO” all data returned from XML is in string format not NSInteger. I have added the functionality for sorting other arrays that are of type integer. Or you can convert from string to integer and then use isNumeric:YES.
Hi,
Thank you for all the detailed information & sample code. I did my first test project & its working.
But for me also sorting is a problem
– the sorting example given here not working as intended. Any other thoughts? thank you
I’d used below code in didFinishDownloading method.
//SORTING
NSMutableArray *srows= [[NSMutableArray alloc] initWithArray:[rows allValues]];
[self sortArrayContainingDictionaryItemsByKey:@"Priority" ArrayToSort:srows Ascending:YES isNumeric:NO];
self.data=srows;
its ok for strings:), but issues with numbers
you may need to iterate through the rows and change the values to NSInteger instead of string and then use isNumeric:YES
Im guessing yours is sorting like 1 10 11 12 etc? or does it not apply any type of sorting?
Thanks for creating this! Couple questions. Does this work with an SSL enabled and secure .NET web service? If so, what would have to be done on the iPhone side to handle those details?
Thanks,
Chris
Works fine with SSL enabled, and no changes needed to iPhone app, as long as your URL points to https protocol.
For secure .NET web service, there are bunch of options, that you must implement outside of this utility. For example, in my application, we cut corner (due to deadlines) and provided web-browser based interface for FORM based authentication policy of .NET web service. (Programming client for form based authentication in .NET web service is a pain)
Also I ended up using this utility purely for dataset parsing, and it worked very well, since our Web Service is a SOAP based, I ended up creating SOAP client for iPhone using http://sudzc.com!
Hope this is useful.
Way to thank me — purchase my app(itms://itunes.apple.com/us/app/multimeasure/id396067036?mt=8).
-DJay
I have been using this from the start and love it. I am needing to make a small change and I am not so sure how to go about it as I am still a new Iphone programmer.
The app has 3 views.. the root controller view that lists “Markets”…when you select a market, it loads a custom cell loaded from nib that displays some ip address, and other info…
that view has a right arrow, that when you click it, it pulls up a webcam view (based on one of the ips that was loaded from the webservice).
all of that works great. However…when you come back from the webcam view, im loading all of the data back in from scratch. I only want the webservice calls to load when you back out to the root view and select a new market.
So right now, that all happens in the DataseParserViewController.m in the ViewDidLoad
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
clear data
[self.data removeAllObjects];
[self.filteredData removeAllObjects];
[self.tableView reloadData];
cacheDB = [[CacheDBCommands alloc] init];
cacheDB.delegate = self;
[cacheDB initDownloadMyDataset:@"Board Number" Param2:@"MiniGoose"];
}
unfortunately ViewDidLoad runs both when the custom nib is loaded as well as the return from the webcam view.
I only want it when the custom nib loads… I tried this all in ViewDidLoad, but then it only loads the first market…since that fires when the view is created…
what am I missing?
ooops that should read “So right now, that all happens in the DatasetParserViewController.m ViewDidAppear…”
This was actually easier than I thought. I just created a bool to toggle when to run the cacheDB initdownload calls in the viewDidAppear. I set it to Yes when the view is first called from the rootViewController and then I set the bool to NO in the didFinishdownloading method.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
//clear data
if (runLookUp == YES) {
[self.data removeAllObjects];
[self.filteredData removeAllObjects];
[self.tableView reloadData];
cacheDB = [[CacheDBCommands alloc] init];
cacheDB.delegate = self;
[cacheDB initDownloadMyDataset:@"Board Number" Param2:@"MiniGoose"];
}
}
Great! I have used this for one of my projects and works perfectly!
However, I’ve been missing methods that return a single value (integer, boolean, etc.).
But now I have a problem, do not know how to call a method if the parameters are a structure. How I can send a structure to call a webservice method?
The structure is similar to this (in xml):
int
int
int
string
decimal
int
int
string
decimal
NOTE: function “methoName”
–> Parameters: p1 [type int], p2 [type structure]
–> p2 structure is: e1 [type int], e2 [type int], e3 [type string], e4 [type decimal] (and the number of elements for p2 is variable)
Thanks very much!!
This is the part of xml.
int
int
int
string
decimal
int
int
string
decimal
One more time! this is the xml…
<setOrder >
<p1>int</p1>
<p2>
<element>
<e1>int</e1>
<e2>int</e2>
<e3>string</e3>
<e4>decimal</e4>
</element>
<element>
<e1>int</e1>
<e2>int</e2>
<e3>string</e3>
<e4>decimal</e4>
</element>
</p2>
</setOrder>
I’m a C# developer and am looking to start writing iPhone apps. I feel like I’m going back in time, learning objective-c from C#.
I’m looking for just an example as this one to implement into a project that I’ve yet to start. From the sounds of the replies, this seems to be an outstanding project done by the author, but I don’t know where to start. I’m hung up on where to put the code in step 5 and probably just have problems understanding the rest of the article from there on out.
How did you all learn Obj-C? I’ve checked out some books from the library and have been watching the videos from my Lynda.com account which have all been very helpful, but I’m not quite sure where to start out from, getting a good project up and running and learning the fundamentals. I’ve always started learning a language (vb.net, c#, t-sql, etc) by delving into a project that just had to get done; rough around the edges at first, but then through actual code development and project requirements, my skills were honed.
Please email me with any suggestions/links/advise/schools/resources/etc that you have used to become a successful iPhone developer! Thank you. brian.sheahan [at] gmail dot com
I started out with Beginning iPhone Development by Mark leMarche Publisher is APRESS. there is a lot of useful info and a lot of common scenario type stuff that you will find invaluable. I did run into some issues since things change quite rapidly between the time things are actually published and the iPhone SDK framework is updated. But most of these are easy to figure out as you go. after that book I was able to pick up everything else by reading the apple reference documents. They have a lot of very well documented stuff. Its only after you try doing the more advanced stuff that the apple reference docs start lacking. For example, I had a ton of problems working with Audio Buffers to create audio recordings that you could pause and insert audio into.
At any rate that book helped me immensely.
Hi,
Firstly thanks for providing this great bit of code, I’ve got the code to call my webservice, but I can’t seem to get the parser to find any tables in my dataset…..
I don’t know what I’m doing wrong my Web service is as follows
private DataSet Test()
{
SqlConnection cons = new SqlConnection(con);
SqlDataAdapter DA = new SqlDataAdapter(“SELECT HouseIdent,HouseName FROM House”, cons);
DataSet DS = new DataSet();
DA.Fill(DS,”Houses”);
return DS;
}
Is there someone I’m missing?
I used the Programming in Objective-c 2.0 by Stephen G Kochran for the core syntax help… and then I just bought a bunch of books on how to program for Iphone.. “Iphone SDK Develiopment by Dudney and Adamson is pretty good but there are a few typos in some of the code examples but you can download the fixed versions.
I have only been doing C3 for 2 years so im still new to that as well. At first it was like you said, going back into time (I used to do C and C++ but only for a short time). I did a Lot of Delphi (Objective Pascal) before C#. I found that C# was so easy and life was easy. Objective C was terse and hard to remember what to do and the problem with having to manually link things up to the UI through Interface builder and manually controlling memory is a hassle. With all of that said, it seemed that after a while, I was just able to read the code, and it was just like anything else…takes practice. I dont code that often in either language so I attrify. Like the post above, you can google a lot of tutorials, hit the forums, and the Apple docs.
I hope this helps. ALso, if you want the code to my web service and iphone app that uses this library, I would be happy to share it. The author helped me a lot on it…but it is in production internally at our company and works great.
Was there ever a response to this? I can get the code to work fine but any parameters passed seem to be dropped by the .NET side??
Michael
Not without seeing your code please send to o0JoeCool0o@gmail.com I will try and help you out. -Kris
Hi there!
I’m having some troubles with the response from the service. Namely the response I should get in the appropriate response node from the soap service is something like this
“base64Binary” and when I parse the value of the node in XMLDataSetParser class, it has bunch of letters and numbers but it is not structured so I could extract the tables! Has any one had troubles with the decoding of the result???
This works great. I have a question however, how do you get a list of all columns for one particular table from the dataset?
Do you want to just get the column names? you can do this by getting a row
NSMutableArray *rows = [mydataSet getRowsForTable:@"MyTable"] NSMutableDictionary *myRow = [rows objectAtIndex:0];
NSArray *columnNames = [[NSArray alloc] initWithArray:[myRow allKeys]];
i did download the sample project, and edited xmlnamespace, webserviceUrl etc…
but i doesn’t work.. debugging error looks like…
2010-12-17 16:02:12.798 DatasetParser[23133:207] ******* Accessibility Status Changed: On
2010-12-17 16:02:12.878 DatasetParser[23133:207] ********** Loading AX for: com.yourcompany.DatasetParser ************
2010-12-17 16:02:13.066 DatasetParser[23133:6103] Method: SelectDB Parameter:NAME Value:23
2010-12-17 16:02:13.067 DatasetParser[23133:6103] Method: SelectDB Parameter:AGE Value:23
2010-12-17 16:02:13.110 DatasetParser[23133:6103] Connection Error: {
AGE = 23;
NAME = 23;
}
2010-12-17 16:02:13.110 DatasetParser[23133:6103] TableCount:0
2010-12-17 16:02:13.111 DatasetParser[23133:6103] Document Started Parsing…
2010-12-17 16:02:13.112 DatasetParser[23133:6103] Document Ended Parsing…
Connection error occurs.
ps. i edited this code…
self.XMLNamespace = @”http://tempuri.org/”; //this must match what is in your .net web service code
self.ServerURL = @”http://myserverhost/iPadWebService2/OracleWebService.asmx”;
…
//set up method and parameters
DataCon.MethodName = @”SelectDB”;
DataCon.MethodParameters = [[NSMutableDictionary alloc] init];
[DataCon.MethodParameters setObject:param1 forKey:@"NAME"];
[DataCon.MethodParameters setObject:param2 forKey:@"AGE"];
Please email your server code to o0JoeCool0o@gmail.com
Any update to sky_oo’s problem? I get the same error, however my data is returned. The other problem that I’m having is the “TableCount: 0″ issue which results in the dataset not being displayed. Any help is greatly appreciated.
So i fixed the no data displayed issue by changing [row objectForKey:@"Column1"]
to match one of the column headers from my actual webservice. Still getting the connection error though. Hopefully I’m making sense here.
You should ignore the Tablecount:0 it was left over debugging code I was using to check if the tables were being cleared after re downloading data. You can see where that code is if you do a text search in the project. Just comment it out. As for data not being displayed Its too hard to see what the problem is by you telling me it just isnt working. You can email your code to me at o0JoeCool0o@gmail.com and I can take a look to see whats wrong. I ahve done this for about 5 people and it usually turns out to be minor.
A new update is coming soon that addresses a few issues one of them being sorting numerically. and having the rows sorted in the roworder that they were sent in. Subscribe to my blog to be notified when I post the new update.
-Kris
Is there any update on #31?
I downloaded sample code but i am not able to display data. As per tutorial i made changes like this:-
self.XMLNamespace = @”http://me.greaterservice.com/”;
self.ServerURL = @”http://me.greaterservice.com/iphonewebservice.asmx”;
Webservice URL is http://me.greaterservice.com/iphonewebservice.asmx and it is displaying data.
While debugging i got this message
Current language: auto; currently objective-c
2011-02-28 14:20:40.565 DatasetParser[1789:5e03] Method: LoadList Parameter:param1 Value:param1Value
2011-02-28 14:20:40.572 DatasetParser[1789:5e03] Method: LoadList Parameter:param2 Value:pram2Value
(gdb) continue
2011-02-28 14:21:03.408 DatasetParser[1789:5e03] Connection Error: {
param1 = param1Value;
param2 = pram2Value;
}-(null)
(gdb) continue
2011-02-28 14:21:14.496 DatasetParser[1789:5e03] TableCount:0
2011-02-28 14:21:14.497 DatasetParser[1789:5e03] Document Started Parsing…
2011-02-28 14:21:14.499 DatasetParser[1789:5e03] Document Ended Parsing…
Please Help Me…!
Something about memory has changed since ios 4.0 you need to change 2 lines.
in WebServiceHelper.m find the following code in the function “initiateConnection” NSError *WSerror; NSURLResponse *WSresponse; // Call the xml service and return response into a MutableData object NSMutableData *myMutableData = (NSMutableData *)[NSURLConnection sendSynchronousRequest:request returningResponse:&WSresponse error:&WSerror]; if (WSerror) { NSLog(@”Connection Error: %@”, [WSerror description], nil); } return myMutableData; change the first 2 lines to NSError *WSerror = nil; NSURLResponse *WSresponse = nil;
Thanks for your valuable reply.By changing these lines error was fixed but still not able to display data. While debugging i am getting following message:-
Current language: auto; currently objective-c
2011-03-01 14:33:30.359 DatasetParser[864:5e03] Method: LoadList Parameter:Sam Value:param1Value
2011-03-01 14:33:30.363 DatasetParser[864:5e03] Method: LoadList Parameter:Test Value:pram2Value
(gdb) continue
(gdb) continue
2011-03-01 14:33:36.860 DatasetParser[864:5e03] TableCount:0
2011-03-01 14:33:36.861 DatasetParser[864:5e03] Document Started Parsing…
2011-03-01 14:33:36.862 DatasetParser[864:5e03] Document Ended Parsing…
This is what we need – an insight to make everynoe think
rqvfMu qrapfddhzoys
Thanks Kris, You are really Rock Star. Finally i was able to made it. I just made changes in Webservice and also in Xcode, i replaced Column1 to my Database column name in DataSetParsarviewController.
Kris, I am hoping in future you will come up with some more great tutorials that will make our life better.
Cheers,
Sam
Hi,
I am not very clear about Step 7. Where do i put that code. Please excuse me, as i’m new in x code.
Thanks
Changes required : If there are mutliple tables in one dataset the current procedure add the row coulmns of first datatable to next i.e.
this is how self.tables is:
PatientInformation = {
0 = {
PatientName = Checking1;
“Business_Phone” = 6255698445;
}}
InsuranceInformation = {
0 = {
PatientName = “”; //note that these values are not originally sent from xml
“Business_Phone” = “”;//note that these values are not originally sent from xml
insuranceName = ” Imran”;
}}
so we are having redundancy here. Had it been me, I would have added this line of code over here in xmlDataSetParser
- (void)parser:(NSXMLParser *)parser didStartElement: ….{
if (self.DataSetName != nil && beginTable == NO)
{
//dsgroup contains a dataset we can now add a new table if it does not currently exist
if([self.Tables objectForKey:elementName] == nil)
{
NSMutableDictionary *newTable = [[NSMutableDictionary alloc] init];
//—————my added line —————————-//
[self.dicCurrentRow removeAllObjects]; //removes the previous elements from dictionary
}
can anyone tell me the reason why i shouldnt be adding this line here ? if there isnt any, then wasnt it here before ? [self.dicCurrentRow removeAllObjects]; ?
[Grab A Life] Comment: “Using .NET Web Services And Datasets with iPhone Revision 2!”
Thanks! When I was creating the framework I had that line there and was running into memory issues with objects getting deallocated. If this works for you then I will take a second look and try adding that back in.
-Kris
*From:*
So far it works for me
, but you can regressively test it yourself aswell
//Imran Yousaf
Hi everyone,
I’m a newbie in iPhone Programming and i have question: (if it is too silly, sorry in advance
- Is it possible to use just parsing part of this library? How? I have already done with communication of web service. The request returns with “.NET untype DataSet” in XML format. And i keep it in NSXMLParser format…
So how can i use this library to just serialize the classes?
Thank you…
-Cn
Examle of Response method:
schemaxml
Have you tried consuming MTOM webservice & any bytestream to file conversion in objective -c
Anyone else having a little trouble with one record not being sorted?? I have tried several different datasets, and there seems to be a single record that is not being sorted, just pushed to the last item in the NSDictionary.
thanks
tony
Haven’t noticed that, but i’ll check. Did you check your source data? Sometimes a blank space in front of a field will throw off a sort.
Could any one please let me know how we can call a sequence of webservice requests.
For example. First I get some data using webservice1.
http://www.abc.com/webservice1.asmx
Next after its completion i want to call the next one.
http://www.abc.com/webservice1.asmx
I am not able to achieve this sucessfully.
Please let me know.
Thanks
Murlai.
you need to call the second one in the didfinishDownloading delegate call of the first one.
i am getting an error on this line
NSMutableData *retData;
retData = [DataCon initiateConnection];
EXC_BAD_ACCESS….
any idea please ..
detail on CacheDBCommands.m
#pragma mark -
#pragma mark Download Work Methods
-(void)getMyDataset:(NSArray *)params
{
NSString *param1 = [params objectAtIndex:0];
NSString *param2 = [params objectAtIndex:1];
// Create an object to the class above which is the connection to the WCF Service
WebServiceHelper *DataCon = [[WebServiceHelper alloc] init];
//set up service method and urls
DataCon.XMLNameSpace = @”ForXCode”;
DataCon.XMLURLAddress = @”http://www.qt-ltd.com/wsHelloWorld/service1.asmx”;
//set up method and parameters
DataCon.MethodName = @”wsHelloWorld”;
DataCon.MethodParameters = [[NSMutableDictionary alloc] init];
[DataCon.MethodParameters setObject:param1 forKey:@"CustName"];
[DataCon.MethodParameters setObject:param2 forKey:@"School"];
NSMutableData *retData;
retData = [DataCon initiateConnection];
self.myDataset.doDebug = YES; //uncomment to print all data to NSLog
[self.myDataset parseXMLWithData:retData];
[DataCon release];
//call delegate to let view know we have finished downloading this
[delegate performSelectorOnMainThread:@selector(didFinishDownloading:)
withObject:self
waitUntilDone:NO];
}
@end
To sort, I use NSSortDescriptors.
anyone can solve my problem please
Use this tool to check if you have the correct namespace on XMLnamespace.
http://ditchnet.org/soapclient/
Hi there.
Thanks for all your effort creating such a program to get values from online database so easily.
I have downloaded your program and tried running it in iOS SDK 4.3 (In both Simulator and Device).
My Webservice has this method
Public function getVehicleDetails (Maker as string, CarName as string) as ArrayList
I have changed the Web Service Address as it says in my .net Webservice, and also changed the name of the method alongwith the parameters but getting the error below.
[Session started at 2011-07-16 19:37:38 +0900.]
2011-07-16 19:37:43.288 DatasetParser[1867:6903] Method: getVehicleDetails Parameter:param1 Value:Toyota
2011-07-16 19:37:43.292 DatasetParser[1867:6903] Method: getVehicleDetails Parameter:param2 Value:Corolla
2011-07-16 19:37:43.658 DatasetParser[1867:6903] Connection Error: {
param1 = Toyota;
param2 = Corolla;
}
2011-07-16 19:37:43.659 DatasetParser[1867:6903] TableCount:0
2011-07-16 19:37:43.660 DatasetParser[1867:6903] Document Started Parsing…
2011-07-16 19:37:43.661 DatasetParser[1867:6903] Document Ended Parsing…
Any advice will be much appreciated
Waiting for your kind reply.
the framework is for datasets and you are returning an ArrayList from your function instead of a dataset.
Thanks for the reply.
I have rewritten the web service to return the DataSet, and its showing the data when i run it in browser.
After changing the method name when i run your program, i am getting the same error, the Connection Error, although the log shows that i m getting all the data in XML form but its just that the data is not being parsed and it says connection error in WebServiceHelper.m initiateConnection method.
I hope you can advice me with this error.
Waiting for your kind reply.
I am trying to fetch xml using NSMutableRequest and NSURL connection but I am receiving 0 bytes but when I passed ?WSDL in the link so in response get the schema, but I want to fetch xml and then will parse using TouchXML. My code below.
NSString *username = @”username”;
NSString *password = @”pasword”;
NSString *soapMessage = [NSString stringWithFormat:
@"\n"
"\n"
"\n"
"\n"
"%@\n"
"%@\n"
"\n"
"\n"
"\n"
"\n"
"\n\n",password,username];
NSLog(@”%@”,soapMessage);
NSURL *url = [NSURL URLWithString:@"http://websitename.com/website_english/wservice/website_service.asmx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
/*ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseKeychainPersistence:YES];
[request setUsername:@"username"];
[request setPassword:@"password"];
[request setDelegate:self];
[request startAsynchronous];*/
NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];
[request addValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request addValue: @"http://tempuri.org/METHODNAME" forHTTPHeaderField:@"SOAPAction"];
[request addValue: msgLength forHTTPHeaderField:@"Content-Length"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if( theConnection )
{
webData = [[NSMutableData data] retain];
}
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)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@”ERROR with theConenction”);
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@”DONE. Received Bytes: %d”, [webData length]);
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
NSLog(@”%@”,theXML);
[theXML release];
}
Can’t find a way to say: Thank you for the code.
Made this job a LOT easier.
Is there anyway to donate? Please, contact me through my e-mail.
Erich.
Your kind words are enough payment. Thank you very much!
I’m new in iOS developement platform, i went throught the steps, but i stuck in step seven, at this “self.act” cause it gives me error it doesn’t know the “act”?
Hi, great stuff!!! I am using VisualStudio 2005 webservice, takes 1 parameter. I always get Connection Error: param2
I tried to take out param2 but then it says Connection Error: param1 :O
I am getting a response though.
response:
…
test
…
My question is how do I use this data/response?
(replace [] with <> )
response:[?xml version="1.0" encoding="utf-8"?][soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"][soap:Body][getUserInfoResponse xmlns="http://tempuri.org/"][getUserInfoResult][xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"][xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"][xs:complexType][xs:choice minOccurs="0" maxOccurs="unbounded"][xs:element name="Table"][xs:complexType][xs:sequence][xs:element name="USERNAME" type="xs:int" minOccurs="0" /]
…
[/xs:sequence][/xs:complexType][/xs:element][/xs:choice][/xs:complexType][/xs:element][/xs:schema][diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"][NewDataSet xmlns=""][Table diffgr:id="Table1" msdata:rowOrder="0"][USERNAME]test[/USERNAME]
…
[/Table][/NewDataSet][/diffgr:diffgram][/getUserInfoResult][/getUserInfoResponse][/soap:Body][/soap:Envelope]
the response is more obviously but let’s say I want to use USERNAME element…
BTW username is xs:string (typo error when copy/pasting)
I uncommented: self.myDataset.doDebug = YES;
And I get a Thread 3: Program received signal: “EXC_BAD_ACCESS”. on NSLog(@”Connection Error: %@”, [WSerror description], nil);
Uncommented: NSLog(@”%@”, self.Tables, nil);
And I get a Table:
2011-11-15 11:36:32.861 DatasetParser[4235:11503] {
Table = {
0 = {
USERNAME = test;
};
};
}