I need to set a text of a label if some particular conditions are true (don't pay attention to them because that part of code is correct). It should be very easy but surprisingly it does not work! That action is completely skipped. I think the problem is caused by that huge amount of "if statements".
This is my code: (the part that is skipped is in the 2nd piece of code)
-(void)setCustomUsername{
stillChecking = YES;
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
{
// Did user allow us access?
if (granted == YES)
{
// Populate array with all available Twitter accounts
arrayOfAccounts = [account accountsWithAccountType:accountType];
// Sanity check
if ([arrayOfAccounts count] > 0)
{
NSString *customUser = [self updateCustomUser];
int numberOfAccounts = [arrayOfAccounts count];
int accountsAdded = 0;
specAccountIndex = 0;
NSLog(#"index 0 = %#", [[arrayOfAccounts objectAtIndex:0] username]);
NSLog(#"index 1 = %#", [[arrayOfAccounts objectAtIndex:1] username]);
NSLog(#"spec_username = %#", customUser);
NSLog(#"numberOfAccounts = %i", numberOfAccounts);
// Check if a specified username exist.
if (isThereASpecifiedUsername) {
NSLog(#"3");
while (numberOfAccounts > accountsAdded) {
NSLog(#"4");
if ([customUser isEqualToString:[[arrayOfAccounts objectAtIndex:specAccountIndex] username]]) {
NSLog(#"NewTweet will use the account at index %i", specAccountIndex);
accountsAdded = numberOfAccounts;
stillChecking = NO;
//[accountIndexLabel setText:[NSString stringWithFormat:#"%i", selAccountIndex]];
}
else{
++specAccountIndex;
++accountsAdded;
}
}
NSLog(#"specAccountIndex: %i", specAccountIndex);
}
else {
---------------------------THIS IS THE IMPORTANT PART (BELOW)----------------------------
//we set the value of a simple integer to 0
specAccountIndex = 0;
//now we set the string "finalChoice" equal to specAccountIndex
NSString *finalChoice = [NSString stringWithFormat:#"%i", specAccountIndex];
//now just a check (and yes, it works)
NSLog(#"The app will use the account at index %#", finalChoice);
//than we set the text of a label equal to finalChoice (This part does *NOT* work)
[accountIndexLabel setText:finalChoice];
//than we check if the text has been set (This part does *NOT* work)
NSLog(#"accountIndexLabel check = %#", accountIndexLabel.text);
stillChecking = NO;
}
}}}];
while (stillChecking) {}
NSLog(#"accountIndexLabel check at the end of the process = %#", accountIndexLabel.text);
}
As found out in the comments by Paul, your accountIndexLabel is nil. I suppose this is because you forgot to drag a line from the UILabel to the outlet in the owner's code or forgot to add the outlet before your UILabel field OR if you initialize your UILabel in code that you forgot to alloc init it.
Setting a NSString onto a nil object won't raise an exception in Objective-C as it would do in other languages, in fact it's calling a setter on a nil object which is calling a method on a nil object which just does nothing in Objective-C.
Vote up Pauls comment :-)
Related
I need to know whatever an int64_t has decimals, and how many. This should be placed in if-else-statement. I tried this code, but it causes the app to crash.
NSNumber *numValue = [NSNumber numberWithInt:testAnswer];
NSString *string = [numValue stringValue];
NSArray *stringComps = [string componentsSeparatedByString:#"."];
int64_t numberOfDecimalPlaces = [[stringComps objectAtIndex:1] length];
if (numberOfDecimalPlaces == 0) {
[self doSomething];
} else {
[self doSomethingElse];
}
Your question doesn't make a lot of sense; you are creating the NSNumber object from an int so it will never have decimal places, as an int cannot store them. The reason your code is crashing is that it assumes that the array of components is always at least 2 elements long (as you use objectAtIndex:1).
This is better, though still not that good:
NSString *answer = ...; // From somewhere
NSArray *stringComps = [answer componentsSeparatedByString:#"."];
if ([stringComps count] == 0) {
[self doSomething];
} else if [stringComps count] == 1) {
[self doSomethingElse];
} else {
// Error! More than one period entered
}
This still isn't a very good test as it only tests if a period (.) has been entered, not a valid number.
I'm trying to simply search for videos using a query, which is working perfectly using the below code.
// Create a service object for executing queries
GTLServiceYouTube *service = [[GTLServiceYouTube alloc] init];
// Services which do not require sign-in may need an API key from the
// API Console
service.APIKey = #"AIzaSy...";
// Create a query
GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"id,snippet"];
query.maxResults = 10;
query.q = searchBar.text;
query.videoEmbeddable = #"true";
query.type = #"video";
//query.country = #"US";
// Execute the query
GTLServiceTicket *ticket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
// This callback block is run when the fetch completes
if (error == nil) {
GTLYouTubeSearchListResponse *products = object;
[videoArray removeAllObjects];
// iteration of items and subscript access to items.
for (GTLYouTubeSearchResult *item in products) {
NSMutableDictionary *dictionary = [item JSONValueForKey:#"id"];
NSLog(#"%#", [dictionary objectForKey:#"videoId"]);
YoutubeVideo *video = [[YoutubeVideo alloc]init];
[video setLblTitle:item.snippet.title];
//Get youtube video image
[video setImgIconURL:[NSURL URLWithString:item.snippet.thumbnails.defaultProperty.url]];
[video setLblVideoURL:[dictionary objectForKey:#"videoId"]];
[video setLblChannelTitle:item.snippet.channelTitle];
[videoArray addObject:video];
}
reloadData = YES;
[tableView reloadData];
//Download images asynchronously
[NSThread detachNewThreadSelector:#selector(downloadImages)
toTarget:self
withObject:nil];
}else{
NSLog(#"Error: %#", error.description);
}
}];
However, now I'd like to display certain information about the video. Some of this information I can get out of
item.snippet
But I also need to get the video duration, and number of views. How can I get them using Youtube API 3.0?? I also had an idea to try using GData just for this, but it literally triples the load time to use
NSString *JSONString = [NSString stringWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://gdata.youtube.com/feeds/api/videos/%#?v=2&alt=json", [video lblVideoURL]]] encoding:NSUTF8StringEncoding error:nil ];
How do I get the duration of the video, plus the number of views the video has?
Search query only accept ID and Snippet as parts. If you change to Video List Query you can include other parts, but you have to use one of the filters.
I think you'll have to get the video ID with the search query and do another query (Now a video query) filtering by ID (the Id you got), than you can get all other information of the videos you searched.
The problem is i'm having trouble getting the video ID, i think the API use the word "identifier" instead of "id" because it's a reserved word of objective-c.
Edit: Yeah, it was just a matter of time, just request my GTLYoutubeSearchResponse.JSON, an manipulated it as i wanted.
FIRST QUERY:
GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"id,snippet"];
query.maxResults = 10;
query.q = #"iphone";
query.fields = #"items(id,snippet)";
query.order = #"viewCount";
//query.channelId = #"UCsnbNwitAF9BzjjdMfRyK2g";//Kavaco
[appDelegate.service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
id object,
NSError *error) {
if (error == nil) {
appDelegate.videos = object;
[self performSegueWithIdentifier:#"videoList" sender:self];
}
else {
NSLog(#"%#", error.description);
}
}];
SECOND QUERY: In my TableViewController, inside my cellForRowAtIndexPath i do another query for each video i found. Be sure to request only the variables you need to avoid spending your credits, in my case i requested only viewCount.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell" forIndexPath:indexPath];
GTLYouTubeVideo *video = appDelegate.videos[indexPath.row];
NSMutableDictionary *videoIdJson = [video.JSON objectForKey:#"id"];
NSString *videoId = [videoIdJson objectForKey:#"videoId"];
cell.textLabel.text = video.snippet.title;
GTLQueryYouTube *query = [GTLQueryYouTube queryForVideosListWithPart:#"statistics"];
query.identifier = videoId;
query.maxResults = 1;
query.fields = #"items/statistics(viewCount)";
[appDelegate.service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
id object,
NSError *error) {
if (error == nil) {
GTLYouTubeVideoListResponse *detalhe = object;
NSMutableDictionary *responseJSON = detalhe.JSON;
NSArray *tempArray = [responseJSON objectForKey:#"items"];
NSMutableDictionary *items = tempArray[0];
NSMutableDictionary *statistics = [items objectForKey:#"statistics"];
_views = [[NSString alloc] initWithFormat:#"Views: %#",[statistics objectForKey:#"viewCount"]];
cell.detailTextLabel.text = _views;
}
else {
NSLog(#"%#", error.description);
}
}];
cell.detailTextLabel.text = _views;
return cell;
}
Hope it helps.
Collect the id from search API and do another video list API call is the proper way to do what you want to achieve. The video list API call can put multiple video ids separate by comma in the same call. The extra call shouldn't consider exhausting because this is expected behavior on API v3:
Project Member #1 je...#google.com
That's the expected behavior, and not likely to change. Since the
search.list() method can return channels, videos, and playlists, only
properties that make sense for all of those resource types are
returned in the search responses. If you need to obtain any other
properties, making a follow-up request to, e.g., videos.list() is
required. Note that you can pass in up to 50 video ids to
videos.list(), so you can effectively look up an entire page's worth
of search.list() results in a single video.list() call.
If you try https://developers.google.com/youtube/v3/docs/videos/list#try-it , you set contentDetails,statistics as the part, you should able to get the following result:
"contentDetails": {
"duration": "PT20M38S",
"dimension": "2d",
"definition": "hd",
"caption": "false",
"licensedContent": false
},
"statistics": {
"viewCount": "191",
"likeCount": "7",
"dislikeCount": "0",
"favoriteCount": "0",
"commentCount": "0"
}
PT20M38S means 20 minutes and 38 seconds, based on ISO 8601(http://en.wikipedia.org/wiki/ISO_8601)
The best way for make this is:
if (!service) {
service = [[GTLServiceYouTube alloc] init];
service.shouldFetchNextPages = YES;
service.shouldFetchInBackground = YES;
service.retryEnabled = YES;
service.APIKey = #"AIzaSyDSO2JPnM_r9VcDrDJJs_d_7Li2Ttk2AuU";
}
[youtubeList removeAllObjects];
GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"id"];
query.maxResults = 50;
query.q = withText;
query.fields = #"items(id)";
query.order = #"viewCount";
query.type = #"video";
// query.videoDuration = #"long";//any-long-medium-short
__block NSInteger incrementRequest = 0;
[service executeQuery:query completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error) {
NSLog(#"Error is!! = %#", error.localizedDescription);
return;
}
GTLYouTubeVideoListResponse *idsResponse = object;
for (GTLYouTubeVideoListResponse *videoInfo in object) {
[youtubeList addObject:videoInfo.JSON];
GTLQueryYouTube *query2 = [GTLQueryYouTube queryForVideosListWithIdentifier:[[videoInfo.JSON valueForKey:#"id"] valueForKey:#"videoId"] part:#"id,contentDetails,snippet,statistics"];
query2.maxResults = 1;
query2.fields = #"items(id,contentDetails,snippet,statistics)";
query2.order = #"viewCount";
[service executeQuery:query2 completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error) {
NSLog(#"Error is!! = %#", error.localizedDescription);
return;
}
GTLYouTubeVideoListResponse *detalhe = object;
for (NSMutableDictionary *tmpDict in youtubeList) {
if ([[[tmpDict valueForKey:#"id"] valueForKey:#"videoId"] isEqualToString:[[[detalhe.JSON valueForKey:#"items"] objectAtIndex:0] valueForKey:#"id"]]) {
[tmpDict removeObjectForKey:#"id"];
//condition personal
if (![Utils parseISO8601TimeIsGrater30:[[[[detalhe.JSON valueForKey:#"items"] objectAtIndex:0] valueForKey:#"contentDetails"] valueForKey:#"duration"]]) {
BOOL isBlockedInUs = NO;
for (NSString *countryRestric in [[[[[detalhe.JSON valueForKey:#"items"] objectAtIndex:0] valueForKey:#"contentDetails"] valueForKey:#"regionRestriction"] valueForKey:#"blocked"]) {
if ([countryRestric isEqualToString:#"US"]) {
isBlockedInUs = YES;
break;
}
}
if (!isBlockedInUs) {
[tmpDict addEntriesFromDictionary:detalhe.JSON];
[tmpDict setValue:[[[[detalhe.JSON valueForKey:#"items"] objectAtIndex:0] valueForKey:#"snippet"] valueForKey:#"publishedAt"] forKey:#"publishedAt"];
} else {
[youtubeList removeObject:tmpDict];
}
} else {
[youtubeList removeObject:tmpDict];
}
break;
}
}
incrementRequest ++;
if ([idsResponse.items count] == incrementRequest) {
//Finish
[self.tableView reloadData];
}
}];
}
}];
I want to make a user login form and it needs to use emails not just usernames. Is there any way i can make a alert pop up if it is not an email? btw All of this is in xcode.
There is a way using NSPredicate and regular expression:
- (BOOL)validateEmail:(NSString *)emailStr {
NSString *emailRegex = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex];
return [emailTest evaluateWithObject:emailStr];
}
Then, you can display an alert if email address is wrong:
- (void)checkEmailAndDisplayAlert {
if(![self validateEmail:[aTextField text]]) {
// user entered invalid email address
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Enter a valid email address." delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
[alert release];
} else {
// user entered valid email address
}
}
To keep this post updated with modern code, I thought it would be nice to post the swift answer based off of akashivskyy's original objective-c answer
// MARK: Validate
func isValidEmail(email2Test:String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let range = email2Test.rangeOfString(emailRegEx, options:.RegularExpressionSearch)
let result = range != nil ? true : false
return result
}
I did something like this in my app, where I validated that the email address field had 2 parts separated by the '#' symbol, and at least 2 parts separated by a '.' symbol. This does not check that it is a valid email address, but does make sure that it is in the correct format, at least. Code example:
// to validate email address, just checks for # and . separators
NSArray *validateAtSymbol = [[emailRegisterTextField text] componentsSeparatedByString:#"#"];
NSArray *validateDotSymbol = [[emailRegisterTextField text] componentsSeparatedByString:#"."];
// checks to make sure entries are good (email valid, username available, passwords enough chars, passwords match
if ([passwordRegisterTextField text].length >= 8 &&
[passwordRegisterTextField text].length > 0 &&
[[passwordRegisterTextField text] isEqual:[passwordVerifyRegisterTextField text]] &&
![currentUser.userExist boolValue] &&
![[emailRegisterTextField text] isEqualToString:#""] &&
([validateAtSymbol count] == 2) &&
([validateDotSymbol count] >= 2)) {
// get user input
NSString *inputEmail = [emailRegisterTextField text];
NSString *inputUsername = [userNameRegisterTextField text];
NSString *inputPassword = [passwordRegisterTextField text];
NSString *inputPasswordVerify = [passwordVerifyRegisterTextField text];
NSLog(#"inputEmail: %#",inputEmail);
NSLog(#"inputUsername: %#",inputUsername);
NSLog(#"inputPassword: %#",inputPassword);
NSLog(#"inputPasswordVerify: %#",inputPasswordVerify);
// attempt create
[currentUser createUser:inputEmail username:inputUsername password:inputPassword passwordVerify:inputPasswordVerify];
}
else {
NSLog(#"error");
[errorLabel setText:#"Invalid entry, please recheck"];
}
You can have an alert pop up if something is incorrect, but I chose to display a UILabel with the error message, since it seemed less jarring to the user. In the above code, I checked the format of the email address, the password length, and that the passwords (entered twice for verification) matched. If all of these tests were not passed, the app did not perform the action. You can choose which field you want to validate, of course...just figured I'd share my example.
This way works well for me.
1.check string has only one #
2.check at least has one . after #
2.with out any space after #
-(BOOL)checkEmailString :(NSString*)email{
//DLog(#"checkEmailString = %#",email);
BOOL emailFlg = NO;
NSArray *atArr = [email componentsSeparatedByString:#"#"];
//check with one #
if ([atArr count] == 2) {
NSArray *dotArr = [atArr[1] componentsSeparatedByString:#"."];
//check with at least one .
if ([dotArr count] >= 2) {
emailFlg = YES;
//all section can't be
for (int i = 0; i<[dotArr count]; i++) {
if ([dotArr[i] length] == 0 ||
[dotArr[i] rangeOfString:#" "].location != NSNotFound) {
emailFlg = NO;
}
}
}
}
return emailFlg;
}
I've got a problem with an UITextView and one of its delegate methods in my navigation based app:
- (BOOL)textView:aView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
I managed to limit the max length of text the user can input using the above method. But I'm using a leaky array for that matter I think.
The problem is:
I want to save the amount of typed characters right in the very moment the user enters the last line of my textview. I then use that value to calculate the string length - which I compare to the textview's content size to set a limit. The code works fine - but since the method it's inside of is updating with every text input, I'm having trouble releasing the array in the right moment.
Here's some code:
if (numLines == 9)
{
if (!numCharsArray)
{
numCharsArray = [[NSMutableArray alloc] initWithCapacity:1]; // Stack trace gives this line 3,3% of the leak.
}
numChars = tView.text.length;
NSNumber *number = [[NSNumber alloc] initWithInteger:numChars]; // This line gets 77,3%.
[numCharsArray addObject:number]; // This line gets the rest, 24,3%.
[number release];
startChars = [[numCharsArray objectAtIndex:0] integerValue];
NSString *lastLine = [[NSString alloc]initWithString:[[tView text] substringFromIndex:startChars]];
CGSize lineSize = [lastLine sizeWithFont:tView.font forWidth:tView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
[lastLine release];
if (range.length > text.length)
{
return YES;
}
else if (numLines == 9 && lineSize.width >= tView.contentSize.width - 45)
{
return NO;
}
}
else
{
numCharsArray = nil;
/*
if(numCharsArray)
{
[numCharsArray release];
}
*/
}
I tried the out-commented statement above, but that gives me an app crash once I leave the last line of the textview. And as you can see in the code comments - without releasing the array I get a leak.
So how and where do I release that array correctly - keeping it safe while the user is on the last line?
Just replace with
first one
numCharsArray = [NSMutableArray array]; // you do not need to release
//explicitly as its autorelease numberWithInt
second one
NSNumber *number = [NSNumber numberWithInt:numChars]; //autorelease
NSString *lastLine = [[tView text] substringFromIndex:startChars];
I am trying to add data to CoreData. It works fine when I build from Xcode to the phone but when I try to start the app directly from iPhone it crashes on first save to the Context.
I read a text file that is synced via iTunes File Sharing, the file is pretty big (~350 000 lines). The values I get from the file is added to two different arrays (barcodes and productNames). The arrays are later batched through and the sent to the function where I save the data.
From the array loop:
[...]
words = [rawText componentsSeparatedByString:#";"];
int loopCounter = 0;
int loopLimit = 20000;
int n = 0;
int wordType;
NSEnumerator *word = [words objectEnumerator];
NSLog(#"Create arrays");
while(tmpWord = [word nextObject]) {
if ([tmpWord isEqualToString: #""] || [tmpWord isEqualToString: #"\r\n"]) {
// NSLog(#"%#*** NOTHING *** ",tmpWord);
}else {
n++;
wordType = n%2;
if (wordType == kBarcode) {
[barcodes addObject: tmpWord];
}else if (wordType == kProduct) {
[productNames addObject: tmpWord];
}
// Send to batch //
loopCounter ++;
if (loopCounter == loopLimit) {
loopCounter = 0;
NSLog(#"adding new batch");
[self addBatchOfData];
[barcodes release];
[productNames release];
barcodes = [[NSMutableArray arrayWithCapacity:20000] retain];
productNames = [[NSMutableArray arrayWithCapacity:20000] retain];
}
}
[...]
And then the save-function:
-(void)addBatchOfData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError *error;
NSUInteger loopLimit = 5000;
NSUInteger loopCounter = 0;
NSString *ean;
NSString *designation;
for (int i=0; i<[barcodes count];i++ ) {
ean = [barcodes objectAtIndex:i];
designation = [productNames objectAtIndex:i];
Product *product = (Product *)[NSEntityDescription insertNewObjectForEntityForName:#"Product" inManagedObjectContext:importContext];
[product setDesignation:designation];
[product setBarcode:ean];
loopCounter ++;
if (loopCounter == loopLimit) {
NSLog(#"Save CoreData");
[importContext save:&error];
[importContext reset];
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
loopCounter = 0;
}
}
// Save any remaining records
if (loopCounter != 0) {
[importContext save:&error];
[importContext reset];
}
[pool drain];
}
It's really irritating that it works fine when I build from Xcode. Hopefully there is a setting that I missed or something...
EDIT: Forgot to mention that I don't get passed the Default-screen and I don't have any logs. Can it have something to do with the provisioning?
Offload your file loading in a background thread and let the phone start up your main window and view. iOS will kill your app if you do not present a view in a timely manor (this is what you are seeing).
I have to do something like this for my xml -> CoreData converter code. I just present the user with a view notifying them of what is going on and a progress bar (I use https://github.com/matej/MBProgressHUD).
something like:
self.hud = [[MBProgressHUD alloc] initWithView:window];
// Set determinate mode
hud.mode = MBProgressHUDModeDeterminate;
hud.delegate = self;
hud.labelText = #"Converting Data File";
[self.window addSubview:hud];
// Show the HUD while the provided method executes in a new thread
[hud showWhileExecuting:#selector(convertToCoreDataStoreTask) onTarget:self withObject:nil animated:YES];
You just have to make sure that you use a separate NSManagedObjectContext in the new thread.
I would suggest that you implement this delegate method and then try to see what is going on with memory.
when running in the simulator, you have no memory constraints, but when running in the phone you do
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
}
I think I find the solution to my question.
What I was doing was that I started all the heavy data crunch in the "- (void) viewDidLoad {". When I changed it to start the crunch after I clicked a button in the app, it worked just fine.
Right now it's just finding out where the start the data crunch, any suggestions?