Convert NSString into executable statement - ios

I am facing an issue with converting NSString into executable statement of objective C.
For example,
NSString *strColorAssembly = #"[UIColor redColor]";
Now, I need to convert this string into an executable code and pass to .color property.
This is just an example, I am trying to build a project in which everything will be dynamic, so It would be much helpful if anyone can provide me right direction to go ahead.

I'm not sure if you can programmatically accomplish that goal. If it were me I would implement a method that accepted an NSString (or an NSInteger) and return a UIColor.
- (UIColor *)colorDecode:(NSString *)colorPassed
{
if ([colorPassed isEqualToString #"red"])
{
return [UIColor redColor];
}
else if
.
.
.
}
OR
- (UIColor *)colorDecodewithInt:(NSUInteger)colorIndex
{
switch (colorIndex)
{
case (0)
{
return [UIColor redColor];
break;
}
case (1)
.
.
.
.
default
{
return <some default color>;
break;
}
}
}
EDIT:
I suppose you could also use an NSDictionary that consists of a collection of UIColor objects as well. The bottom line is that you're going to be restricted to a pre-defined set of colors in your compiled code unless you start playing with CGColor at which point you can start dynamically pass the RGB values.

It sounds like what you are looking for is the setValue:ForKey: method. Anything that conforms to the NSKeyValueCoding Protocol will have that option available. However, this will only work for the key part (i.e. the name of the property). iOS explicitly prohibits any modification to the distributed code. I would recommend using the keys as strings and writing some kind of interpreter for the rest of your data.

You can't do that on iOS, period. iOS prevents you from allocating memory pages that can both be written to and also executed.
The best you could manage is an interpreter that takes strings in, parses them, and attempts to map them to Objective-C calls (using NSClassFromString and NSSelectorFromString).
I would recommend not doing what you're trying to do, and instead use one of the many bindings from Cocoa to a dynamic language. I think the most popular ones bind Cocoa to Lua, so then you can write your Lua code and everything will be dynamic.

Related

improving redundant if else query (maybe with design pattern?) AND reduce global variables?

My code works fine, but it seems for me very amateurish.
1)
For example: I'm working with bluetooth and I have always to check, whether there is a device and if the user has a connection.
if isThereADevice != nil && isThereADevice.connected() {
//do stuff via bluetooth
} else {
//do an alternative
}
This construct exists > 25 in my "project". It would be cool to find a better solution, but my coding experience isn't so good. I would be happy to get some information, code snippets or ideas, which I can google :)
2)
The 2nd stupid thing is, that I have a lot global variables. This doesn't look like good style.
For example: In the game at the beginning the user pick a stone. This stone has an ID. Overall I have five UIViewController and in every UIViewController I need this ID.
I just created just a Globals.swift and define:
var globalID: String = "default"
Meanwhile I have > 20 global variables like time, rounds, some objects... When do I use globals in Swift? Do I use them?
What is an solution for my ID-problem?
Without more context it's hard to give a solid answer to your question, but hopefully what I'm about to say will lead you down the right path.
It sounds like you have a Bluetooth service in which there needs to be one and only one of it's type. This may be a good opportunity to use a singleton pattern to represent the Bluetooth service. You could also use a delegate pattern to interface with the Bluetooth service from various entities in your application.
More Information:
Singleton Pattern in Swift
Singleton Design Pattern
Delegation
Delegates and Data Sources
As far as global scope in iOS goes, avoid it. Structs are meant to represent values in Swift, use them to store your values!
For example:
struct UIConstants {
var color = UIColor.blackColor()
var height: CGFloat = 10
}
You can now use this anywhere:
class MyView: UIView {
let constants = UIConstants()
init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = constants.color
}
}
That way these values can be reused, and changed in one location if you ever needed to apply changes to all the entities in your application.
Hope this helps!
For part one create a bluetooth helper class with some static functions that do this check for you and takes in some blocks
class BluetoothHelper {
class func doStuffViaBluetooth(bluetoothDeviceAvailible: () -> (), bluetoothDeviceUnavailible: () -> ())
if isThereADevice != nil && isThereADevice.connected() {
bluetoothDeviceAvailible()
} else {
bluetoothDeviceUnavailible()
}
}
this could then be called like so
BluetoothHelper.doStuffViaBluetooth(bluetoothDeviceAvailible: {
//Do Bluetooth stuff
}, bluetoothDeviceUnavailible: {
//Handle error case
})
This doesn't reduce the number of lines in your code it will in fact increase them slightly but this is far more abstracted and you are only implementing the logic of testing for the device once. This is also much easier for you to test.
As for answer two I would suggest creating a data model with objects like Player and Rounds and passing these objects between view controllers.

Obj-C setValuesForKeysWithDictionary 64-bit vs 32-bit

I am pulling a JSON object from my API and creating it using the following code (in hindsight not the way to go):
+ (YActivity *)instanceFromDictionary:(NSDictionary *)jsonDictionary
{
YActivity * instance = [[YActivity alloc] init];
[instance setAttributesFromDictionary:jsonDictionary];
return instance;
}
- (void)setAttributesFromDictionary:(NSDictionary *)jsonDictionary
{
if (![jsonDictionary isKindOfClass:[NSDictionary class]]) {
return;
}
[self setValuesForKeysWithDictionary:jsonDictionary];
}
One of the keys is "type". I have a read-only variable #synthesized called "type". On the 32-bit version of my app, this is set right away before setValue:(id)value forUndefinedKey:(NSString *)key is called. I reference this value in that method, and on the 64-bit version of my app, when the breakpoint hits this method, type is not set yet.
Clearly this isn't the best course of action. I am just wondering if anyone else as seen this or if I'm barking up the wrong tree. I diffed the files between the two versions and they are identical. I am running them both on iOS 8.1 Simulator, the API is returning the same thing for both...I'm stumped. Basically on the old version defined keys are set before undefined, and on the new version it seems the opposite of that.
NSDictionary objects are unordered collections, so code should never make assumptions about the order in which a dictionary will enumerate its own keys. It turns out that there are implementation differences between the 32- and 64-bit runtimes that affect where hashed values end up being stored.
Since the API contract explicitly doesn't guarantee order, that shouldn't cause problems, but it can (and in this case apparently does) have the side-effect of causing code that formerly 'worked' to break when compiled for the 64-bit architecture.
A quick way to fix the problem you're currently having without significantly changing the implementation would be to enumerate the dictionary's keys yourself, which would allow you to provide an array of keys ordered however you wish:
- (void)setAttributesFromDictionary:(NSDictionary *)dictionary
{
// So instead of doing this...
//
// [self setValuesForKeysWithDictionary:dictionary];
// You could do something along these lines:
//
NSMutableArray *keys = dictionary.allKeys.mutableCopy;
// TODO: insert code to change the order of the keys array.
// Then loop through the keys yourself...
for (NSString *key in keys)
{
[self setValue:dictionary[key] forKey:key];
}
}

NSColorList for iOS, or some way to share colors between IB and code

When developing my apps I usually create a category on UIColor and a custom color palette (CLR file) for Interface Builder for my specific colors. But this means I have to add new colors and update old colors in 2 places. I could create a bunch of IBOutlets, set colors in code, and not use the CLR file, but I hate creating a ton of IBOutlets.
I would like to include the CLR file in my bundle and then access the colors wherever I need in code. On OS X I believe I could achieve this with the NSColorList class. Since NSColorList doesn't exist for iOS I believe my only option is to build my own "ColorList" class that can create and return UIColors from a CLR file.
Is the CLR file format documented somewhere? Am I missing some other way to keep my colors in 1 place and access them from both IB and code?
Apple Docs:
NSColorList Class
Color Programming Guide
I have done just that. I can't exactly share the all code I've written as I wrote it at work under NDA. But as a tip the solution i came up with was a pretty trivial clr exporter to a UIColor category. You can easily write a Mac OS terminal app to do this. Example:
for (NSString *key in _colorList.allKeys) //_colorList is NSColorList
{
NSColor *color = [_colorList colorWithKey:key];
NSNumber *red = #([color redComponent]);
NSNumber *blue = #([color blueComponent]);
NSNumber *green = #([color greenComponent]);
NSNumber *alpha = #([color alphaComponent]);
NSString *method = [NSString stringWithFormat:methodString, key, red, blue, green, alpha];
[codeString appendString:method];
}
If you keep your designer to some standard of common cocoa method naming and color naming convention you can then use the CLR in the IB and use the exporter to export your category. It's not exactly what you're looking for (or I was looking for) but it certainly does the job.

Creating a custom iOS Jailbreak keyboard

I have developed a custom input method and now would like to develop a tweak that would register it as a keyboard in iOS.
There are many different keyboards in Cydia (mainly from Chinese developers) such as TouchPal and Baidu Input that appear in settings as a keyboard, so it is definitely possible.
I have tried looking into the following options (barely 4 days in IDA, Xcode with theos and console):
Text Input bundles located in /System/Library/TextInput — seems to have nothing to deal with the keyboards themselves? Some superclass headers are missing (i.e. TIZephyr... classes) so I couldn't quite figure it out. However a native integration would be awesome.
TextInput private framework — also seems to be just for dictionary and so on
UIKit's UIKB.. and UIKeyboard.. classes — UIKeyboardImpl seems to be something related with the keyboard functioning and UIKeyboardLayout is the thing you build upon.
I tried hooking UIKeyboardDictationLayout to just give a plain instance of a UIKeyboardLayout upon initialization — and when I tapped the mic button on the keyboard, the keyboard went blank! That kind of implementation would be nice too (even though killing dictation functionality is undesired). However, I can't find where do I send typing events as well.
So the points are:
What is responsible for registering a class as an input method?
What is responsible for receiving typing events?
I am asking this in hope that there are developers who had to do something similar already, because I couldn't find any articles nor anything that would give me a hint in the header files and bundles.
Thanks in advance.
I got it right this february even though didn't have the time to respond and it's not quite necessary now that iOS 8 has come.
Still, this is how you load your own keyboard:
%hook UIKeyboardInputMode
+ (id)keyboardInputModeWithIdentifier:(id)arg1 {
id o = %orig;
return o;
}
- (id)primaryLanguage {
if([TegakiLayout isTegaki:[self identifier]]) return #"Tegaki";
return %orig;
}
%end
%hook UIKeyboardImpl
/* This is where the magic is! */
+ (Class)layoutClassForInputMode:(NSString*)arg1 keyboardType:(int)arg2 {
Class sass = %orig;
if ([TegakiLayout isTegaki: arg1]) {
return [TegakiLayout class];
}
return sass;
}
%end
extern "C" NSArray*UIKeyboardGetSupportedInputModes();
extern "C" NSArray*UIKeyboardGetActiveInputModes();
static NSArray* (*orig_modes)();
NSArray* rep_modes() {
NSArray* res = [orig_modes() arrayByAddingObjectsFromArray:#[#"TEGAKI", #"TEGAKI_Graffiti"]];
return res;
}
static NSArray* (*orig_active_modes)();
NSArray* rep_active_modes() {
NSArray* res = orig_active_modes();
return res;
}
%ctor {
%init;
MSHookFunction(UIKeyboardGetSupportedInputModes, rep_modes, &orig_modes);
MSHookFunction(UIKeyboardGetActiveInputModes, rep_active_modes, &orig_active_modes);
}
where TegakiLayout is a subclass of UIKeyboardLayout.
You then implement - (BOOL)isAlphabeticPlane for returning whether it's a traditional keyboard thing and do the custom view creation in showKeyboardWithInputTraits:screenTraits:splitTraits:.
To type in you then use [[UIKeyboardImpl activeInstance]insertText:#"\n"];.
To create a 'globe' button you use this:
Class sw = NSClassFromString(#"UIInputSwitcherView");
[[sw sharedInstance]selectNextInputMode];
Don't forget to implement -keyboardName and -keyplaneName as well!
I'll post the whole project one day probably, but for now it's too large to describe here. This should be enough to get you up and running, though.

Change app language in iOS without restarting the app

I have seems some apps can change the language internally within the app without the need of restarting the app, I am wondering how they are implemented.
For example, for us using NSLocalizedString, I know it is possible to set the language at runtime at main.m when your AppDelegate is not initialized, but once it is initialized (particularly your view controller is created), change it has not effect until the next restart
[[NSUserDefaults standardUserDefaults]
setObject:[NSMutableArray arrayWithObjects:language, nil]
forKey:#"AppleLanguages"];
Anyone have idea how those dynamic language change can be done without restarting the app?
There's some discussion of other approaches here, in particular a notification based approach:
iOS: How to change app language programmatically WITHOUT restarting the app?
In my view there are really three tasks here:
(1) re-localization of resources automatically loaded from nibs. (for example if you dynamically instantiate another custom UIView from a nib, the "old" language strings and settings (images, text direction) will still be loaded)
(2) re-localization of strings currently displayed on the screen.
(3) re-localization of strings inserted by the developer (you) in program code.
Let's start with (3). If you look for the definition you will notice that NSLocalizedString is a macro. So if you don't want to change existing code too much, you can probably solve the problem of (3) by creating a new header file. In that header file, #undef and then re-#define NSLocalizedString to pick the localized string from the appropriate place--not the one that iOS defaults to, but one that you keep track of in some global variable (e.g., in an app delegate ivar). If you don't want to redefine NSLocalizedString but you still make your own alternative , you should probably still #undef NSLocalizedString if you don't want future developers to accidentally call it instead of the macro you replace it with. Not an ideal solution, but maybe the most practical.
As for (1), if you haven't done your localization in Interface Builder, but rather you do it dynamically in viewDidLoad, etc., no problem. You can use the same behavior just discussed (i.e., the modified NSLocalizedString, etc.). Otherwise you can either (a) implement a notification system as described in the link above (complicated), or (b) consider moving localization from IB to viewDidLoad, or (c) try overriding initWithNibName: and swap out the object loaded with the old language resources, with one loaded with the new language resources. This was an approach mentioned by Mohamed at the very bottom of this discussion: http://learning-ios.blogspot.ca/2011/04/advance-localization-in-ios-apps.html. He claims it causes problems (viewDidLoad isn't called). Even if it doesn't work, trying it out might point you towards something that does.
Finally, (2) is presumably the easiest task: just remove and re-add the current view (or in some cases, just redraw it).
the idea is to write a new macro like NSLocalizedString which should check if to take the translation from another specific bundle or not.
The method 2 in this article explain exactly how to do it.
In this particular case, the author doesn't use a new macro, but directly set a custom class for [NSBundle mainBundle].
I hope that #holex will understand the problem reading this.
I'm always using this way, it works perfectly, it might help you as well.
you should set all the texts with NSLocalizableString(...) for the UI for the current language in the -viewWillAppear: method of your every UIViewController.
using this way you (I mean, the users) don't need to restart the application after changing the language of iOS in the Settings.
of course, I'm using the Apple's standard localisation architecture.
UPDATE on (24 Oct 2013)
I've experienced the –viewWillAppear: method won't be performed for the actual view when the application enters to foreground; to solve that issue I also commit the procedure (see above) when I receive UIApplicationWillEnterForegroundNotification notification in the view.
My implementation uses a class to change the language and access the current language bundle. It's an example so if you were to use different languages than I am then change the methods to use your exact language codes.
This class will access the preferred languages from NSLocale and take the first object which is the language being used.
#implementation OSLocalization
+ (NSBundle *)currentLanguageBundle
{
// Default language incase an unsupported language is found
NSString *language = #"en";
if ([NSLocale preferredLanguages].count) {
// Check first object to be of type "en","es" etc
// Codes seen by my eyes: "en-US","en","es-US","es" etc
NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0];
if ([letterCode rangeOfString:#"en"].location != NSNotFound) {
// English
language = #"en";
} else if ([letterCode rangeOfString:#"es"].location != NSNotFound) {
// Spanish
language = #"es";
} else if ([letterCode rangeOfString:#"fr"].location != NSNotFound) {
// French
language = #"fr";
} // Add more if needed
}
return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:#"lproj"]];
}
/// Check if preferred language is English
+ (BOOL)isCurrentLanguageEnglish
{
if (![NSLocale preferredLanguages].count) {
// Just incase check for no items in array
return YES;
}
if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:#"en"].location == NSNotFound) {
// No letter code for english found
return NO;
} else {
// Tis English
return YES;
}
}
/* Swap language between English & Spanish
* Could send a string argument to directly pass the new language
*/
+ (void)changeCurrentLanguage
{
if ([self isCurrentLanguageEnglish]) {
[[NSUserDefaults standardUserDefaults] setObject:#[#"es"] forKey:#"AppleLanguages"];
} else {
[[NSUserDefaults standardUserDefaults] setObject:#[#"en"] forKey:#"AppleLanguages"];
}
}
#end
Use the class above to reference a string file / image / video / etc:
// Access a localized image
[[OSLocalization currentLanguageBundle] pathForResource:#"my_image_name.png" ofType:nil]
// Access a localized string from Localizable.strings file
NSLocalizedStringFromTableInBundle(#"StringKey", nil, [OSLocalization currentLanguageBundle], #"comment")
Change language in-line like below or update the "changeCurrentLanguage" method in the class above to take a string parameter referencing the new language code.
// Change the preferred language to Spanish
[[NSUserDefaults standardUserDefaults] setObject:#[#"es"] forKey:#"AppleLanguages"];
I was stuck in same issue, my requirement was "User can select language from drop down & application have to work according selected language (English or arabic)" What i have done i create two XIB and fetch XIB and Text according selected language. In this way user can select language. I used NSBundle for the same. like
For XIB
self.homeScreen = [[HomeScreen alloc] initWithNibName:#"HomeScreen" bundle:[CommonData sharedCommonData].languageBundle];
For Text
_lblHeading.text = [self languageSelectedStringForKey:#"ViewHeadingInfo"];
/**
This method is responsible for selecting language bundle according to user's selection.
#param: the string which is to be converted in selected language.
#return: the converted string.
#throws:
*/
-(NSString*) languageSelectedStringForKey:(NSString*) key
{
NSString* str=[[CommonData sharedCommonData].languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
You need to load another bundle like this(where #"en" could be locale you need):
NSString *path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
and make macros/function like NSLocalizedString which use your loaded bundle or use methods on that bundle directly like this
[languageBundle localizedStringForKey:key value:value table:tableName];
[[NSBundle mainBundle] localizations] lists all app localizations(including "Base").
Also I wrote helper class which does this(note that it has ReactiveCocoa as a dependency). It allows language change without app restart and sends current locale each time it's changed.

Resources