I am trying to make a leaderboard using GameCenter on iOS7 that shows only scores submitted in the last week.
I am aware that with iOS7 the use of timescope when opening the gamecenter view controller has been deprecated, but every post I find on this suggests building a leaderboard within the game to get around this.
We have our own leaderboards implemented in game which we fetch using loadScoresWithCompletionHandler, but the timeScope settings still does not appear to work. What ever I set it to, I receive the best score of all time for the user, not the best sore in the last day / week.
Has anyone else found this or does it work for everyone else? I confirm the score is wrong be looking at the "date" on any received GKScores.
The code in question is below:
const char* szName("LeaderboardName_Week1");
NSString* pxLeaderboardName = [NSString stringWithUTF8String:szName];
GKLeaderboardTimeScope eTimeScope = GKLeaderboardTimeScopeWeek;
GKLeaderboard* leaderboardViewer = [[[GKLeaderboard alloc] init] autorelease];
if (leaderboardViewer)
{
[leaderboardViewer setIdentifier: pxLeaderboardName];
[leaderboardViewer setTimeScope: eTimeScope];
[leaderboardViewer setRange: NSMakeRange(1, 100)];
[leaderboardViewer setPlayerScope: (bFriendsOnly)?GKLeaderboardPlayerScopeFriendsOnly:GKLeaderboardPlayerScopeGlobal];
[leaderboardViewer loadScoresWithCompletionHandler:^(NSArray* scores, NSError* error)
{
if (error || !scores)
{
NSLog(#"GameKit Score retrieval Error:\n%#", error);
}
else
{
NSLog(#"GameKit Score retrieval complete, %d scores. Retrieving player names.", (u_int)scores.count);
GKScore* playerScore = leaderboardViewer.localPlayerScore;
u_int uIndex2;
for (uIndex2 = 0; uIndex2 < scores.count; ++uIndex2)
{
GKScore* score = [scores objectAtIndex:uIndex2];
//score.date is out of the requested range at this point.
xEntry.m_uRank = static_cast<u_int> (score.rank);
xEntry.m_lScore = score.value;
xEntry.m_uContext = score.context;
}
}
}];
}
I am doing all this in the sand box. Any help appreciated!
Related
Is it possible to spoof network providers just like it is possible to spoof locations in iOS?
I have an app that will get a user's ISO country code using Core Location, however I would like a fallback for when the user doesn't authorize location services for my app.
I have a function that is called in order to set a user's country according to their carrier; see below.
- (void)carrierBasedLocationSet {
if (DefaultCountryCode && ![DefaultCountryCode isEqualToString:#"Default"]) {
////NSLog(#"Skip Carrier Based Location set : DefaultCountryCode is [%#]", DefaultCountryCode);
return;
}
/***********************************
* Set country code based on Carrier
***********************************/
CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init];
Carrier = [networkInfo subscriberCellularProvider];
NSString *isoCountryCode = Carrier.isoCountryCode;
if (isoCountryCode == nil || isoCountryCode.length == 0) {
isoCountryCode = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
}
self.ISO_CountryCode = [isoCountryCode uppercaseString];
self.CarrierBased_ISO_Country = self.ISO_CountryCode;
}
This code works and produces US, which is where I am located. However, I want to test this out for different countries. Simply editing the product scheme to spoof a location in Australia, for example, does not give me back the AU country code and still gives me US.
Does anyone know if what I am trying to do is possible? Getting a user's location is essential to my application and I am unsure of another alternative.
In Contact apps there's group like "iCloud", "yahoo", "gmail". In swift, is it possible to fetch contact from gmail source only?
Tested code. Hope it will solve your problem...
func getAppropriateName(for container: CNContainer?) -> String? {
var name = ""
if (container?.name == "Card") || container?.name == nil {
name = "iCloud"
}
else if (container?.name == "Address Book") {
name = "Google"
}
else if (container?.name == "Contacts") {
name = "Yahoo"
}
else {
name = "Facebook"
}
return name
}
iCloud/yahoo/gmail etc are CNContainer. Gmail/iCloud is of type CNContainerTypeCardDAV. So first you need to fetch all contacts, and then filter the array based on the CNContainerType of that contact. But unfortunately, we cannot identify which CardDav is it, i.e iCloud/Gmail.
Please see more details here: How do we know which CNContainer represents iCloud?
You can achieve this by looking at Contacts framework runtime headers here: https://github.com/JaviSoto/iOS10-Runtime-Headers/tree/master/Frameworks/Contacts.framework
You can call them by performSelector message. It's a bit messy, but works.
Generally what you have to do is following:
CNContactStore* store = [CNContactStore new];
// fetch accounts that sync contacts with your device (array of CNAccount)
// since CNAccount class isn't available by default, we treat it as NSObject for our puproses
NSArray* accounts = [store performSelector:#selector(accountsMatchingPredicate:error:) withObject:nil withObject:nil];
// you can iterate through this array, I just use first one for this example
NSObject* account = [accounts firstObject];
// get identifier of the account for NSPredicate we use next
NSString* accountId = [account performSelector:#selector(identifier)];
// Display name of the account (aka Yahoo, Gmail etc.)
NSString* accountName = [account performSelector:#selector(_cnui_displayName)];
// NSPredicate that help us to get corresponding CNContainer
NSPredicate* containerPredicate = [[CNContainer class] performSelector:#selector(predicateForContainersInAccountWithIdentifier:) withObject:accountId];
// Fetching CNContainer
CNContainer* container = [[store containersMatchingPredicate:containerPredicate error:nil] firstObject];
After that it's all about general usage of CNContainers.
Hope it will help.
PS. It works on iOS 10, for future versions you should check for Contacts.framework runtime changes.
PPS. I didn't check on swift, but should work either.
Sorry for my english.
Good luck :)
Using the API I get 500 pictures, upload them asynchronously. Then I want to keep all these pictures in CoreData, but the application crashes due to insufficient memory.
When upload finished i call method createFromBlock
+(id)createFromBlock:(MRBlock *)block{
ManagedBlock *item = [ManagedBlock MR_createInContext:DefaultContext];
item.id = #(block.id);
item.name = block.name;
item.slidesInBlock = #(block.slidesInBlock);
item.sizeBlock = block.sizeBlock;
item.desc = block.desc;
item.imagePath = block.imagePath;
item.image = [MRUtils transformedValue:block.image];
item.price = block.price;
int i = 0;
ManagedItem *new = nil;
for (MRItem *lol in block.items){
NSLog(#"%i", i);
new = [ManagedItem createFromItem:lol];
new.block = item;
[item addItemsObject:new];
new = nil;
i++;
}
[DefaultContext MR_saveWithOptions:MRSaveSynchronously completion:nil];
return item;
}
In foreach block.items app is crashed. approximately after 150-160 positions.
If i comment new = [ManagedItem createFromItem:lol]; - app dont crash
+(id)createFromItem:(MRItem *)object{
ManagedItem *item = [ManagedItem MR_createInContext:DefaultContext];
item.id = #(object.id);
item.title = object.title;
item.detail = object.detail;
item.imagePath = object.imagePath;
item.image = [MRUtils transformedValue:object.image];
return item;
}
First, you should not load all your data and then save it. You should load in small batches, and save each batch.
However, for your specific example, I believe you can get away with turning each object into a fault after saving.
I have never used magical record, and have no idea if it will allow you do to contexty things without calling its methods. I looked at it once, but it hides way too many details for me... Unless you are doing the most basic things, Core Data can be quite complex, and I want to know everything that is going on with my core data code.
+(id)createFromBlock:(MRBlock *)block{
#autoreleasepool {
ManagedBlock *item = [ManagedBlock MR_createInContext:DefaultContext];
item.id = #(block.id);
item.name = block.name;
item.slidesInBlock = #(block.slidesInBlock);
item.sizeBlock = block.sizeBlock;
item.desc = block.desc;
item.imagePath = block.imagePath;
item.image = [MRUtils transformedValue:block.image];
item.price = block.price;
NSUInteger const batchSize = 10;
NSUInteger count = block.items.count;
NSUInteger index = 0;
while (index < count) {
#autoreleasepool {
NSMutableArray *newObjects = [NSMutableArray array];
for (NSUInteger batchIndex = 0;
index < count && batchIndex < batchSize;
++index, ++batchIndex) {
MRItem *lol = [block.items objectAtIndex:index];
ManagedItem *new = [ManagedItem createFromItem:lol];
new.block = item;
[item addItemsObject:new];
[newObjects addObject:new];
}
[DefaultContext MR_saveWithOptions:MRSaveSynchronously completion:nil];
for (NSManagedObject *object in newObjects) {
// Don't know if MagicalRecord will like this or not...
[DefaultContext refreshObject:object mergeChanges:NO];
}
}
}
return item;
}
Basically, that code processes 10 objects at a time. When that batch is done, the context is saved, and then turns those 10 objects into faults, releasing the memory they are holding. The auto-release-pool makes sure that any object created in the containing scope are released when the scope exits. If no other object holds a retain on the objects, then they are deallocated.
This is a key concept when dealing with large numbers or just large objects in Core Data. Also, understanding the #autoreleasepool is extremely important, though people who have never used MRR do not appreciate its benefit.
BTW, I just typed that into the editor, so don't expect it to compile and run with a simple copy/paste.
I'm doing some tutorials about how to send and receive data to and from Apple HealthKit app.
Part of the tutorial I'm doing is how to get the height from the healthKitStore.
I want to do the same thing but to retrieve the glucose data instead of the height, I did all the steps but got stuck at this piece of code:
var heightLocalizedString = self.kUnknownString;
self.height = mostRecentHeight as? HKQuantitySample;
// 3. Format the height to display it on the screen
if let meters = self.height?.quantity.doubleValueForUnit(HKUnit.meterUnit()) {
let heightFormatter = NSLengthFormatter()
heightFormatter.forPersonHeightUse = true;
heightLocalizedString = heightFormatter.stringFromMeters(meters);
}
As shown, the meters var is being assigned a double value from the meterUnit, and then creating a constant formatter to format the meters var and assign it to the pre-declared var (heightLocalizedString)
My question is, when I use this method for the glucose reading, I face a couple of issues, the first problem is I can't figure out what glucose units are available, the only one I get is
HKUnitMolarMassBloodGlucose
and when I use it an error appears says "'NSNumber' is not a subtype of 'HKUnit'", it's clear from the error this parameter is not a subtype of the HKUnit class.
Another issue is, as shown in the previous code there is a formatter for the height (NSLengthFormatter()), but I can't see a such formatter for the Glucose.
Actually I'm not sure if have to follow exactly the tutorial to get the Glucose data, but also I don't see another manner to do so.
Any ideas please?
Here is the code I'm using to retrieve the glucose data:
func updateGluco(){
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)
self.healthManager?.readMostRecentSample(sampleType, completion: {(mostRecentGluco, error) -> Void in
if (error != nil){
println("Error reading blood glucose from HealthKit store: \(error.localizedDescription)")
return;
}
var glucoLocalizedString = self.kUnknownString;
self.gluco = mostRecentGluco as? HKQuantitySample
println("\(self.gluco?.quantity.doubleValueForUnit(HKUnit.moleUnitWithMolarMass(HKUnitMolarMassBloodGlucose)))")
self.gluco?.quantity.doubleValueForUnit(HKUnit.moleUnitWithMolarMass(HKUnitMolarMassBloodGlucose))
if let mmol = self.gluco?.quantity.doubleValueForUnit(HKUnit.moleUnitWithMolarMass(HKUnitMolarMassBloodGlucose)) {
glucoLocalizedString = "\(mmol)"
} else {
println("error reading gluco data!")
}
dispatch_async(dispatch_get_main_queue(), {() -> Void in
self.glucoLabel.text = glucoLocalizedString})
})
}
The "mmol" variable returns a nil value.
I don't know if this relates to my problem, but I've just read an article said that Apple brought back blood glucose tracking in iOS 8.2 and my application deployment target is 8.1. (I can't upgrade the deployment target to the latest iOS despite that the XCode is updated to the last release!)
Looking in the header, blood glucose requires units of the type (Mass / Volume), which means you need to specify a compound unit with a Mass unit divided by a Volume unit:
HK_EXTERN NSString * const HKQuantityTypeIdentifierBloodGlucose NS_AVAILABLE_IOS(8_0); // Mass/Volume, Discrete
Typically, the units that people measure blood glucose in are mg/dL, and mmol/L. You can construct these by using:
HKUnit *mgPerdL = [HKUnit unitFromString:#"mg/dL"];
HKUnit *mmolPerL = [[HKUnit moleUnitWithMetricPrefix:HKMetricPrefixMilli molarMass:HKUnitMolarMassBloodGlucose] unitDividedByUnit:[HKUnit literUnit]];
Note that doubleValueForUnit: requires an HKUnit, not an NSNumber. See for more information
I figured out the solution
To get the actual value of the sample gulco, I have only to use this property: self.gluco?.quantity, that's exactly what I wanted before.
The default unit for the Glucose in HealthKit is mg\dL, in order to change the unit simply I extract the number value from the result and then divide it by 18.
Here is the code I'm using to retrieve the glucose data:
NSInteger limit = 0;
NSPredicate* predicate = [HKQuery predicateForSamplesWithStartDate:[NSDate date] endDate:[NSDate date] options:HKQueryOptionStrictStartDate];;
NSString *endKey = HKSampleSortIdentifierEndDate;
NSSortDescriptor *endDate = [NSSortDescriptor sortDescriptorWithKey: endKey ascending: NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType: [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose]
predicate: predicate
limit: limit
sortDescriptors: #[endDate]
resultsHandler:^(HKSampleQuery *query, NSArray* results, NSError *error)
{
dispatch_async(dispatch_get_main_queue(), ^{
// sends the data using HTTP
// Determine the Blood Glucose.
NSLog(#"BloodGlucose=%#",results);
if([results count]>0){
NSMutableArray *arrBGL=[NSMutableArray new];
for (HKQuantitySample *quantitySample in results) {
HKQuantity *quantity = [quantitySample quantity];
double bloodGlucosegmgPerdL = [quantity doubleValueForUnit:[HKUnit bloodGlucosegmgPerdLUnit]];
NSLog(#"%#",[NSString stringWithFormat:#"%.f g",bloodGlucosegmgPerdL]);
}
}
});
}];
[self.healthStore executeQuery:query];
// And Units :
#implementation HKUnit (HKManager)
+ (HKUnit *)heartBeatsPerMinuteUnit {
return [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]];
}
+ (HKUnit *)bloodGlucosegmgPerdLUnit{
return [[HKUnit gramUnit] unitDividedByUnit:[HKUnit literUnit]];
}
I have a weird error. I have a relationship between a call object and status object. the relationship name from the call object is statusForCall.
the data is imported into core data programmatically from JSON. it will not create a call without a status. the user has no way of changing the statusForCall to nil.
however after a period of time (seems to be days) when accessing calls via a fetch request into an array, when I access aCall.StatusForcall.statusID it starts returning nil. There is no way the code can have updated the statusForCall to nil.
Any ideas what might be causing this and where to start looking?
the cachename is nil throughout the application.
interestingly, if the user redownload the application the problem is solved. The code doesn't change and the data doesn't change (there is no migration - it's the same version) - but for some reason this ALWAYS fixes the issue.
i'm really struggling to know what to look at to get to the bottom of this.
code for setting the status below (edited down for conciseness). As i say the job initially has a status but seems to lose the relationship after a period od of time (i can't tell how long this is as the user is unreliable on this)
Call *theCall;
//see if we have the call on the device - if not create a new one, if we do update that one
BOOL contentIsInCoreData = NO;
for (Call *thisCall in existingCalls)
{
if ([thisCall.callID isEqualToNumber:[checkDic objectForKey:#"callID"]])
{
theCall=thisCall;
contentIsInCoreData = YES;
}
}
NSError * error = nil;
NSDateFormatter *dateFormat =[[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MMM-yyyy"];
if (contentIsInCoreData==NO)
{
[self.postDelegate updateGetJobsAndTimesProgress:[NSString stringWithFormat:#"Adding new job: %#",[checkDic objectForKey:#"woRef"]]];
//new call add bits that will not change
theCall = (Call*)[NSEntityDescription insertNewObjectForEntityForName:#"Call" inManagedObjectContext:self.managedObjectContext];
}
//set various properties from the JSON dictrionary – these have been cut down for the example code
theCall.woRef=[checkDic objectForKey:#"woRef"];
theCall.shortAddress=[checkDic objectForKey:#"shortAddress"];
theCall.address=[[checkDic objectForKey:#"address"] stringByReplacingOccurrencesOfString:#"\\n" withString:#"\n"];
theCall.postCode=[checkDic objectForKey:#"postCode"];
theCall.callID=[checkDic objectForKey:#"callID"];
//****THIS IS WHERE THE STATUS IS SET – PLEASE NOTE THE STATUS WILL EXIST IN CD ALREADY and the JSON WILL have a valid statusID
NSFetchRequest *request2=[NSFetchRequest fetchRequestWithEntityName:#"CallStatus"];
request2.predicate=[NSPredicate predicateWithFormat:#"callStatusID ==%#",[checkDic objectForKey:#"callStatusID"]];
error = nil;
NSArray * existingStatus = [self.managedObjectContext executeFetchRequest:request2 error:&error];
CallStatus *selectedStatus;
selectedStatus=[existingStatus firstObject];
theCall.statusForCall = selectedStatus;
error = nil;
if ([self.managedObjectContext save:&error])
{
//NSLog(#"saved job");
}
else
{
NSLog (#"***BROKEN GET JSON!!!\r\r%#",error);
}
turns out that a relationship property had been accidently changed - it was cascading a delete from the many end - which resulted in the one end being deleted.