using UILocalNotification with randomising strings - ios

I have implemented UILocalNotification success on my project which i failed on randomising strings came from NSDictionary that i need to send whole worlds in each every 5 minute period. In every 5 minute, i need to pass random string in UILocalNotification.
Here is my example code :
NSArray *word1Array= [_tmp objectForKey:#"word1"];
NSArray *word2Array = [_tmp objectForKey:#"word2"];
if([word1Array count] > 0)
{
int minCount = 0;
int totalcount = (int)[word1Array count];
int randomIndex = (arc4random()%(totalcount-minCount))+minCount;
NSString *word1 = [word1Array objectAtIndex:randomIndex];
NSString *word2 = [word2Array objectAtIndex:randomIndex];
NSString *wordbody = [NSString stringWithFormat:#"%# - %#",word1,word2];
UILocalNotification *reminderNote = [[UILocalNotification alloc]init];
reminderNote.repeatInterval = NSMinuteCalendarUnit;
reminderNote.alertBody = wordbody;
reminderNote.alertAction = #"Bak";
reminderNote.soundName = #"sound.aif";
reminderNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:60 * 5];
reminderNote.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:reminderNote];
}
This codes starts to send notification with randomised strings but every 5 minutes, it passed 4-5 words same time which is really annoying. How can i fixed it for just sends 1 string in each 5 minute ?

If I understand your problem correctly, you get the same string for N times at minute 5th. It is because you schedule the Local Notification at the same time.
You should put the above code into something like:-
for(int i=1;i<=5 ;i++){
//Your other code
reminderNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:60 * 5 * i];
//Your other code
}
The above code will schedule to local notification for 5 times, it will notify the user at minute 5, 10, 15, 20, 25 from the current time.
Update Answer:-
Each application on a device is limited to 64 scheduled local
notifications. The system discards scheduled notifications in excess
of this limit, keeping only the 64 notifications that will fire the
soonest. Recurring notifications are treated as a single notification.
See: https://developer.apple.com/Library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/WhatAreRemoteNotif.html
Schedule a new Local Notification after another: UILocalNotification with various alert body

Related

running timers in tableViewCells being overwritten when tableView scrolls

I have tried asking this question several times but I haven't been able to explain what is going on. Maybe some screen shots may help. I can only post one because I don't have enough reputation points yet.
screenshot after tableview scroll
You can see that one of the timers (2) has been reset. I have tried to fix this multiple ways without success. Here is the code that puts the timers into the tableview cells:
-(void) calculateTimer:(NSTimer *)theTimer
{
self.timerItem = [theTimer userInfo];
// for date only cell
if(self.timerItem.timerType == 0){
[theTimer invalidate];
}
for (NRCItemCell *cell in [self.tableView visibleCells])
{
NSIndexPath *ip = [self.tableView indexPathForCell:cell];
NSUInteger row = [[[NRCItemStore sharedStore]allItems] indexOfObjectIdenticalTo:self.timerItem];
if (row == ip.row){
[self configureTimers:cell forRowAtIndexPath:ip];
cell.timer.text = self.timerItem.timerOutput;
cell.timerName.text = self.timerItem.timerName;
}
}
}
-(void)configureTimers:(NRCItemCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NRCtimerItem *item = [[NRCItemStore sharedStore]allItems][indexPath.row];
NSInteger timerType = item.timerType;
// timerType set by TimerTypeTableView Controller as follows:
// 0 - date
// 1 - seconds elapsed
// 2 - minutes elapsed
// 3 - hours elapsed
// 4 - days elapsed
// 5 - months elapsed
// 6 - years elapsed
switch (timerType) {
case 0:{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
NSDate *date = [NSDate date];
NSString *formattedDateString = [dateFormatter stringFromDate:date];
item.timerOutput = formattedDateString;
}
break;
case 1:
{
NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
interval = (-1 * interval);
int time = round(interval);
div_t h = div(time, 3600); //seconds total, divided by 3600 equals
int hours = h.quot; // hours, divided by 60 equals
div_t m = div(h.rem, 60); // minutes
int minutes = m.quot;
int seconds = m.rem; // and remainder is seconds
// NSLog(#"%d:%d:%d", hours, minutes, seconds);
//NSString *intervalString = [NSString stringWithFormat:#"%ld", (long)time];
NSString *intervalString = [NSString stringWithFormat:#"%d hours, %d minutes, %d seconds", hours, minutes, seconds];
NSString *outputString = [intervalString stringByAppendingString:#" ago"];
item.timerOutput = outputString;
}
break;
case 2:
{
NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
interval = (-1 * interval);
int time = roundf(interval);
div_t h = div(time, 3600); // seconds total, divided by 3600 equals
int hours = h.quot; // hours, divided by 60 equals
div_t m = div(h.rem, 60); // minutes
int minutes = m.quot;
NSString *intervalString = [NSString stringWithFormat:#"%d hours, %d minutes", hours, minutes];
NSString *outputString = [intervalString stringByAppendingString:#" ago"];
item.timerOutput = outputString;
}
break;
case 3:
{
NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
interval = (-1 * interval);
int time = roundf(interval);
div_t h = div(time, 3600); // seconds total, divided by 3600 equals
int hours = h.quot; // hours
NSString *intervalString = [NSString stringWithFormat:#"%d hours", hours];
NSString *outputString = [intervalString stringByAppendingString:#" ago"];
item.timerOutput = outputString;
}
break;
case 4:
{
NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
interval = (-1 * interval);
int time = roundf(interval);
div_t h = div(time, 3600); // seconds total, divided by 3600 equals
int hours = h.quot; // hours, divided by 24 equals
div_t d =div(h.rem, 24); // days
int days = d.quot;
NSString *intervalString = [NSString stringWithFormat:#"%d days, %d hours", days, hours];
NSString *outputString = [intervalString stringByAppendingString:#" ago"];
item.timerOutput = outputString;
}
break;
case 5:
{
NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
interval = (-1 * interval);
int time = roundf(interval);
div_t h = div(time, 3600); // seconds total, divided by 3600 equals
__unused int hours = h.quot; // hours, divided by 24 equals
div_t d =div(h.rem, 24); // days
int days = d.quot;
div_t y = div(d.rem, 12);// divided by 12 equals months
int months = y.quot;
NSString *intervalString = [NSString stringWithFormat:#"%d months, %d days", months, days];
NSString *outputString = [intervalString stringByAppendingString:#" ago"];
item.timerOutput = outputString;
}
break;
case 6:
{
NSTimeInterval interval = [self.timerItem.startTime timeIntervalSinceNow];
interval = (-1 * interval);
int time = roundf(interval);
div_t h = div(time, 3600); // seconds total, divided by 3600 equals
__unused int hours = h.quot; // hours, divided by 24 equals
div_t d =div(h.rem, 24); // days
int days = d.quot;
div_t y = div(d.rem, 365);// divided by 365 equals years
int years = y.quot;
NSString *intervalString = [NSString stringWithFormat:#"%d years, %d days", years, days];
NSString *outputString = [intervalString stringByAppendingString:#" ago"];
item.timerOutput = outputString;
}
break;
}
}
The key is the for(NRCItemCell *cell in [self.tableView visibleCells]) fast enumeration. The idea is to loop through the visible cells and only update a cell if the cell's indexPath is equal to the position of the timer in my datastore. However, it looks like scrolling the tableView causes a mismatch between indexPath and and position of the timer in the datastore so that the wrong cell gets overwritten. I have searched all over for an answer and have tried several different approaches but the solution depends on the subtitle label in my custom cell not being overwritten unless the cell position matches the datastore position (which is the way MVC should work, as I understand it). But using reusable cells and scrolling apparently doesn't work the way I thought it did. If there is a solution, I sure would like the help. Thanks in advance!
Your main issue here is the reuse of the cells. Every time you scroll the table the cells are reused with the data of other cells. To keep it short, store your timers data in an array and not in the actual cell.
Importent pointers:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
cell.timerView.data = nil; //reset the data in the current timer
myCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)// Makes sure the cell is still visible
updateCell.timerView.data = timersArray[indexpath.row];
});
}
My bad. I found the bug in my code. It wasn't with tableView at all. I was storing the timers in an array, correctly, not in the cell, even though that's what the comments in the code said. The bug was that I was inadvertently firing a timer every pass through the tableView, so these timers would fire at unpredictable times and then my code would overlay the cell for the corresponding timer. A lot of debugging work, but good experience. Code is working now.
Thanks for the comments!

UILocalNotification triggered but not scheduled

Hi people I have a problem with my app, where I want to add some basic LocalNotifications, which repeat themselves every week. I want to do this in a method called "scheduleLocalNotificationForItem:", which is called when the doneBarButtonItem is pressed. This all seems to be working so far, because when I log all the scheduled notifications every scheduled notification shows up. But when I use the app, the scheduled notifications get triggered and show up but there are some additional notifications, which I haven't set myself and I can't determinate where they come from, which appear too.
So here's my code:
- (int)scheduleNotifitactionsForItem:(AlarmItem *)item
{
NSArray *reorderdRepeat = [NSArray arrayWithArray:[self transformArray:item.repeat]];
int missedDays = 0;
int scheduledAlarms = 0;
for (int i = 0; i < item.repeat.count; i++) {
if ([[reorderdRepeat objectAtIndex:i] boolValue] == true) {//Problem determinating true values at end of array
NSInteger integerOfDay = i + 1;//reorderRepeat should contain seven items, icrement i bevore adding it to integerOfDay
NSDate *lastAlarmTime = [self getFireDateForDayOfWeek:integerOfDay withTime:item.time];
NSArray *allAlramTimesForDay = [self getFireDatesForTime:lastAlarmTime andCycle:item.cycles];
for (int i = 0; i < allAlramTimesForDay.count; i++) {
NSDate *alarmTime = [allAlramTimesForDay objectAtIndex:i];
UIApplication *application = [UIApplication sharedApplication];
UILocalNotification *notification = [UILocalNotification new];
NSDictionary *userInfo = #{#"index":[NSString stringWithFormat:#"%d",item.notification]};
notification.repeatInterval = NSCalendarUnitWeekday;
notification.alertBody = item.title;
notification.userInfo = userInfo;
notification.fireDate = alarmTime;
notification.soundName = item.sound;
[application scheduleLocalNotification:notification];
scheduledAlarms += 1;
}
} else {
missedDays += 1;
}
}
return scheduledAlarms;
}
Help is appreciated ;)
Your repeatInterval should be NSCalendarUnitWeekOfYear (or old NSWeekCalendarUnit). NSCalendarUnitWeekday (NSWeekdayCalendarUnit) will repeat everyday.

Different result for [NSDate date] in several devices

To start, I have to say that I set autoset in date&time settings and time zone is the same for each device. So I use [NSDate date] to get time stamp in milliseconds, then encode to NSData and send to another device. On receiver data is being decoded and subtract with new [NSDate date]. So that I get total time needed for send and receive message. That I was thought because when sender is iPhone 4 iOS6 and receiver is iPhone 5 iOS7 then receiver have earlier time stamp than sender. I don't know why? Maybe [NSData date] isn't the most reliable class for that kind of operations? I use GCDAsyncUdpSocket for sending/receiving UDP.
Code sender
NSData *data2 = [self createRandomNSData:8192];
NSMutableData *dataToSend =[NSMutableData data];
[dataToSend appendBytes:&tag length:sizeof(int)];
long long currentTimeStamp = (long long)([[NSDate date] timeIntervalSince1970]*1000.0);
[dataToSend appendBytes:&currentTimeStamp length:sizeof(long long)];
[dataToSend appendData:data2];
NSLog(#"%i || %lld || %lu",tag, currentTimeStamp,(unsigned long)[dataToSend length]);
[_udpSocket sendData:dataToSend toHost:#"230.0.0.1" port:_port withTimeout:-1 tag:tag];
tag++;
Code receiver
char* dataBytes = [data bytes];
int inTag;
long long inCurrentTimeStamp;
[data getBytes:&inTag length:sizeof(int)];
[data getBytes:&inCurrentTimeStamp range:NSMakeRange(sizeof(int), sizeof(long long))];
long long currentTimeStamp = (long long)([[NSDate date] timeIntervalSince1970]*1000.0);
long long timeStampDiff = currentTimeStamp - inCurrentTimeStamp;
self.delay = timeStampDiff;
NSLog(#"%i || %lld || %lu",inTag, timeStampDiff,(unsigned long)[data length]);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM/dd/yyyy hh:mm:ss"];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
NSString *strSystemTime = [dateFormatter stringFromDate:[NSDate date]];
I faced the same issue and resolved it by setting NSLocale. I hope this solution works for you too.
Don't use a long long of the NSDate's timeIntervalSince1970 * 1000. Use the timeIntervalSince1970 expressed as a double, directly. That will save all the resolution of the date.
Simply add bytes to your data that are the sizeof(double).
Log the double value and it's byte stream before sending, and the double value and it's byte stream on receipt on the remote device and compare them.
If both devices are phones on the same network, and you have them to set their clocks automatically (settings>general>date and time) then their clocks should be synchronized within a fraction of a second.
On receiver data is being decoded and subtract with new [NSDate date]
That's the problem. It has nothing to do with the iOS version installed on the device: in general, if your timestamps are produced by different computers, you cannot subtract them, and expect any kind of precision to come out of it, because the device clocks are not synchronized enough to measure network latency, because of clock skew.
Consider this simplistic example: let's say the clocks on computers Alice and Bob are 10 seconds apart: when Alice's clock shows 12:00:00, Bob's clock shows 12:00:10.
Alice sends Bob its timestamp, which says 14:23:06. It takes the package one second to reach Bob, now Bob sees 14:23:17 when the package arrives. If Bob simply subtracts Alice's timestamp from his own, he would conclude that the package took 11 seconds to reach it.
If Bob sends Alice his timestamp now - let's say it's 14:23:18, Alice would receive it one second later, which by Alice's clock would be 14:23:09. Now Alice would conclude that the package took -9 (yes, negative nine!) seconds to reach it, which makes no sense at all.
Fortunately, if it is fair to assume that the latency is the same on both legs of the round-trip, you can measure the latency by factoring out the clock skew. The idea is to obtain two pairs of timestamps constructed in such a way that the clock skew is a factor in both pairs, but the sign of the skew is opposite.
Consider the timestamps from the above example:
A1=14:23:06 B1=14:23:17
B2=14:23:18 A2=14:23:09
Each pair, A1-B1 and B2-A2, contain the skew, but in the first pair the skew is positive, while in the second pair it is negative. Hence, if you average the two time differences, you would end up with your roundtrip delay.
((B1-A1)+(A2-B2)) / 2 =
(11 + -9) / 2 =
2 / 2 = 1 second
This should be enough for you to implement a simple program for measuring the roundtrip latency in your system.
long intervalValue= (long)([[NSDate date] timeIntervalSince1970]);
NSString *intervalString =[NSString stringWithFormat:#"%ld",intervalValue];
int dif=13-[intervalString length];
for (int k=0; k<dif; k++) {
intervalString=[NSString stringWithFormat:#"%#0",intervalString];
}
unsigned long long convertedValue=[intervalString longLongValue]+0530;

Local Notification Ever Changing Text

I am working on getting local notifications to fire at a time every day (set by the user). I have done this in the past, but just where it was one static message that would get shown every day. I would like for it to take the text for the local notification from a plist file I have made with each row being a quote. Is there a way to fire local notifications, but have it change the text every day?
I have right now:
- (IBAction)scheduleNotification {
Class cls = NSClassFromString(#"UILocalNotification");
if (cls != nil) {
UILocalNotification *notif = [[cls alloc] init];
notif.fireDate = [datePicker date];
notif.timeZone = [NSTimeZone defaultTimeZone];
notif.alertBody = #"Today's 5 Minutes With God Study Is Now Available";
notif.alertAction = #"Ok";
notif.soundName = UILocalNotificationDefaultSoundName;
notif.applicationIconBadgeNumber = 1;
NSInteger index = [scheduleControl selectedSegmentIndex];
switch (index) {
case 0:
notif.repeatInterval = NSDayCalendarUnit;
break;
case 1:
notif.repeatInterval = 0;
break;
}
NSDictionary *userDict = [NSDictionary dictionaryWithObject:#"Today's Quote!"
forKey:kRemindMeNotificationDataKey];
notif.userInfo = userDict;
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
[self.notifications addObject:notif];
[notif release];
}
}
So, how would I get the alertBody to show a different message each day?
You have to create a new notification every time, for every new message.

Does days or number of times used have priority in Appirater?

With Appirater, it allows us developers to ask the user to rate the app. There are two different settings:
static double _daysUntilPrompt = 15;
static NSInteger _usesUntilPrompt = 10;
My question is simple, do 15 days have to go by and 10 uses have to both be YES to have the alert come up? Or do they have a priority type of logic, meaning if one happens before the other, the alert will come up?
Both have to happen before it will come up
You can see the logic for yourself on line 180 of Appirater.m:
- (BOOL)ratingConditionsHaveBeenMet {
if (_debug)
return YES;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]];
NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
NSTimeInterval timeUntilRate = 60 * 60 * 24 * _daysUntilPrompt;
if (timeSinceFirstLaunch < timeUntilRate)
return NO;
// check if the app has been used enough
int useCount = [userDefaults integerForKey:kAppiraterUseCount];
if (useCount <= _usesUntilPrompt)
return NO;
Both conditions need to be met.

Resources