#import "Project-Swift.h" comes some strange issue - ios

Project is mixed with oc and swift.
#import "myProjectName-Swift.h" in the AppDelegate.m,:
Project is Objective-c language created by me, and using almost swift language to write. and In the Appdelegate.m I want to use swift's class, and so
But unfortunate, my project occurs some error in myProjectName-Swift.h:
The code is below:
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, copy)
NSString * _Nonnull ;)
+ (NSString * _Nonnull);
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, copy)
NSString * _Nonnull ;)
+ (NSString * _Nonnull);
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, strong) UIColor * _Nonnull APP_COLOR;)
+ (UIColor * _Nonnull)APP_COLOR;
The error are:
Expected ';' at end of declaration list.
cannot declare variable inside #interface or #protocal
Expected member name or ';' after declaration specifiers
Expected ']'
Property requires fields to be named
Missing '[' at start of message send expression
And so on...
Attension
And In the global swift, I set Chinese characters variable like this:
class Global: NSObject {
}
EDIT - 1
Because I think a error point out the static let "'s ,so I annotation this line, project will not show this error. Or I delete the ,`, will not show the error too.

Objective-C doesn't support Unicode in identifiers (variable names, class names, method names etc), only in string literals ("🏋"). Only a subset of ASCII is allowed (start with _[a-zA-Z], +[0-9] afterwards).
Example:
class MyClass : NSObject {
func 😞() { }
}
This will break when you compile it in a mixed project. Instead you need to give it a valid Objective-C name, like so:
class MyClass : NSObject {
#objc(unhappyFace)
func 😞() { }
}
which will compile (and you need to access it as unhappyFace on the ObjC side).
Or in your example you need to modify your Global object:
class Global: NSObject {
#objc(emptyNumberNotAllowed)
static let 手机号码不能为空 = "手机号码不能为空"
... etc ...
}

Related

Serialize JSONModel - How to convert custom object to JSON in iOS?

How to serialize a custom object with JSONModel ?
#property(nonatomic,strong) NSString<Optional> * tag_post_type;
#property(nonatomic,strong) NSString<Optional> * tag_users_type;
#property(nonatomic,strong) NSArray<Optional> * tags;
#property(nonatomic,strong) NSMutableArray<TagMediaModel*>* tag_posts;
#property(nonatomic,strong) NSMutableArray<TagLocationModel*>* locations;
I try to create a JSON file out of my custom Object with the JSONModel framework for iOS. I get the Error:
EXCEPTION: Invalid type in JSON write (TagMediaModel)
When I call toJSONString Method, I got this issue.
[tagAutomaticModel toJSONString];
This is the model data:
locations = (
"<TagLocationModel> \n [id]: 780307344\n [name]: Hotel Central Park, india\n</TagLocationModel>",
"<TagLocationModel> \n [id]: 463004401\n [name]: Miraj Cinema new year\n</TagLocationModel>",
"<TagLocationModel> \n [id]: 246187965\n [name]: Surya Treasure Island asia\n</TagLocationModel>",
);
"tag_posts" = (
"<TagMediaModel> \n [media_code]: BS0tQeFhU_Z\n [media_id]: 1492016420475981785\n [media_url]: https://scontent.cdninstagram.com/t51.2885-15/e15/17881459_...\n [media_type]: I\n</TagMediaModel>"
);
#property(nonatomic,strong) NSMutableArray<TagMediaModel>* tag_posts;
made few change as above, just removed the star in TagMediaModel.
The angle brackets after NSMutableArray contain a protocol. As #R.Mohan said, you need to remove * pointer and use without pointer.
The protocol must be in place. So add #protocol tag_posts and #protocol locations at top in respective class.
EXCEPTION: Invalid type in JSON write (TagMediaModel) I guess you are converting json to your custom class one or more times. Do it only once. It will resolve your problem.

Get class information from ObjCPropertyDecl

I having some trouble extracting the class information from a clang ObjCPropertyDecl type.
Example Objective-C file:
#import <Foundation/Foundation.h>
#interface Test: NSObject
#property (nonatomic, strong, nullable) NSObject *test;
#property (nonatomic, strong, nullable) NSArray<NSObject *> *test1;
#end
#implementation Test
#end
Dumping the ast gives me this:
...
|-ObjCInterfaceDecl 0x104bbf8e0 <test.m:7:1, line:12:2> line:7:12 Test
| |-super ObjCInterface 0x103962618 'NSObject'
| |-ObjCImplementation 0x104bbff40 'Test'
| |-ObjCPropertyDecl 0x104bbfa20 <line:9:1, col:51> col:51 test 'NSObject * _Nullable':'NSObject *' readwrite nonatomic strong
| |-ObjCPropertyDecl 0x104bbfbe0 <line:10:1, col:62> col:62 test1 'NSArray<NSObject *> * _Nullable':'NSArray<NSObject *> *' readwrite nonatomic strong
| |-ObjCMethodDecl 0x104bbfc50 <line:9:51> col:51 implicit - test 'NSObject * _Nullable':'NSObject *'
| |-ObjCMethodDecl 0x104bbfcd8 <col:51> col:51 implicit - setTest: 'void'
| | `-ParmVarDecl 0x104bbfd60 <col:51> col:51 test 'NSObject * _Nullable':'NSObject *'
| |-ObjCMethodDecl 0x104bbfdc8 <line:10:62> col:62 implicit - test1 'NSArray<NSObject *> * _Nullable':'NSArray<NSObject *> *'
| `-ObjCMethodDecl 0x104bbfe50 <col:62> col:62 implicit - setTest1: 'void'
| `-ParmVarDecl 0x104bbfed8 <col:62> col:62 test1 'NSArray<NSObject *> * _Nullable':'NSArray<NSObject *> *'
...
Previously, I had an OCLint rule that relied on checking the type to see if it was an NSArray, so I'd use an ASTVisitor and this code:
string propertyType = node->getType().getAsString();
// compare to "NSArray *"
Note that both the nullable keyword and the generics in the code sample above change the qualified type (see the AST dump).
My question is: Is there a way I can get only the Objective-C class type from an ObjCPropertyDecl e.g. NSArray * or NSString * without any of the extra sugar?
I've tried getSplitDesugaredType(); and that works well for removing nullable parts but that doesn't remove generics.
Edit:
My current thinking is that I might be able to get the Type from the SplitQualType then cast to ObjCObjectPointerType and get the ObjCObjectType and that might have the information I want but, I'll try implementing that tomorrow.
Ok, after much digging around, I found an acceptable solution to this:
std::string propertyType(clang::ObjCPropertyDecl *d) {
QualType T = d->getType();
if (auto TypePtr = T.getTypePtr()) {
if (TypePtr->isObjCObjectPointerType()) {
if (auto InterfaceType = TypePtr->getAsObjCInterfacePointerType()) {
return InterfaceType->getObjectType()->getBaseType().getAsString();
}
}
}
return "";
}
Examples:
#property (nonatomic, strong, nullable) NSObject *test;
Returns: NSObject
#property (nonatomic, strong, nullable) NSArray<NSObject *> *test1; Returns: NSArray
Note: This will only return values for object pointer types, properties for scalar values like NSInteger etc, will return empty string.

Is there a way to Make NSInvocation surport variable parmas function line [NSstring stringWithFormat:..]

Apple doc says "NSInvocation does not support invocations of methods with either variable numbers of arguments or union arguments. "
i searched for hours ,some people says var_list works, but i tryed ,it does Not
but I think there may be a way to do the same thing (reflection) on variable params function,as i metioned ,[stringWithFormat:],
so , I found a way ,please readt the code bellow .
#interface INTObj : NSObject
#property (nonatomic, assign) int realvalue
#end
#interface FloatObj : NSObject
#property (nonatomic, assign) float realvalue
#end
// here ,the selectorName is only know in runtime
NSString *selectorName = #"stringWithFormat:";
SEL selector = NSSelectorFromString(selectorName);
typedef id (*Obj_Imp)(id,SEL,...);
Method md = class_getClassMethod(obj, selector); // here, obj means NSString
Obj_Imp fun = (Obj_Imp )method_getImplementation(md); // stringWithFormat:...
NSString *arg1 = #"hello %f %d";
FloatObj *fo = [[FloatObj alloc] init];
fo.realvalue = 11.3;
INTObj *io = [[INTObj alloc] init];
io.realvalue = 5;
NSObject *arr[3] = {arg1, fo,io};
// it cracks . exc_bad_Access code = 1
NSString *result = fun(obj , selector, arr[0], [arr[1] realvalue],[arr[2] realvalue]) ;
// it works but i cant do this ,because i don't know the type of the 4th parameters at Runtime
NSString *result = fun(obj , selector, arr[0],[(FloatObj *)arr[1] realvalue],[arr[2] realvalue])
why does the second calling of the function "fun" works while the first one cracks?
is there a better way to to do this?
This has nothing to do with NSInvocation or calling the method implementation directly. You should get the same undefined behavior if you called the method directly:
NSString *result = [NSString stringWithFormat:arr[0], [arr[1] realvalue], [arr[2] realvalue]];
or even a regular function:
NSLog(arr[0], [arr[1] realvalue], [arr[2] realvalue]);
In C (and Objective-C) every expression must have a compile-time type. The compiler needs to be know this compile-time type be able to compile the code correctly.
So let me ask you, what should be the compile-time type of [arr[1] realvalue]? Is it int? Is it float? The compiler will do different things depending on what type it is. If it is float for example, the C standard says that float passed to varargs will be promoted to double. The calling conventions for passing an int and a double in varargs are different (in fact, these two types have different sizes). And +[NSString stringWithFormat:] expects the compile-time type of the thing you pass to match the format specifier you give it in your format string, or there will be undefined behavior.
From your format string, it seems like you wanted the [arr[1] realvalue] argument to be float or double since you used %f. Since FloatObj is the class whose realvalue method returns float, it seems that casting arr[1] to FloatObj * is the right thing to do.

Protocol subclassing and conformance

Given
#protocol Response <NSObject>
#end
#protocol DataResponse <Response>
#end
#interface ListUsersResponse : NSObject <DataResponse>
#end
#interface RequestExecutor : NSObject
+(id<Response>) execute: (id<Request>) request receptor: (Class) model;
#end
ListUsersRequest * request = [self buildListUsersRequest];
ListUsersResponse * result = [RequestExecutor execute:request receptor:[ListUsersResponse class]];
I get an Initializing 'ListUsersResponse *__strong' with an expression of incompatible type 'id<Response>' error. Why is that? Cant the compiler detect protocol conformance? How to solve it?
These warning is because your RequestExecutor's execute: method returns an object of a general id<Response> type. The variables, however, are declared as ListUsersResponse * so the compiler expects a more specific type (and it isn't sure if that type cast is correct or not, it has no way of knowing).
You can get rid of the warning by either:
a) Declaring your variables with id<Response> type instead of ListUsersRequest * type like:
id<Response> result = [RequestExecutor execute:request receptor:[ListUsersResponse class]];
Or
b) Casting them on the fly (if you are sure that at that point they will be of the appropriate class):
ListUsersResponse *result = (ListUsersRequest *)[RequestExecutor execute:request receptor:[ListUsersResponse class]];

Why does passing an NSString object to the "format" parameter of XCTAssertTrue cause a build error?

In attempting to use XCTest to test my application, I get a build error when doing the following:
#import <XCTest/XCTest.h>
#interface MyTests : XCTestCase
#end
#implementation MyTests
- (void)testExample
{
NSString *str = #"foo";
XCTAssertTrue(YES, str); // Parse issue: Expected ')'
}
#end
but I do not get a build error if I do this:
#import <XCTest/XCTest.h>
#interface MyTests : XCTestCase
#end
#implementation MyTests
- (void)testExample
{
XCTAssertTrue(YES, #"foo"); // this is just fine...
}
#end
The build error that I get is:
Parse issue: Expected ')'
and it puts an arrow under the "s" in "str".
I discovered that I can remedy this by changing
XCTAssertTrue(YES, str)
to
XCTAssertTrue(YES, #"%#", str)
but I just cannot figure out why that makes a difference. Can somebody please explain why this is so?
The XCT... macros are written to accept format strings — the strings themselves are optional (so that writing XCTAssertTrue(YES) is valid), but they must be constant strings. You cannot pass objects into the macro without having a format string, which is why XCTAssertTrue(YES, #"%#", str) works, but, say, XCTAssertTrue(YES, str) or XCTAssertTrue(NO, nil) wouldn't.
Deep inside the implementation, the code does this:
#"" format
If format is a constant string literal, the compiler concatenates the strings. If format is anything else, you get a compiler error.
Passing predefined text into the assertion is sometimes desirable so:
XCTAssertTrue(YES, #"foo"); // this is just fine...
As is this
#define FOO #"foo"
XCTAssertTrue(YES, FOO); // this is just fine too...
So I do stuff like:
#define DBUEqualityTestFailed #"Equality test failed"
// test
DBNumber *n1 = [#((int)1) dbNumberFromIntValue];
XCTAssertTrue(*(int *)[n1 valuePointer] == 1, DBUEqualityTestFailed);
XCTAssertTrue([n1 valuePointer] == [n1 valuePointer], DBUEqualityTestFailed);
XCTAssertTrue(*(int *)[n1 valuePointer] == *(int *)[n1 valuePointer], DBUEqualityTestFailed);

Resources