EXC_BAD_ACCESS when converting Objective-C code to Swift - ios

I try to use a Objective-C framework to my project and have some questions when converting Objective-C code to Swift.
One of the APIs:
- (void)ioFrameChannel:(PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData*)payload
payload.data is a struct like this:
typedef struct _PTExampleTextFrame {
uint32_t length;
uint8_t utftext[0];
} PTExampleTextFrame;
I want to get PTExampleTextFrame.utftext which is a message send from the framework
So, I create a local struct:
struct CPTExampleTextFrame {
var length: UInt32
var utf8text: UnsafePointer<UInt8>
}
And write like this:
var textFrame = UnsafePointer<CPTExampleTextFrame>(payload.data).memory
textFrame.length = CFSwapInt32(textFrame.length)
print(textFrame.length) // textFrame.length is correct!
let message = NSString(bytes: textFrame.utf8text, length: Int(textFrame.length), encoding: NSUTF8StringEncoding) // error
print(message)
But I get an error: EXC_BAD_ACCESS(code=1,address=0x31)
Can anyone tell me what's the problem?
And also I give you the framework's example which is in Objective-C:
- (void)ioFrameChannel:(PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData*)payload {
if (type == PTExampleFrameTypeTextMessage) {
PTExampleTextFrame *textFrame = (PTExampleTextFrame*)payload.data;
textFrame->length = ntohl(textFrame->length);
NSString *message = [[NSString alloc] initWithBytes:textFrame->utf8text length:textFrame->length encoding:NSUTF8StringEncoding];
[self appendOutputMessage:[NSString stringWithFormat:#"[%#]: %#", channel.userInfo, message]];
} else if (type == PTExampleFrameTypePing && peerChannel_) {
[peerChannel_ sendFrameOfType:PTExampleFrameTypePong tag:tag withPayload:nil callback:nil];
}
}

Since Objective-C is a superset of C, I'd really recommend that you keep code that interfaces with C as Objective-C code. Interfacing C and Swift directly is an absolute pain.
And an array of char and an unsafe mutable pointer to char are obviously not compatible.

You don't need to create a local struct, it's memory layout is not compatible with c type.
After clarification:
let x: PTExampleTextFrame = UnsafeMutablePointer<PTExampleTextFrame>(payload.data).memory
And use x as you will

Related

Objective C Message Argument Array Parsing (AJNMessageArgument)

I'm working with AllJoyn on iOS using objective C. I'm having trouble parsing an ALLJOYN_ARRAY type in objective C. The problem is that the MsgArg type (C++) is abstracted through the AJNMessagArgument type (objective c). The sample code for parsing an array signature of "a{iv}" in c++ is as follows:
MsgArg *entries;
size_t num;
arg.Get("a{iv}", &num, &entries);
for (size_t i = 0; i > num; ++i) {
char *str1;
char *str2;
uint32_t key;
status = entries[i].Get("{is}", &key, &str1);
if (status == ER_BUS_SIGNATURE_MISMATCH) {
status = entries[i].Get("{i(ss)}", &key, &str1, &str2);
}
}
Now in objective c, the msgarg is the handle of the AJNMessageArgument type. I've tried the following to try getting this to work with no avail:
AJNMessageArgument *strings = [AJNMessageArgument new];
size_t numVals;
QStatus status = [supportedLangsArg value: #"as", &numVals, strings.handle];
if(status != ER_OK){
NSLog(#"ERROR: Could not supported languages from the message argument");
}
This returns ER_OK, but I can't see any data in the handle via the debugger like I can with valid AJNMessageArguments.
Passing in &strings.handle throws a compile error "Address of property expression required".
I've tried quite a few other things, but none make much sense compared to the one above.
Please help me! I need an example of how to parse an "as" signature in objc. I haven't been able to find any docs for this.
Thanks for any help!
Ok, short story is this can't be done without adding custom code to the AJNMessageArgument Class. This is because in this scenario, the "value" method will return a pointer to an array of MsgArg types. Objective C cannot interact with MsgArg - Which is the whole reason they created the AJNMessageArgument wrapper for Objective C.
Here is how it is done:
Add this static method to your AJNMessageArgument.mm class:
+ (NSArray*)getAJNMessageArgumentArrayFromMsgArgArray:(void*)arg : (int)size
{
NSMutableArray * toReturn = [NSMutableArray new];
MsgArg *msgArray = (MsgArg*) arg;
for (int i = 0; i < size; ++i)
{
void * msarg = malloc(sizeof(MsgArg));
MsgArg arg = msgArray[i];
memcpy(msarg, &msgArray[i], sizeof(MsgArg));
AJNMessageArgument *toAdd = [[AJNMessageArgument alloc] initWithHandle:msarg];
[toReturn addObject:toAdd];
}
return [toReturn copy];
}
Don't forget to add the method definition to the AJNMessageArgument.h file:
+ (NSMutableArray*)getAJNMessageArgumentArrayFromMsgArgArray:(void*)arg : (int)size
So now, in our objective C code, we can parse the AJNMessageArgument with signature "as" - but we can't cast it to the MsgArg type yet because we can't access that structure outside of objc++ - so we will use a (void *).
+ (NSArray*)getSupportedLangsFromMessageArgument:(AJNMessageArgument*)supportedLangsArg
{
void *strings; //void * to keep track of MsgArg array data.
size_t numVals;
QStatus status = [supportedLangsArg value: #"as", &numVals, &strings];
if(status != ER_OK){
NSLog(#"ERROR: Could not supported languages from the message argument");
}
NSMutableArray *arrayOfMsgArgs = [AJNMessageArgument getAJNMessageArgumentArrayFromMsgArgArray:strings :numVals];
//Now loop through the resulting AJNMessageArguments of type ALLJOYN_STRING - and parse out the string.
NSMutableArray *arrayOfStrings = [NSMutableArray new];
for (AJNMessageArgument *arg in arrayOfMsgArgs) {
NSString* msgArgValue = [AboutUtil getStringFromMessageArgument:arg];
[arrayOfStrings addObject:msgArgValue];
}
return [arrayOfStrings copy];
}
Now we have an NSArray of NSStrings. Whew.
In case you were wanting to see the code to get the NSString out of the AJNMessageArguments that are in the array, here is that method:
+ (NSString*)getStringFromMessageArgument:(AJNMessageArgument*)msgarg
{
char *charStr;
QStatus status = [msgarg value:#"s", &charStr];
if (status != ER_OK) {
NSLog(#"Error");
}
NSString *str = [NSString stringWithFormat:#"%s", charStr];
return str;
}
Happy AllJoyn-ing.

can I switch NSString

I want to switch NSString in XmlParser because if there are 15 or more web-service then every time the loop check for correct element in IF..ELSE.That I don't want to make processor busy..
I have searched a lot and found using enum I can switch NSString but no luck ..
I have tried each possibilities,but some where i am making mistake.
Please help to solve this big problem for me.
Here I have declare my enum:
Here in "elementName" I am getting Exact value as declared in enum:
But instead of 1, I am getting wrong value Like 202896536:
You cant do it by creating enum. You must need to compare the string.
if([elementName isEqualToString:#"UserLoginComplexType"])
//Do something...
You can not cast a string to ENUM value, you will need to parse it, ENUM values are integers not strings.
You will have to use an if statement.
You could use a helper method:
WebServiceList.h
typedef NS_ENUM(NSUInteger, WebServiceList) {
WebServiceListNone = 0,
UserLoginComplexType = 1,
RegisterUserResult = 2,
RecoverPasswordResult = 3,
....
};
FOUNDATION_EXTERN WebServiceList WebServiceListForString(NSString *string);
WebServiceList.m
WebServiceList WebServiceListForString(NSString *string) {
WebServiceList list = WebServiceListNone;
if (![type isKindOfClass:[NSString class]]) {
return CallRecordTypeNone;
}
else if ([string isEqualToString:#"UserLoginComplexType"] {
list = UserLoginComplexType;
}
else if ([string isEqualToString:#"UserLoginComplexType"]) {
list = UserLoginComplexType;
}
else .....
return list;
}
As seen in your commented codes, you're parsing a XML and saving in a NSMutableArray named arrProductList in App Delegate.
After finishing the parsing of XML, the variable should contain the data in array. You should look into the variable & fetch the corresponding value. Since you didn't post any further details about parsing / XML structure, I'm unable to write some codes related to result fetching.
For easy readability and to avoid lots of if-else statements, I like to do mine as a dictionary:
(also makes it easy to update in the future when you add more to your enum)
NSString* elementName = ...;
// Default value
WebServiceList value = UserLoginComplexType;
NSDictionary* stringToEnum = #{#"UserLoginComplexType":#(UserLoginComplexType),
#"RegisterUserResult":#(RegisterUserResult),
#"RecoverPasswordResult":#(RecoverPasswordResult)};
NSNumber* enumValue = stringToEnum[elementName];
if(enumValue != nil)
value = (WebServiceList)enumValue.integerValue;

CGRectFromString() for general structs

#"{0, 1.0, 0, 1.0}"
I wish to convert the above string to a struct like this:
struct MyVector4 {
CGFloat one;
CGFloat two;
CGFloat three;
CGFloat four;
};
typedef struct MyVector4 MyVector4;
CGRectFromString() does the same thing, only for CGRect. How can I do it for my own structs?
If there is a function for rect it means that it is not working by default.
You have to create your own function something like MyVector4FromString.
You may like to to know that you can init struct object like this also.
MyVector4 v1 = {1.1, 2.2, 3.3, 4.4};
This is a very easy C syntax. so I don't think you require to init from string.
See here : 4.7 — Structs
But if you are getting string from server or from other function than yes you have to create your function to do this. You can parse string and init 4 float value.
This link will help you to divide string in multiple part : Split a String into an Array
All the best.
It can be done in the following way:
-(MyVector4)myVector4FromString:(NSString*)string
{
NSString *str = nil;
str = [string substringWithRange:NSMakeRange(1, string.length - 1)];
NSArray *strs = [str componentsSeparatedByString:#","];
MyVector4 myVec4 = {0,0,0,0};
for (int i=0; i<4; i++)
{
CGFloat value = ((NSString*)[strs objectAtIndex:i]).floatValue;
if (i==0) { myVec4.one = value; } else
if (i==1) { myVec4.two = value; } else
if (i==2) { myVec4.three = value; } else
if (i==3) { myVec4.four = value; }
}
return myVec4;
}
This function can parse strings in format shown in your question like #"{12.0,24.034,0.98,100}"

iOS - XML Pretty Print

I am using GDataXML in my iOS application and want a simple way to format and print an XML string - "pretty print"
Does anyone know of an algorithm in Objective C, or one that works in another language I can translate?
You can modify the source code of GDataXMLNode direcly:
- (NSString *)XMLString {
...
// enable formatting (pretty print / beautifier)
int format = 1; // changed from 0 to 1
...
}
Alternative:
As I didn't want to modify the library directly (for maintenance reasons), I wrote that category to extend the class from outside:
GDataXMLNode+PrettyFormatter.h:
#import "GDataXMLNode.h"
#interface GDataXMLNode (PrettyFormatter)
- (NSString *)XMLStringFormatted;
#end
GDataXMLNode+PrettyFormatter.m:
#import "GDataXMLNode+PrettyFormatter.h"
#implementation GDataXMLNode (PrettyFormatter)
- (NSString *)XMLStringFormatted {
NSString *str = nil;
if (xmlNode_ != NULL) {
xmlBufferPtr buff = xmlBufferCreate();
if (buff) {
xmlDocPtr doc = NULL;
int level = 0;
// enable formatting (pretty print / beautifier)
int format = 1;
int result = xmlNodeDump(buff, doc, xmlNode_, level, format);
if (result > -1) {
str = [[[NSString alloc] initWithBytes:(xmlBufferContent(buff))
length:(xmlBufferLength(buff))
encoding:NSUTF8StringEncoding] autorelease];
}
xmlBufferFree(buff);
}
}
// remove leading and trailing whitespace
NSCharacterSet *ws = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimmed = [str stringByTrimmingCharactersInSet:ws];
return trimmed;
}
#end
I've used HTML Tidy (http://tidy.sourceforge.net/) for things like this. It's a C library so can be linked in to and called from an Objective C runtime fairly easily as long as you're comfortable with C. The C++ API is callable from Objective C++ so that might be easier to use if you're comfortable with Objective C++.
I've not used the C or C++ bindings; I did it via Ruby or Python but it's all the same lib. It will read straight XML (as well as potentially dirty HTML) and it has both simple and pretty print options.

How to capture last 4 characters from NSString

I am accepting an NSString of random size from a UITextField and passing it over to a method that I am creating that will capture only the last 4 characters entered in the string.
I have looked through NSString Class Reference library and the only real option I have found that looks like it will do what I want it to is
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange
I have used this once before but with static parameters 'that do not change', But for this implementation I am wanting to use non static parameters that change depending on the size of the string coming in.
So far this is the method I have created which is being passed a NSString from an IBAction else where.
- (void)padString:(NSString *)funcString
{
NSString *myFormattedString = [NSString stringWithFormat:#"%04d",[funcString intValue]]; // if less than 4 then pad string
// NSLog(#"my formatedstring = %#", myFormattedString);
int stringLength = [myFormattedString length]; // captures length of string maybe I can use this on NSRange?
//NSRange MyOneRange = {0, 1}; //<<-------- should I use this? if so how?
}
Use the substringFromIndex method,
OBJ-C:
NSString *trimmedString=[string substringFromIndex:MAX((int)[string length]-4, 0)]; //in case string is less than 4 characters long.
SWIFT:
let trimmedString: String = (s as NSString).substringFromIndex(max(s.length-4,0))
Try This,
NSString *lastFourChar = [yourNewString substringFromIndex:[yourNewString length] - 4];
You can check this function in Swift 5:
func subString(from myString: NSString, length: Int) {
let myNSRange = NSRange(location: myString.length - length, length: length)
print(myString.substring(with: myNSRange))
}
subString(from: "Menaim solved the issue", length: 4) // Output: ssue

Resources