I use a UIImagePickerController to take movies. The length of a movie is limited by setting the videoMaximumDuration property of the controller.
When one tries to take a longer movie, an alert is shown, as expected.
However an unexpected additional alert entitled VIDEO_TOO_LONG_TITLE is shown directly above the controls (see image below).
Apparently this is an iOS bug (only partly localized, no clip has been selected).
Is it possible to hide this unnecessary and inappropriate alert?
imagePicker.allowsEditing = false
I know the question is quite old, but here's a solution to the problem that is still not addressed by Apple.
#implementation NSBundle (UIImagePickerControllerLocalizationFix)
+ (void) load {
SEL const selector = #selector (localizedStringForKey:value:table:);
Method const localizedStringMethod = class_getInstanceMethod (self, selector);
NSString *(*originalImp) (NSBundle *, SEL, NSString *, NSString *, NSString *) = (typeof (originalImp)) method_getImplementation (localizedStringMethod);
IMP const updatedImp = (typeof (updatedImp)) imp_implementationWithBlock (^(NSBundle *bundle, NSString *key, NSString *value, NSString *tableName) {
NSString *const result = originalImp (bundle, selector, key, value, tableName);
if ([key isEqualToString:#"VIDEO_TOO_LONG_TITLE"] && [result isEqualToString:key]) {
static NSBundle *properLocalizationBundle = nil;
static NSString *properLocalizationTable = nil;
static dispatch_once_t onceToken;
dispatch_once (&onceToken, ^{
NSString *const originalBundleName = bundle.infoDictionary [(NSString *) kCFBundleNameKey];
NSArray <NSBundle *> *const frameworkBundles = [NSBundle allFrameworks];
for (NSBundle *frameworkBundle in frameworkBundles) {
NSString *const possibleTableName = [originalBundleName isEqualToString:tableName] ? frameworkBundle.infoDictionary [(NSString *) kCFBundleNameKey] : tableName;
NSString *const localizedKey = originalImp (frameworkBundle, selector, key, value, possibleTableName);
if (![localizedKey isEqualToString:key]) {
properLocalizationBundle = frameworkBundle;
properLocalizationTable = possibleTableName;
break;
}
}
if (!(properLocalizationBundle && properLocalizationTable)) { // Giving up
properLocalizationBundle = bundle;
properLocalizationTable = tableName;
}
});
return originalImp (properLocalizationBundle, selector, key, value, properLocalizationTable);
} else {
return result;
}
});
method_setImplementation (localizedStringMethod, updatedImp);
}
#end
Related
I want to transform an instance of an object into an instance of a subclass of that object class, so that I can use the additional methods and properties of that subclass, in Objective-C.
How can I do this in a way that does not require me to hardcode the properties of that object class in a copy method?
It is not possible to transform an object into an instance of a subclass in Objective-C. However, with the class below you can supply an instance of both the object and the subclass and have the values of all properties copied to the subclass instance. This implementation works with both Objective-C object types and C primitives. You do not have to specify (or indeed even determine) the properties that need to be copied, providing you know that the important variables are visible and can be set (i.e., there are no properties that are exposed as "read only" or not exposed at all, whose values cannot be recalculated by the class). This method is thus relatively robust for known classes and will not require updating to support future changes you make in your object class that fit these parameters. It is iOS 8 compatible.
This class provides four class methods:
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
Copies all properties of object to subclassObject. If the subclassObject is not a subclass of object, nil is returned.
+ (NSDictionary *) propertiesOfObject:(id)object;
Returns a dictionary of all visible properties of an object, including those from all its superclasses (other than NSObject).
+ (NSDictionary *) propertiesOfClass:(Class)class;
Returns a dictionary of all visible properties of a class, including those from all its superclasses (other than NSObject).
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
Returns a dictionary of all visible properties that are specific to a subclass. Properties for its superclasses are not included.
Header:
// SYNUtilities.h
#import <Foundation/Foundation.h>
#interface SYNUtilities : NSObject
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject;
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
#end
Implementation:
#import "SYNUtilities.h"
#import <objc/runtime.h>
#import <objc/message.h>
#implementation SYNUtilities
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
{
if (![[subclassObject class] isSubclassOfClass:[object class]]) {
return nil;
}
NSDictionary * properties = [self propertiesOfObject:object];
NSLog(#"Properties of %#:\n%#", [object class], properties);
for (NSString * property in properties) {
SEL selector = NSSelectorFromString(property);
if (selector) {
id value = [object valueForKey:property];
[subclassObject setValue:value forKey:property];
}
}
return subclassObject;
}
+ (NSDictionary *) propertiesOfObject:(id)object
{
Class class = [object class];
return [self propertiesOfClass:class];
}
+ (NSDictionary *) propertiesOfClass:(Class)class
{
if (class == NULL) {
return nil;
}
NSMutableDictionary * properties = [NSMutableDictionary dictionary];
[self propertiesForHierarchyOfClass:class onDictionary:properties];
return [NSDictionary dictionaryWithDictionary:properties];
}
+ (NSDictionary *) propertiesOfSubclass:(Class)class
{
if (class == NULL) {
return nil;
}
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
return [self propertiesForSubclass:class onDictionary:properties];
}
+ (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
if (class == NULL) {
return nil;
}
if (class == [NSObject class]) {
// On reaching the NSObject base class, return all properties collected.
return properties;
}
// Collect properties from the current class.
[self propertiesForSubclass:class onDictionary:properties];
// Collect properties from the superclass.
return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties];
}
+ (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
unsigned int outCount, i;
objc_property_t *objcProperties = class_copyPropertyList(class, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = objcProperties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[properties setObject:propertyType forKey:propertyName];
}
}
free(objcProperties);
return properties;
}
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// A C primitive type:
/*
For example, int "i", long "l", unsigned "I", struct.
Apple docs list plenty of examples of values returned. For a list
of what will be returned for these primitives, search online for
"Objective-c" "Property Attribute Description Examples"
*/
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// An Objective C id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// Another Objective C id type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
#end
I needed to create a subclass of NSTextFieldCell, used in an NSTableView, and wanted to keep the properties intact that were set for the cell in Interface Builder.
I solved the task by using NSKeyedArchiver, which is made to store and restore an object's properties.
Since NSTextFieldCell implements initWithCoder, it supports the archiver functions, and therefore I could use this code to init my subclass from the other's properties:
- (id)initWithCell:(NSCell *)cell {
// Use NSArchiver to copy the NSCell's properties into our subclass
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[cell encodeWithCoder:arch];
[arch finishEncoding];
NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self = [self initWithCoder:ua];
// Here I'd set up additional properties of my own class
return self;
}
When I change the app used language independently on the device language it doesn't take effect until I close the app and restart it. How to not require app to be restarted for loading all nib files and .strings files again depending on the selected language?
I use this to change language at runtime:
NSArray* languages = [NSArray arrayWithObjects:#"ar", #"en", nil];
[[NSUserDefaults standardUserDefaults] setObject:languages forKey:#"AppleLanguages"];
This works for me :
Swift 4 :
Create a file named BundleExtension.swift and add the following code to it -
var bundleKey: UInt8 = 0
class AnyLanguageBundle: Bundle {
override func localizedString(forKey key: String,
value: String?,
table tableName: String?) -> String {
guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
let bundle = Bundle(path: path) else {
return super.localizedString(forKey: key, value: value, table: tableName)
}
return bundle.localizedString(forKey: key, value: value, table: tableName)
}
}
extension Bundle {
class func setLanguage(_ language: String) {
defer {
object_setClass(Bundle.main, AnyLanguageBundle.self)
}
objc_setAssociatedObject(Bundle.main, &bundleKey, Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
Now whenever you need to change the language call this method :
func languageButtonAction() {
// This is done so that network calls now have the Accept-Language as "hi" (Using Alamofire) Check if you can remove these
UserDefaults.standard.set(["hi"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
// Update the language by swaping bundle
Bundle.setLanguage("hi")
// Done to reintantiate the storyboards instantly
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
UIApplication.shared.keyWindow?.rootViewController = storyboard.instantiateInitialViewController()
}
I had a similar requirement for a Kiosk mode iPad app with tabbed navigation. Not only did the app need to support on-the-fly language changes, but had to do so knowing that most of the tabs were already loaded from the nibs since the app was only restarted (on average) about once a week when a new version was loaded.
I tried several suggestions to leverage the existing Apple localization mechanisms and they all had serious drawbacks, including wonky support in XCode 4.2 for localized nibs -- my IBoutlet connection variables would appear to be set correctly in IB, but at runtime they would often be null!?
I wound up implementing a class that mimicked the Apple NSLocalizedString class but which could handle runtime changes, and whenever a language change was made by a user my class posted a notification. Screens that needed localized strings (and images) to change declared a handleLocaleChange method, which was called at viewDidLoad, and whenever the LocaleChangedNotification was posted.
All of my buttons and graphics were designed to be language independent, although the title text and label text was typically updated in response to locale changes. If I had to change images, I could have done so in the handleLocaleChange methods for each screen, I suppose.
Here is the code. It includes some support for nib/bundle paths which I actually don't use in the final project.
MyLanguage.h
//
// MyLanguage.h
//
//
#import <Foundation/Foundation.h>
#define DEFAULT_DICTIONARY_FOR_STRINGS #""
#define ACCESSING_ALTERNATE_DICTIONARY_SETS_DEFAULT 1
#define LANGUAGE_ENGLISH_INT 0
#define LANGUAGE_SPANISH_INT 1
#define LANGUAGE_ENGLISH_SHORT_ID #"en"
#define LANGUAGE_SPANISH_SHORT_ID #"es"
#define LANGUAGE_CHANGED_NOTIFICATION #"LANGUAGE_CHANGED"
#interface MyLanguage : NSObject
{
NSString *currentLanguage;
NSDictionary *currentDictionary;
NSBundle *currentLanguageBundle;
}
+(void) setLanguage:(NSString *)languageName;
+(NSString *)stringFor:(NSString *)srcString forLanguage:(NSString *)languageName;
+(NSString *)stringFor:(NSString *)srcString;
+ (MyLanguage *)singleton;
#property (nonatomic, retain) NSBundle *currentLanguageBundle;
#property (nonatomic, retain) NSString *currentLanguage;
#property (nonatomic, retain) NSDictionary *currentDictionary;
#end
MyLanguage.m:
//
// MyLanguage.m
#import "MyLanguage.h"
#import "Valet.h"
#define GUI_STRING_FILE_POSTFIX #"GUIStrings.plist"
#implementation MyLanguage
#synthesize currentLanguage;
#synthesize currentDictionary;
#synthesize currentLanguageBundle;
+(NSDictionary *)getDictionaryNamed:(NSString *)languageName
{
NSDictionary *results = nil;
// for now, we store dictionaries in a PLIST with the same name.
NSString *dictionaryPlistFile = [languageName stringByAppendingString:GUI_STRING_FILE_POSTFIX];
NSString *plistBundlePath = [Valet getBundlePathForFileName:dictionaryPlistFile];
if ( [[NSFileManager defaultManager] fileExistsAtPath:plistBundlePath] )
{
// read it into a dictionary
NSDictionary *newDict = [NSDictionary dictionaryWithContentsOfFile:plistBundlePath];
results = [newDict valueForKey:#"languageDictionary"];
}// end if
return results;
}
+(NSString *)stringFor:(NSString *)srcString forDictionary:(NSString *)languageName;
{
MyLanguage *gsObject = [MyLanguage singleton];
// if default dictionary matches the requested one, use it.
if ([gsObject.currentLanguage isEqualToString:languageName])
{
// use default
return [MyLanguage stringFor:srcString];
}// end if
else
{
// get the desired dictionary
NSDictionary *newDict = [MyLanguage getDictionaryNamed:languageName];
// default is not desired!
if (ACCESSING_ALTERNATE_DICTIONARY_SETS_DEFAULT)
{
gsObject.currentDictionary = newDict;
gsObject.currentLanguage = languageName;
return [MyLanguage stringFor:srcString];
}// end if
else
{
// use current dictionary for translation.
NSString *results = [gsObject.currentDictionary valueForKey:srcString];
if (results == nil)
{
return srcString;
}// end if
return results;
}
}
}
+(void) setLanguage:(NSString *)languageName;
{
MyLanguage *gsObject = [MyLanguage singleton];
// for now, we store dictionaries in a PLIST with the same name.
// get the desired dictionary
NSDictionary *newDict = [MyLanguage getDictionaryNamed:languageName];
gsObject.currentDictionary = newDict;
gsObject.currentLanguage = languageName;
// now set up the bundle for nibs
NSString *shortLanguageIdentifier = #"en";
if ([languageName contains:#"spanish"] || [languageName contains:#"espanol"] || [languageName isEqualToString:LANGUAGE_SPANISH_SHORT_ID])
{
shortLanguageIdentifier = LANGUAGE_SPANISH_SHORT_ID;
}// end if
else
shortLanguageIdentifier = LANGUAGE_ENGLISH_SHORT_ID;
// NSArray *languages = [NSArray arrayWithObject:shortLanguageIdentifier];
// [[NSUserDefaults standardUserDefaults] setObject:languages forKey:#"AppleLanguages"];
//
NSString *path= [[NSBundle mainBundle] pathForResource:shortLanguageIdentifier ofType:#"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
gsObject.currentLanguageBundle = languageBundle;
[[NSNotificationCenter defaultCenter] postNotificationName:LANGUAGE_CHANGED_NOTIFICATION object:nil];
}
+(NSString *)stringFor:(NSString *)srcString;
{
MyLanguage *gsObject = [MyLanguage singleton];
// default is to do nothing.
if (gsObject.currentDictionary == nil || gsObject.currentLanguage == nil || [gsObject.currentLanguage isEqualToString:DEFAULT_DICTIONARY_FOR_STRINGS] )
{
return srcString;
}// end if
// use current dictionary for translation.
NSString *results = [gsObject.currentDictionary valueForKey:srcString];
if (results == nil)
{
return srcString;
}// end if
return results;
}
#pragma mark -
#pragma mark Singleton methods
static MyLanguage *mySharedSingleton = nil;
-(void) lateInit;
{
}
// PUT THIS METHOD DECLARATION INTO THE HEADER
+ (MyLanguage *)singleton;
{
if (mySharedSingleton == nil) {
mySharedSingleton = [[super allocWithZone:NULL] init];
[mySharedSingleton lateInit];
}
return mySharedSingleton;
}
+ (id)allocWithZone:(NSZone *)zone
{ return [[self singleton] retain]; }
- (id)copyWithZone:(NSZone *)zone
{ return self; }
- (id)retain
{ return self; }
- (NSUInteger)retainCount //denotes an object that cannot be released
{ return NSUIntegerMax; }
- (oneway void)release //do nothing
{ }
- (id)autorelease
{ return self; }
#end
Don't rely on strings that you have set in your nib file. Use your nib only for layout & setup of views. Any string that is shown to the user (button text, etc) needs to be in your Localizable.strings files, and when you load your nib you need to set the text on the corresponding view/control accordingly.
To get the bundle for the current language:
NSString *path = [[NSBundle mainBundle] pathForResource:currentLanguage ofType:#"lproj"];
if (path) {
NSBundle *localeBundle = [NSBundle bundleWithPath:path];
}
And to use the bundle to obtain your localized strings:
NSLocalizedStringFromTableInBundle(stringThatNeedsToBeLocalized, nil, localeBundle, nil);
Also for date formatting, you might want to look into
[NSDateFormatter dateFormatFromTemplate:#"HH:mm:ss"" options:0 locale:locale];
To use that you will need to create a NSLocale for the corresponding language/country which you wish to use.
Heres what I did. I guess the trick was to use NSLocalizedStringFromTableInBundle instead of NSLocalizedString.
For all strings, use this
someLabel.text = NSLocalizedStringFromTableInBundle(#"Your String to be localized, %#",nil,self.localeBundle,#"some context for translators");
To change language, run this code
NSString * language = #"zh-Hans"; //or whatever language you want
NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:#"lproj"];
if (path) {
self.localeBundle = [NSBundle bundleWithPath:path];
}
else {
self.localeBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"] ];
}
After this, you will likely want to call whatever update code to update the strings to the new languages, for e.g. run this again
someLabel.text = NSLocalizedStringFromTableInBundle(#"Your String to be localized, %#",nil,self.localeBundle,#"some context for translators");
Thats all. No need restart app. Compatible with system settings as well (if you set a language through iOS settings, it will work too). No need external library. No need jailbreak. And it works with genstrings too.
Of course, you should still do the usual for your app settings to persist:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"zh-Hans", nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
(and do a check in your viewDidLoad or something)
NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:#"lproj"];
if (path) {
self.localeBundle = [NSBundle bundleWithPath:path];
}
else {
self.localeBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"] ];
}
You should create your own macro similar to NSLocalizedString but bases the bundle it chooses a string from on a NSUserDefaults value you set (i.e. don't worry about what the value of apples language defaults value is)
When you change the language you should send out a notification, which view controllers, views etc should listen for and refresh themselves
I have a few keys defined as static vars:
static NSString icon_0 = #"image_0.png";
static NSString icon_1 = #"some_image_with_a_different_name.png";
static NSString icon_3 = #"picure_of_a_bear.png";
now inside a datasource method where I get the indexpath i would like to create the variable name from a string:
-(UICollectionviewCell*)cellForIndexPath:(NSIndexPath *)path
{
NSString *varName = [NSString stringWithFormat:#"icon_%d",path.row];
// here I need the static NSString which corresponds to the var name created
// i.e
NSString imageName;
if (indexPath.row == 0)
{
imageName = #"image_0.png";
}
// would be much nicer to do something like
NSString *imageName = [varName evaluate]; // get the content out of it...
}
How can I do this on static variable?
I tried
NSString *iconName = [self valueForKey:str];
but it isn't an iVar so not working...
i'd not use static vars but a static dictionary like this:
runnable example:
#import <Foundation/Foundation.h>
NSDictionary *DDImageName(NSString *varName);
NSDictionary *DDImageName(NSString *varName) {
static NSDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//TODO
//add all names and the image names here
dict = #{#"icon_0": #"image_0.png",
#"icon_1": #"some_image_with_a_different_name.png",
#"icon_2": #"picure_of_a_bear.png"};
});
return dict[varName];
}
//demo only
int main(int argc, char *argv[]) {
#autoreleasepool {
NSString *varName = #"icon_0";
NSString *imgName = DDImageName(varName);
NSLog(#"imageName for %# = %#", varName, imgName);
}
}
If you make your variables instance variables or properties of an object, then you could use key value coding (KVC) to read and write values to them:
-(UICollectionviewCell*)cellForIndexPath:(NSIndexPath *)path
{
NSString *varName = [NSString stringWithFormat:#"icon_%d",path.row];
// here I need the static NSString which corresponds to the var name created
// i.e
NSString imageName;
if (indexPath.row == 0)
{
[self setValue = #"image_0.png" forKey: varName];
}
}
or
string = [self valueForKey: varName];
As #Daij-Djan points out, though, it's probably better to refactor your code to save your information to a dictionary rather than trying to manipulate your instance variables using string variable names. KVC is fairly slow, and will crash your program if a key doesn't exist at runtime, so it's fragile.
I saw that those are similar
SOCIAL_EXTERN NSString *const SLServiceTypeTwitter NS_AVAILABLE(10_8, 6_0);
SOCIAL_EXTERN NSString *const SLServiceTypeFacebook NS_AVAILABLE(10_8, 6_0);
SOCIAL_EXTERN NSString *const SLServiceTypeSinaWeibo NS_AVAILABLE(10_8, 6_0);
SOCIAL_EXTERN NSString *const SLServiceTypeTencentWeibo NS_AVAILABLE(10_9, 7_0);
SOCIAL_EXTERN NSString *const SLServiceTypeLinkedIn NS_AVAILABLE(10_9, NA);
ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierTwitter NS_AVAILABLE(NA, 5_0);
ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierFacebook NS_AVAILABLE(NA, 6_0);
ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierSinaWeibo NS_AVAILABLE(NA, 6_0);
ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierTencentWeibo NS_AVAILABLE(NA, 7_0);
ACCOUNTS_EXTERN NSString * const ACAccountTypeIdentifierLinkedIn NS_AVAILABLE(NA, NA);
Why keep track of both? Why 2 sets of different constants? Is there a function that can convert one to another or should I make them myself?
Without knowing more details about what you're trying to do, needing to convert between the two sets of constants jumps out at me as being kind of the wrong thing to do.
Social.framework and Accounts.framework are separate frameworks that serve different purposes -- you can use one without the other.
I ended up making these functions:
-(NSString *) strAccountTypeIdentifier
{
if (self.bglm==BGLoginModeTwitter)
{
return ACAccountTypeIdentifierTwitter;
}
else if(self.bglm==BGLoginModeFacebook)
{
return ACAccountTypeIdentifierFacebook;
}
else
{
return nil;
}
}
-(NSString *) strSLServiceType
{
if (self.bglm==BGLoginModeTwitter)
{
return SLServiceTypeTwitter;
}
else if(self.bglm==BGLoginModeFacebook)
{
return SLServiceTypeFacebook;
}
else
{
return nil;
}
}
-(ACAccountType *) ACAccounts
{
NSAssert(self.ACAstore, #"ACAStore must not be empty by this time");
return [self.ACAstore accountTypeWithAccountTypeIdentifier:self.strAccountTypeIdentifier];
}
-(NSString *) strAccountName{
if (self.bglm==BGLoginModeTwitter)
{
return #"Twitter";
}
else if(self.bglm==BGLoginModeFacebook)
{
return #"Facebook";
}
else
{
return nil;
}
}
Now, I need only one enum of type BGLoginMode and that is it. I can get the rest easily by calling those functions.
I moved some code that will be used multiple times into a class.
I'm not getting errors, but I'm also not getting results. It seems to skip over my class completely.
Ideally, this class is supposed to do NSURL conns and XMLParser stuff to chew up the data feed from our hosting API. I already have this working but wanted to congeal and somewhat normalize/centralize some of the main logic of my code.
The one function 'bdCheckIfFileExistsAndisValid' is supposed to take a string but return BOOL and it isn't being called at all.
Neither is 'bdParsePlaylistXML' that is supposed to take a string and return an array.
I put breakpoints everywhere in my class and none are hit.
I'm new so I'm not sure if I did everything right. Here's some code, thanks in advance.
--------------------CUSTOM CLASS:(.h)
#interface bdXMLParser : NSObject {
NSMutableArray *playlist;
//Playlist XML info
BOOL recordTrackName;
BOOL recordTrackDescription;
BOOL recordTrackThumbnailAbsoluteLocation;
BOOL recordTrackURL;
NSString *TrackName;
NSString *TrackDescription;
NSString *TrackThumbnailAbsoluteLocation;
NSString *TrackURL;
}
-(NSMutableArray*) bdParsePlaylistXML:(NSString *) playlistXMLFileName;
-(BOOL) bdCheckIfFileExistsAndisValid:(NSString *) localFileName;
----------------CUSTOM CLASS (.m):
#import "bdXMLParser.h"
#implementation bdXMLParser
{
NSMutableData *webData;
NSMutableArray *playlist;
NSXMLParser *xmlParserPlaylist;
}
-(NSString*) bdDocumentsDirectory{
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return documentsPath;
}
-(int) bdCheckFileCreationDate:(NSString *) fileName {
//get XML file path
NSString *localFilePath = [[self bdDocumentsDirectory] stringByAppendingPathComponent:fileName];
//local file check
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
NSDictionary* attrs = [filemgr attributesOfItemAtPath:localFilePath error:nil];
NSDate *fileCreationDate = [attrs objectForKey: NSFileCreationDate];
NSDate *rightNow = [NSDate date];
NSTimeInterval lastDiff = [fileCreationDate timeIntervalSinceNow];
int lastDiffINT = round(lastDiff);
NSLog(#"NSFileCreationDate:%#",fileCreationDate);
NSLog(#"CurrentDate:%#",rightNow);
NSLog(#"lastDiff:%f",lastDiff);
return lastDiffINT;
}
-(BOOL) bdCheckIfFileExistsAndisValid:(NSString *) fileName {
//local file check
NSString* foofile = [[self bdDocumentsDirectory] stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
if ((fileExists == YES) && ([self bdCheckFileCreationDate:foofile] > -86400))//(24 hrs = 86400 seconds)
return YES;
else
return NO;
}
HERE's THE VIEW WHERE I'M TRYING TO USE IT:(menu.h)
#import "bdXMLParser.h"
#interface MenuScreenViewController : UIViewController <NSXMLParserDelegate>
- (IBAction)btnPlayerPlayPause:(id)sender;
(menu.m)
- (IBAction)btnPlayerPlayPause:(id)sender {
//if array exists, don't reload xml, dont reparse xml, just go to the view
if (playlist.count == 0){
//Playlist!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//========================================================================
//1st: check to see if we have a local cached xml data
//if we do, check if it is <24hr old and if so load it
//if not, go get it with connection and overwrite/store it
//init blogs NSMutableArray
playlist = [[NSMutableArray alloc] init];
//local file check
bdXMLParser *myParser;
BOOL fileExistsAndValid = NO;
=HERE!==fileExistsAndValid = [myParser bdCheckIfFileExistsAndisValid:PlaylistName];
//1st
if (fileExistsAndValid)//(<24 hrs old)
{
NSLog (#"File fileExistsAndValid");
=AND HERE!!=playlist = [myParser bdParsePlaylistXML:PlaylistName];
NSLog(#"playlist:%u", playlist.count);
//load first track
[self LoadTrack:0];
}
else{
NSLog (#"File doesn't exist");
//call refresh function
//[self refreshAlbumPhotoXML];
[myParser bdRefreshPlaylistXML];
}
}
}
you forgot initing the class
bdXMLParser *myParser= [[bdXMLParster alloc]init];