'CFURLCreateStringByAddingPercentEscapes' deprecated in STTwitter Library - deprecation-warning

I'm using the STTwitter Library in my App and it's been throwing a deprecation warning that says the following:
'CFURLCreateStringByAddingPercentEscapes' is deprecated: first deprecated
in iOS 9.0 - Use [NSString stringByAddingPercentEncodingWithAllowedCharacters:]
instead, which always uses the recommended UTF-8 encoding, and which encodes
for a specific URL component or subcomponent (since each URL component or
subcomponent has different rules for what characters are valid).' deprecated
error.
Here's the code that is causing the problem in STTwitter:
#implementation NSString (RFC3986)
- (NSString *)st_stringByAddingRFC3986PercentEscapesUsingEncoding:(NSStringEncoding)encoding {
NSString *s = (__bridge_transfer NSString *)(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8));
return s;
}
#end
The question is how to replace this with equivalent code using 'stringByAddingPercentEncodingWithAllowedCharacter'.

Here's valid replacement code:
#implementation NSString (RFC3986)
- (NSString *) st_stringByAddingRFC3986PercentEscapesUsingEncoding: (NSStringEncoding) encoding {
NSMutableCharacterSet *allowed = [NSMutableCharacterSet alphanumericCharacterSet];
[allowed addCharactersInString: #"-._~"];
return( [self stringByAddingPercentEncodingWithAllowedCharacters: allowed] );
}
#end
I've tested the new code verses the old and in all cases, thus far, the strings they generate are equal.
In the original code, the idea was to escape a specific named set of characters. They were:
!*'();:#&=+$,/?%#[]
When you switch to using 'stringByAddingPercentEncodingWithAllowedCharacters', the logic reverses and only characters not named are escaped. So, the new code specifies 'alphanumericCharacterSet' and a small group of additional characters are added to it. The added characters are:
-._~
Those characters not named in the new code are the same set set of characters explicitly named in the old code..
As an aside, it is not clear to me that that the passed encoding parameter in the original code was ever used.

Related

How to work around NSTask calling -[NSString fileSystemRepresentation] for arguments

It seems that NSTask calls -[NSString fileSystemRepresentation] to encode values for each of the arguments you give it.
This can become a problem in some situations due to the fact that -fileSystemRepresentation encodes using decomposed unicode forms: for example, the a-umlaut (ä) would be encoded as U+0061 (Latin small letter a) and U+0308 (Combining diaeresis), as opposed to U+00E4 (Latin small letter a with diaeresis). The -UTF8String method, on the other hand, seems to do the opposite.
I need my NSTask arguments to be encoded using composed forms. How do I work around this issue?
A possible solution would be to subclass NSString and provide your own implementation of -fileSystemRepresentation, but unfortunately NSString is a class cluster and thus very difficult to subclass (which is also discouraged by Apple's documentation).
However, we can create a separate class that poses as an NSString, but provides its own implementation of -fileSystemRepresentation.
This can, however, create problems if NSTask does anything with the class identity of the argument objects. Currently I have no evidence that this is the case — this workaround seems to work perfectly.
Header:
// MYTaskArgument.h
#interface MYTaskArgument : NSObject
+ (instancetype) taskArgumentWithString:(NSString *)str;
#end
Implementation:
// MYTaskArgument.m
#interface MYTaskArgument ()
#property(copy) NSString *string;
#end
#implementation MYTaskArgument
+ (instancetype) taskArgumentWithString:(NSString *)str {
MYTaskArgument *ret = [[MYTaskArgument alloc] init];
ret.string = str;
return ret;
}
- (const char *) fileSystemRepresentation {
return self.string.UTF8String;
}
- (id) forwardingTargetForSelector:(SEL)aSelector {
return self.string;
}
#end
We ran in to this trying to execute shortcuts via NSTask, essentially:
shortcuts run "Foréver"
NSTask decomposes the characters (FB10036117), and then shortcuts cannot find the matching shortcut (FB10036113).
Our solution was to pre-convert the argument to UTF8, and then map the characters to octal \123 (because octal works in POSIX and thus bash, dash, and csh), and then expand the parameter within the command using printf:
/usr/bin/shortcuts run "`printf '\106\157\162\303\251\166\145\162'`"
Clearly not an ideal solution, but it works and doesn't depend on trying to fake an NSString (which is very clever, but potentially very fragile).

NSTableViewCell showing , for int

I recently installed Mountain Lion and noticed that my Mac OS X app that has data bindings now is showing an int32 NSTableViewCell as x,xxx.
IE: The .storedata file shows <attribute name="itemid" type="int32">2533</attribute> while the item is displayed in the cell as 2,533. I cannot figure out why it does this in Mountain Lion but didn't in Lion.
How do I get the cell to show as 2533 instead of 2,533
It is somewhat documented in the 10.8 release notes:
NSString localized formatting
In 10.8, in apps linked against the 10.8 SDK, -localizedStringWithFormat:, and -initWithFormat:locale: (and friends) when supplied with a non-nil locale, will now do localized formatting of numbers. Previously these calls already did decimal point handling; so in some locales comma would be used for decimal point separator. This new behavior builds upon this to use localized digits as well as thousands separators and proper placement of the sign symbol.
However I still consider it a bug (and filed it), there are many problems with it (screwing with legacy UIs, occasionally wrong behavior when edited by hand etc).
For new UIs you are probably best of adding a number formatter to your nib that you use for all text fields showing numbers.
If (like in my case) you have many nib files with even more text fields, this ugly hack may help:
#import "HHUTextFieldCell.h"
#implementation HHUTextFieldCell //:: NSTextFieldCell
//*****************************************************************************
// Class methods
//*****************************************************************************
+ (void)load {
//
// 10.8 started using thousands separators for text fields. For our legacy
// apps we don't want those. Rather than changing dozens of xib files with
// hundreds of text fields, we use a replacement class to modify the text
// fields to not have a thousands separator.
//
[NSKeyedUnarchiver setClass:[HHUTextFieldCell class] forClassName:#"NSTextFieldCell"];
}
//*****************************************************************************
// Overwritten methods
//*****************************************************************************
- (void)setObjectValue:(id < NSCopying >)object {
//
// If `object` is an NSNumber object and no formatter is set, we instead
// set the description of that number via -setStringValue:. Otherwise
// use the original implementation.
//
if(!self.formatter && [(NSObject *)object isKindOfClass:[NSNumber class]])
{
[super setStringValue:[(NSObject *)object description]];
}
else
{
[super setObjectValue:object];
}
}
#end
I ended up adding a Value Transformer
#implementation ItemIdValueTransformer
+(Class)transformedValueClass
{
return [NSString class];
}
+ (BOOL)allowsReverseTransformation
{
return NO;
}
- (id)transformedValue:(id)value
{
// Remove the ,'s from Mountain Lion and up
NSString *string = [NSString stringWithFormat:#"%li",(long)[value integerValue]];
return string;
}
#end

Problem declaring and calling internal metthods

How do I declare and use small helper functions inside my normal methods ?
In on of my objective-c methods I need a function to find an item within a string
-(void) Onlookjson:(id) sender{
NSString * res = [[sender gstring] copy];
persInfoBirth.text = getKeyValue(res, #"Birth");
}
I came up with a normal C type declaration for helper function getKeyvalue like this
NSString * getKeyvalue(NSString * s, NSString * key){
NSString *trm = [[s substringFromIndex:2] substringToIndex:[s length]-3];
NSArray *list = [trm componentsSeparatedByString:#";"];
//....
NSString res;
res = [list objectAtIndex:1];
//...
return res;
}
Example input string in s:
s=#"{ Birth = "1910"; Death = "1936"; }";
Anyway I get an exception "unrecognized selector sent to instance" for any of the two first lines in the helper function
How do I declare helper functions that are just to be used internally and how to call them safely ?
regards
Martin
Is this the real code? Do you get zero errors and warnings from the compiler? You must not ignore compiler warnings and you should turn on the Static Analyser in addition to the standard warnings.
There are many things wrong with the above code, most of which are nothing todo with declaring and calling methods. There is no way the above code could compile so maybe it pasted incorrectly or something..
Anyway.. declaring and using methods. Why are using a c function? Unless you have a good reason why not use Objective-c ? If you do have a good reason to use a C function the your definition should be:-
NSString *getKeyvalue( NSString *s, NSString *key ){
...
}
note the arguments. As NSString instances reside in the heap (not on the stack) you always want to pass pointers to them.
You then need to put the declaration in the header file:-
NSString *getKeyvalue( NSString *s, NSString *key )
EDIT:
In Objective-c there is no distinction between normal methods and helper methods, there is only one kind, and you have aleray written one
- (void)onLookJson:(id)sender { .. }
Taking it apart..
All methods begin with + or –, indicating Class method or Instance method. As you are familiar with C++ i guess you know what this means.
(void) is the return type. ie this method doesn't return a value. If it did it might look like (float) or (NSString *) or (id).
onLookJson: is the method name and the method takes 1 argument. Notice that the ':' is actually part of the name. This method is never is any circumstance just 'onLookJson'. An argument must always follow the :, so a method that doesn't take any arguments must not have one.
Ex
- (NSString *)fullName { .. }
This is an instance method, for example of a Person Class, you would call it like:-
NSString *theName = [aPerson fullName];
So
a method name that takes no
arguments is like 'speak'
a method
name that takes 1 argument is like
'speakTo:'
a method name that takes 2
arguments is like 'speakTo: language:'
a method name that takes 3
arguments is like 'speakTo: language: volume:'
etc.
All that is left is to put in the argument types and names.
Your function definition:
NSString *getKeyvalue( NSString *s, NSString *key ){
would become..
- (NSString *)getValue:(NSString *)s key:(NSString *)key { .. }
again, you need to declare it in the header or you will get a compiler warning.
- (NSString *)getValue:(NSString *)s key:(NSString *)key;

Understanding the line between Objective-C and Foundation, specifically NSString

In Objective-C, I can write:
id pString = #"Hello, World.";
and the compiler will instantiate an NSString without me needing to explicitly call a factory method. However, NSString is really just a Foundation class and thus presumably not part of the actual Objective-C language definition.
So when I write #"String", how does the compiler know to build an NSString in particular, and not some other string-like object? In other words, where does the Objective-C language stop and the Foundation library start?
When you write Objective-C code outside of Cocoa or GNUStep environments, #"..." is not linked to NSString.
In this case, gcc provides an option for specifying a class associated to literal strings:
-fconstant-string-class=class-name
Use class-name as the name of the class to instantiate for each
literal string specified with the syntax "#"..."". The default
class name is "NXConstantString".
The #"" directive appears to be built-in to the objective-c compiler.
For instance, if you remove all #imports from your .m source file (& prefix header), the following line will be a syntax error:
NSString *string = #"ABCD"; // (Doesn't know anything about NSString class)
However, if you change the Foundation NSString type to the built-in void type, it will compile just fine:
void *string = #"ABCD";
So, even without Foundation's NSString definition, the compiler knows how to turn #"" into something that can become an NSString instance at runtime (it probably won't instantiate without Foundation, but the compiler doesn't seem to mind); Since it accepts the syntax without needing any external library definitions, the compiler sees #"" as part of the language.
Your code, however, won't be able to make use of any #"" instance without importing Foundation.h, so from the point of view of your program, #"" is part of the library.
Another interesting bit is that an Objective-C string literal (#"" notation) returns the same type as an explicitly instantiated NSString object:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
printf("string literal class: %s\n", object_getClassName(#"a string literal"););
NSString *str = [[NSString alloc] initWithUTF8String:"asdf"];
printf("explicit NSString class: %s", object_getClassName(str));
[pool drain];
return 0;
}
I vaguely remember that in other, older implementations of Objective-C, the string literal actually returned an object of a slightly different class, but that could be used interchangeably with NSString/NSCFString. Not totally sure on that part, though.

Functions in Objective-C

I am trying to write a function which returns a string created from two input strings;
but when I try the function declaration
NSString Do_Something(NSString str1, NSString str2)
{
}
the compiler gets sick. (Worked fine for a different function with int arguments.)
If I change the input arguments to pointers to strings, in also gets sick.
So how do I pass Objective-C objects into a function?
All Objective-C objects being passed to functions must be pointers. Rewriting it like this will fix your compiler error:
NSString *Do_Something(NSString *str1, NSString *str2) { }
Also, please keep in mind that this is a (C-style) function and not an instance method written on an Objective-C object. If you wanted this to actually be a method on an object it would probably look something like this:
NSString *doSomethingWithString1:(NSString *)str1 string2:(NSString *)str2 { }
I say "probably" because you can name it however you want.
Functions are perfectly fine in Objective-C (and in fact earn some of the language's benefits).
See my answer to C function always returns zero to Objective C, where someone was trying what you are and had a problem with the compiler assuming return type. The structure that I set up there is important when you are using functions, just like when you are using objects and methods. Be sure to get your headers right.
To be pedantic, you're using a function definition of:
NSString *DoSomething(NSString *str1, NSString *str2) {
// Drop the _ in the name for style reasons
}
And you should be declaring it in a .h file like so:
NSString *DoSomething(NSString *str1, NSString *str2);
Just like C.
that doesn't work for me. i've just declared in the .h:
NSString *myFunction(NSDecimal *value);
and i type in the .m:
NSString *myFunction(NSDecimal *value){
//code
}
but always i get an error saying expected '(' before '*' token
now is fixed. for some reason... sorry.