SOAPEngine
This generic SOAP client allows you to access web services using a your iOS app, Mac OS X app and Apple TV app.
With this Framework you can create iPhone, iPad, Mac OS X and AppleTv apps that supports SOAP Client Protocol. This framework able executes methods at remote web services with SOAP standard protocol.
Features
- Support both 2001 (v1.1) and 2003 (v1.2) XML schema.
- Support array, array of structs, dictionary and sets.
- Support for user-defined object with serialization of complex data types and array of complex data types, even embedded multilevel structures.
- Supports ASMX Services, WCF Services (SVC) and now also the WSDL definitions.
- Supports Basic, Digest and NTLM Authentication, WS-Security, Client side Certificate and custom security header.
- Supports iOS Social Account to send OAuth2.0 token on the request.
- AES256 or 3DES Encrypt/Decrypt data without SSL security.
- An example of service and how to use it is included in source code.
iOS
Requirements for- iOS 8.0 and later
- Xcode 8.0 or later
- Security.framework
- Accounts.framework
- Foundation.framework
- UIKit.framework
- libxml2.dylib
Mac OS X
Requirements for- OS X 10.9 and later
- Xcode 8.0 or later
- Security.framework
- Accounts.framework
- Foundation.framework
- AppKit.framework
- Cocoa.framework
- libxml2.dylib
Apple TV
Requirements for- iOS 9.0 and later
- Xcode 8.0 or later
- Security.framework
- Foundation.framework
- UIKit.framework
- libxml2.dylib
Limitations
- for WCF services, only supports basic http bindings (basicHttpBinding).
- in Mac OS X unsupported image objects, instead you can use the NSData.
Known issues
-
Swift 4: the library is currently written in Objective-C and when you import the swift library you will get build errors like this
The use of Swift 3 @objc inference in Swift 4 mode is deprecated
.For silent this warning is need sets
Swift 3 @objc Inference
to default value in the the Build settings of target. but It's not all; the classes used to create requests must be declared with@objcMembers
andNSObject
, eg:class MyClass { ... } let param = MyClass() // ... // ... let soap = SOAPEngine() soap.setValue(param, forKey: "myKey") // ... // ...
the declaration of MyClass must become :
@objcMembers class MyClass: NSObject { ... }
Security for Xcode 8.x or later
From the new Xcode 8 is required an additional setting for the apps, if this setting does not exist you will see a log message like this:
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
To resolve this, add few keys in info.plist, the steps are:
- Open
info.plist
file of your project. - Add a Key called
NSAppTransportSecurity
as a Dictionary. - Add a Subkey called
NSAllowsArbitraryLoads
as Boolean and set its value to YES as like following image.
ref link: http://stackoverflow.com/a/32631185/4069848
How to use
with Delegates :
#import <SOAPEngine64/SOAPEngine.h>
// standard soap service (.asmx)
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
soap.delegate = self; // use SOAPEngineDelegate
// each single value
[soap setValue:@"my-value1" forKey:@"Param1"];
[soap setIntegerValue:1234 forKey:@"Param2"];
// service url without ?WSDL, and you can search the soapAction in the WSDL
[soap requestURL:@"http://www.my-web.com/my-service.asmx"
soapAction:@"http://www.my-web.com/My-Method-name"];
#pragma mark - SOAPEngine Delegates
- (void)soapEngine:(SOAPEngine *)soapEngine didFinishLoading:(NSString *)stringXML {
NSDictionary *result = [soapEngine dictionaryValue];
// read data from a dataset table
NSArray *list = [result valueForKeyPath:@"NewDataSet.Table"];
}
with Block programming :
#import <SOAPEngine64/SOAPEngine.h>
// TODO: your user object
MyClass myObject = [[MyClass alloc] init];
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
soap.version = VERSION_WCF_1_1; // WCF service (.svc)
// service url without ?WSDL, and you can search the soapAction in the WSDL
[soap requestURL:@"http://www.my-web.com/my-service.svc"
soapAction:@"http://www.my-web.com/my-interface/my-method"
value:myObject
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
NSLog(@"%@", dict);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
directly from WSDL (not recommended is slow) :
#import <SOAPEngine64/SOAPEngine.h>
// TODO: your user object
MyClass myObject = [[MyClass alloc] init];
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
// service url with WSDL, and operation (method name) without tempuri
[soap requestWSDL:@"http://www.my-web.com/my-service.amsx?wsdl"
operation:@"my-method-name"
value:myObject
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
NSLog(@"Result: %@", dict);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
with Notifications :
#import <SOAPEngine64/SOAPEngine.h>
// TODO: your user object
MyClass myObject = [[MyClass alloc] init];
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
soap.version = VERSION_WCF_1_1; // WCF service (.svc)
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(soapEngineDidFinishLoading:)
name:SOAPEngineDidFinishLoadingNotification
object:nil];
// service url without ?WSDL, and you can search the soapAction in the WSDL
[soap requestURL:@"http://www.my-web.com/my-service.svc"
soapAction:@"http://www.my-web.com/my-interface/my-method"
value:myObject];
#pragma mark - SOAPEngine Notifications
- (void)soapEngineDidFinishLoading:(NSNotification*)notification
{
SOAPEngine *engine = notification.object; // SOAPEngine object
NSDictionary *result = [engine dictionaryValue];
NSLog(@"%@", result);
}
Synchronous request :
#import <SOAPEngine64/SOAPEngine.h>
NSError *error = nil;
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.responseHeader = YES; // use only for non standard MS-SOAP service like PHP
NSDictionary *dict = [soap syncRequestURL:@"http://www.my-web.com/my-service.amsx"
soapAction:@"http://tempuri.org/my-method" error:&error];
NSLog(@"error: %@, result: %@", error, dict)
Swift 3 language :
var soap = SOAPEngine()
soap.userAgent = "SOAPEngine"
soap.actionNamespaceSlash = true
soap.version = VERSION_1_1
soap.responseHeader = true // use only for non standard MS-SOAP service
soap.setValue("param-value", forKey: "param-name")
soap.requestURL("http://www.my-web.com/my-service.asmx",
soapAction: "http://www.my-web.com/My-Method-name",
completeWithDictionary: { (statusCode : Int,
dict : [AnyHashable : Any]?) -> Void in
var result:Dictionary = dict! as Dictionary
print(result)
}) { (error : Error?) -> Void in
print(error)
}
settings for SOAP Authentication :
#import <SOAPEngine64/SOAPEngine.h>
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
// authorization
soap.authorizationMethod = SOAP_AUTH_BASIC; // basic auth
soap.username = @"my-username";
soap.password = @"my-password";
// TODO: your code here...
settings for Social OAuth2.0 token :
#import <SOAPEngine64/SOAPEngine.h>
#import <Accounts/Accounts.h>
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
// token authorization
soap.authorizationMethod = SOAP_AUTH_SOCIAL;
soap.apiKey = @"1234567890"; // your apikey https://dev.twitter.com/
soap.socialName = ACAccountTypeIdentifierTwitter;
// TODO: your code here...
Encryption/Decryption data without SSL/HTTPS :
#import <SOAPEngine64/SOAPEngine.h>
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.userAgent = @"SOAPEngine";
soap.encryptionType = SOAP_ENCRYPT_AES256; // or SOAP_ENCRYPT_3DES
soap.encryptionPassword = @"my-password";
// TODO: your code here...
Params with Attributes :
// book
NSMutableDictionary *book = [NSMutableDictionary dictionaryWithObject:@"Genesis" forKey:@"name"];
// chapter
NSDictionary *attr = @{@"order": @"asc"};
NSDictionary *child = [soap dictionaryForKey:@"chapter" value:@"1" attributes:attr];
[book addEntriesFromDictionary:child]; // add chapter to book
// book attributes
[soap setValue:book forKey:@"Book" attributes:@{@"rack": @"2"}];
it builds a request like this:
<Book rack="2">
<name>Genesis</name>
<chapter order="asc">1</chapter>
</Book>
W3Schools example :
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.actionNamespaceSlash = YES;
// w3schools Celsius to Fahrenheit
[soap setValue:@"30" forKey:@"Celsius"];
[soap requestURL:@"http://www.w3schools.com/webservices/tempconvert.asmx"
soapAction:@"http://www.w3schools.com/webservices/CelsiusToFahrenheit"
complete:^(NSInteger statusCode, NSString *stringXML) {
NSLog(@"Result: %f", [soap floatValue]);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
WebServiceX example :
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.actionNamespaceSlash = NO;
[soap setValue:@"Roma" forKey:@"CityName"];
[soap setValue:@"Italy" forKey:@"CountryName"];
[soap requestURL:@"http://www.webservicex.com/globalweather.asmx"
soapAction:@"http://www.webserviceX.NET/GetWeather"
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
NSLog(@"Result: %@", dict);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
BarCode with WebServiceX example :
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.actionNamespaceSlash = NO;
NSDictionary *barCodeParam = @{
@"Height" : @(100),
@"Width" : @(150),
@"Angle" : @(0),
@"Ratio" : @(5),
@"Module" : @(0),
@"Left" : @(0),
@"Top" : @(0),
@"CheckSum" : @"true",
@"FontName" : @"Arial",
@"FontSize" : @(20),
@"BarColor" : @"black",
@"BGColor" : @"white",
@"barcodeOption" : @"None",
@"barcodeType" : @"CodeEAN13",
@"checkSumMethod" : @"None",
@"showTextPosition" : @"BottomCenter",
@"BarCodeImageFormat" : @"PNG" };
[soap setValue:barCodeParam forKey:@"BarCodeParam"];
[soap setValue:@"9783161484100" forKey:@"BarCodeText"];
[soap requestURL:@"http://www.webservicex.net/genericbarcode.asmx"
soapAction:@"http://www.webservicex.net/GenerateBarCode"
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
NSString *imgBase64 = [soap stringValue];
NSData *base64 = [[NSData alloc] initWithBase64Encoding:imgBase64];
UIImage *barCodeImage = [[UIImage alloc] initWithData:base64];
} failWithError:^(NSError *error) {
NSLog(@"Error: %@", error);
}];
PAYPAL example with certificate :
SOAPEngine *soap = [[SOAPEngine alloc] init];
// PAYPAL associates a set of API credentials with a specific PayPal account
// you can generate credentials from this https://developer.paypal.com/docs/classic/api/apiCredentials/
// and convert to a p12 from terminal use :
// openssl pkcs12 -export -in cert_key_pem.txt -inkey cert_key_pem.txt -out paypal_cert.p12
soap.authorizationMethod = SOAP_AUTH_PAYPAL;
soap.username = @"support_api1.your-username";
soap.password = @"your-api-password";
soap.clientCerficateName = @"paypal_cert.p12";
soap.clientCertificatePassword = @"certificate-password";
soap.responseHeader = YES;
// use paypal for urn:ebay:api:PayPalAPI namespace
[soap setValue:@"0" forKey:@"paypal:ReturnAllCurrencies"];
// use paypal1 for urn:ebay:apis:eBLBaseComponents namespace
[soap setValue:@"119.0" forKey:@"paypal1:Version"]; // ns:Version in WSDL file
// certificate : https://api.paypal.com/2.0/ sandbox https://api.sandbox.paypal.com/2.0/
// signature : https://api-3t.paypal.com/2.0/ sandbox https://api-3t.sandbox.paypal.com/2.0/
[soap requestURL:@"https://api.paypal.com/2.0/"
soapAction:@"GetBalance" completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
NSLog(@"Result: %@", dict);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
Magento 2 login example :
SOAPEngine *soap = [[SOAPEngine alloc] init];
soap.selfSigned = YES; // only for invalid https certificates
soap.responseHeader = YES;
soap.actionNamespaceSlash = NO;
soap.envelope = @"xmlns:urn=\"urn:Magento\"";
[soap setValue:@"your-username" forKey:@"username"];
[soap setValue:@"your-apykey" forKey:@"apiKey"];
[soap requestURL:@"https://your-magentohost/api/v2_soap/"
soapAction:@"urn:Mage_Api_Model_Server_V2_HandlerAction#login"
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict)
{
NSLog(@"Login return: %@", [soap stringValue]);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
SOAPEngine *soap = [[SOAPEngine alloc] init];
// read local file
NSData *data = [NSData dataWithContentsOfFile:@"my_video.mp4"];
// send file data
[soap setValue:data forKey:@"video"];
[soap requestURL:@"http://www.my-web.com/my-service.asmx"
soapAction:@"http://www.my-web.com/UploadFile"
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
NSLog(@"Result: %@", dict);
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
SOAPEngine *soap = [[SOAPEngine alloc] init];
// send filename to remote webservice
[soap setValue:"my_video.mp4" forKey:@"filename"];
[soap requestURL:@"http://www.my-web.com/my-service.asmx"
soapAction:@"http://www.my-web.com/DownloadFile"
completeWithDictionary:^(NSInteger statusCode, NSDictionary *dict) {
// local writable directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [[paths firstObject] stringByAppendingPathComponent:@"my_video.mp4"];
// the service returns file data in the tag named video
NSData *data = dict[@"video"];
[data writeToFile:@"my_video.mp4" atomically:YES];
} failWithError:^(NSError *error) {
NSLog(@"%@", error);
}];
Optimizations
First of all, if you note a slowdown in the response of the request, try to change the value of the property named actionNamespaceSlash
.
After, when using the method named requestWSDL
three steps are performed :
- retrieve the WSDL with an http request.
- processing to identify the soapAction.
- calls the method with an http request.
this is not optimized, very slow, instead you can use the optimization below :
- retrieving manually the SOAPAction directly from WSDL (once with your favorite browser).
- use the method named requestURL instead of requestWSDL without WSDL extension.
Install in your apps
With Cocoapods
Read the "Getting Started" guide
Cocoapods and Swift
Read the Integrating SOAPEngine with a Swift project
Standard installation
Read the "Standard Installation" guide
Licenses
Trial (simulator) | Single App | Enterprise (multi App) |
---|---|---|