inApp purchase... different products in 3 separate spots - ios

I have an in app purchase issue... I have an iPad app I'm upgrading. Previously used MKStoreKit. Started to upgrade to the latest version, but it turned out to be too complicated and too different to just "plug-in". Created a new "store kit" from scratch, which is much simpler and is built for iOS5+ and is ARC compliant.
Scenario: I have 3 spots to purchase. 1) is a full upgrade from a popover; 2) is an "icons" popover, which allows the user to purchase sets of icons for use in the app; 3) is a "backgrounds" popover, which allows the user to purchase sets of backgrounds for use in the app. All are in different views within the app.
Obviously, the available items all have to be available, but they also need to be segregated into the different popovers.
I have a "store manager" and a helper for the manager. The manager has a class method which includes all of the products listed in a set, and are accessed via a single "productIdentifier" variable in the manager. The actual items (images) are included in the app and are connected to an SQL database. I have played with additional sets, and have used subarrayWithRange. I have the correct number of sets showing in the popovers, although they are not showing the correct count, nor the correct images in each set.
I am looking for suggestions on how to separate these products into their unique "categories", while retaining the product identifier class of the manager, so that they feed back to the manager. I'm not looking for someone to solve it "for me"... I fully believe in solving one's own issues. I'm merely looking for a few suggestions in steering me in the correct path.
Should I create separate class methods for each type of purchase? Subsets within the main set?
Have any suggestions on how I could solve this issue? Any and all are appreciated.

I was able to solve this issue by using a refresh table method. I simply use different ranges for the 3 different spots.
-(void)reloadData
{
_products = nil;
[TJ_IAPHelper.sharedInstance requestProductsWithCompletionHandler:^(BOOL success, NSArray *products)
{
// by using subarrayWithRange, we limit what is displayed in the table
// those items that actually correspond to the background sets
// from the Apple store
_purchaseableBackgrounds = [products subarrayWithRange:NSMakeRange(0, 6)];
if (success)
{
_products = _purchaseableBackgrounds;
[self.tableView reloadData];
}
[self.refreshControl endRefreshing];
}];
}

Related

Firebase data structure for iOS app with profiles and basic gamification

I am currently building out my iOS 10 app with user profiles, which should hold certain achievements and experience points (similar system to StackOverflow). I have already built my Facebook Login, FIRAuth etc. At the moment I am thinking about the data structure of Firebase's DB. I have read through the Firebase Guide for flat data structures and some general Firebase data guides like the one from Ray Wenderlich.
This is what users can already do:
login with Facebook (...) and get picture, name etc. (already handled via FIRAuth)
visit a profile page, which shows their name and profile picture (already handled in a ProfileViewController)
This is my to-do list:
basic achievements via badges/titles similarly to StackExchange/StackOverflow e.g. user has done action XY 20 times => badge for 20 times for action XY; I was thinking of either:
a) having an array/list in each of the users' profile, which holds all of the gathered badges (as strings) OR
b) having a single boolean var for each achievement within the user profiles
a second achievements tracker, which tracks the general usage of the app - something like a levelling system in RPG games with experience points so just an Int value for every profile in terms of the DB
My question:
How would you combine the FIRAuth profiles with the database to hold the aforementioned badges (could be up-to 50-60 achievements) and the separate levels/xp points of the users?
Keep in mind that I am new to the modelling of JSON DBs. I don't want a solution in terms of code etc. for the functions in question - just a helping hand for the data structure of the user profiles.
I would also be looking into the extension of the user profiles in the future (messaging etc.) and I should be able to send notifications to all users who have certain levels/achievements in the future (and this makes me lean towards b)) - so too much nesting is out of the question already.
Using the structure below, you can retrieve a particular user's achievements by observing the children of the user's achievements path. However this only get's you the keys of the achievements.
Given you have a user with the uid 1j6Ft1BT30TFG403obvGfjOHE4th, for each child in user-achievements/1j6Ft1BT30TFG403obvGfjOHE4th, you can use the child keys to observe the value of each achievement at achievements/-KQpsPExLsKdnVHMliiP.
{
"users": {
"1j6Ft1BT30TFG403obvGfjOHE4th": {
"username": "john"
}
},
"user-achievements": {
"1j6Ft1BT30TFG403obvGfjOHE4th": {
"-KQpsPExLsKdnVHMliiP": true
}
},
"achievements": {
"-KQpsPExLsKdnVHMliiP": {
//
}
}
}

iOS: Design pattern for populating asynchronously fetched data

I am developing an app that fetches data from the web and displays it to the user. Assume that the data is reviews of a restaurant and one review is displayed on one view. The user can swipe left or right to go to the prev/next review. The data is fetched asynchronously (one thread for each review).
Here is the problem statement - Assume that 5 reviews have been fetched and the user is looking at the 3rd one currently. Now, the 6th review is fetched and I want to display it as the 4th review to the user (because the publish date of the 6th review is more recent than the 5th review). How should my model class inform the view controller?
I have considered some options -
Provide an array to the view controller and then send NSNotifications about new items to be inserted in-between the array at a specific index
Use an NSFetchedResultsController (this is a bit tricky because I am not using it with a table view controller)
View controller always asks for the next review to be displayed (from the model) and does not have a array of reviews with it
Are there any established design patterns that are employed in such a scenario? Other suggestions apart from the 3 above are welcome!
Just use an NSFetchedResultsController. When using NSIndexPaths just ignore the section. It's basically a glorified NSArray with free notifications.
Here's how I think I'd do it:
Make sure that the NSFetchRequest for your NSFetchedResultsController is sorted by publish date.
Handle NSFetchedResultsControllerDelegate methods.
When the NSFetchedResultsController updates, save the current object, reload the collection view, and then scroll to the saved object without any animation. This will appear to the user as if nothing happened to the current page.
While there is no perfect design pattern for every programming problem, the closest I can think of that relates to your problem is a combination of the Command and Observer patterns.
https://en.wikipedia.org/wiki/Command_pattern
The observer pattern is used in the NSNotification center.
While it's unclear as to why you'd want to skip a review, you could have two arrays to store them when fetched. The first holds all reviews that you have fetched. The second holds all reviews that are displayed.
Then you can get the last review in the fetched array, as if it were a stack. This way you always have the last one loaded displayed to the user.
I am confused why the order of display is different than the true order, ie why the 6th review comes before the 5th, but you asked about patterns to help.
Apart from MVC and observer, which are in the other answers and comments, I'd suggest using lazy loading with a virtual proxy. When reviews have been fetched, you can just display their proxy (eg with a "loading..." Message until they're fully in memory).
See more here: http://en.wikipedia.org/wiki/Proxy_pattern
I would recommend using the observing pattern to inform your controller than new data as been fetched. When receiving the signal, your view controller could update its array of "restaurant review" (either by adding the old one and reordering it according to some sort descriptors of your flavor or by querying the DAO directly).
Let's say you are fetching your data from internet and populating a CoreData entity with the results. Once you got your downloaded data you can populate your core data "Review" entity.
In order to "listen" at the change happening in core data, your controller should, in the viewDidLoad body, register itself as an observer for the NSManagedObjectContextDidSaveNotification.
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(updateInfo:) name:NSManagedObjectContextDidSaveNotification object:nil];
Then in your updateInfo, you can get the changes
- (void) updateInfo:(NSNotification *)notification
{
self.reviews = [self.managedObjectContext performRequest:myFetchRequest error:nil];
}

Can you dynamically load modules in Angular?

I have an angular app with two distinct sections to it. Each section belongs to a type of user. There are some commonalities but for the most part rather different. For example:
/receptionist
- View Diary
- Answer Phone
- Make Tea
/ceo
- View Diary
- Announce Earnings
- Strategize
Both the ceo and the receptionist need the functionality for viewing the diary. I was thinking wanting to change the modules loaded (and the routing) depending on who logged in.
if (user.type === 'receptionist') {
app = angular.module('receptionistApp', ['diary', 'phone', 'tea']);
else {
app = angular.module('ceoApp', ['diary', 'earning', 'strategy']);
}
I am wanting to do this because there is some overlap, but not a lot. And each app is actually quite big.
I am not wanting to achieve anything security wise here. Each app will have different types of users and roles. Those will be secured through WebApi. I just wanting to avoid loading all of the modules when 45% of them will be of no interest to the other app.
Yes.
Also, (and you probably won't need this) you can load modules even more dynamically, and initialize AngularJS yourself instead of having it load instantly. You can remove the ng-app directive, and do a manual initialization.
angular.element(document).ready(function() {
angular.module('myApp', []);
angular.bootstrap(document, ['myApp']);
});

How do I persist graphic data in iPhone/iPad?

I am making an iPad app where the user can create graphic content with images and text. I am storing this in memory in an array of custom UIView subclasses. Each of these view subclasses can have any number of subviews containing images or text.
Now I need to save these in the device. As I explore, there seem to be many ways to do this and would like to know what would be the best for this case.
It looks like you are asking for the architectural design of what will end up being a Drawing app. This means that best it's really dependent on you specific use-cases, and cannot be answered completely unless you provide a quite detailed list of requirement.
But in general, I could try to give you some general tips that will have anyway to be integrated with you own specific nitty-gritty implementation.
This description will make some assumptions regarding the basic use cases that an app like this may need:
The user can create an image using multiple tools to achieve the result. These can be anything, from a brush to a textfield and so on
The information regarding which tools have been used to create the picture and how this tools have influenced the picture current look, can be saved in order to allow the user to later on edit the picture
Said this, the main problem is: how to store your drawing state in order to recover it later?
There are indeed many ways to achieve it, but I believe 2 of them are what would be considered "clean and famous enough".
NSKeyedArchiver
This wouldn't be my favourite (difficult to maintain), but if you have to deal with UIView, it's probably gonna be the quickest.
The NSKeyedArchiver is
.. a concrete subclass of NSCoder, provides a way to encode objects
(and scalar values) into an architecture-independent format that can
be stored in a file.
It implements the Memento design pattern and It's the same pattern described in Pro Objective-C Design Patterns, that, incidentally, presents a case study that has many of the most important use-cases matching yours:
A drawing pad allows scribbling with the user’s finger.
[...]
It allows the user to save a scribble.
It allows the user to open a saved scribble.
[...]
It's an app for having a drawing pad, where you can draw lines with your finger.
Yours looks like a simplified version of this, with images and texts instead of the scribble.
So, what's the pro, in this specific case, of using the NSKeyedArchiver? The UIView already implements the NSCoding protocol, the one needed to archive the object. So, for most of the information you need to store (coordinates, frame size, background color ...), you don't have to do anything but... archiving the object.
For any additional attribute on top of the UIView (for instance: the local path of your image, because archiving an UIImageView is really expensive), you can take a look at this article that explains with proper detail what you have to do in order to take advantage of the NSKeyedArchiver to store your object states.
This all boils down to:
implement the NSCoding protocol for each of the tools your drawing app is gonna provide
keep track of the subviews that the user create (images, text...)
when the user hit "save", loop through them, create an archive, and store them to a sensful path. The first component of the path could be the name of the Drawing, the second one the name of the tool and the third an id for each time the tool has been used. Like
// A mountain image
/<path to you Document dir>/Mountains/Image/1
// A sun
/<path to you Document dir>/Mountains/Image/2
// The text "Mountain is awesome"
/<path to you Document dir>/Mountains/Text/1
Then of course you will have to save the list of Drawing names somewhere, either in a plist file or in a NSUserDefault, so to be able to show them to the user in case they want to restore them for editing.
Core data
This is probably the cleanest and more maintainable way to store you object states, but is gonna be a bit tough and cumbersome, in particular if it's the first time you use core data. I'm not gonna dig into Core Data, but I can give you some guidelines of the whole procedure. Basically:
You create a db schema that represents each of the tools your are gonna let the user use. Like: a table for Image, a table for Text and so on
On each table you put the attributes you need to remember (location, text color for "Text", image URL for "Image" and so on)
You create a table for the Drawing that the user create, with a 1-many relationship to the tool tables. This relations represents the object shown in the drawing.
Initialize you drawing canvas and each component according to what's stored in the db
Every time the user hit "save", create or update the proper db tables in order to reflect the current drawing configuration in the storage.
One of the advantages of this approach is that, if one day you want to change a tool component property or add new ones, you can take advantage of schema migrations in order to deliver backward compatibilities with new updates. So the users will still be able to use their old drawings.
And so on and so forth...
These are two of the zilions of possibilities. You could use also use:
NSUSerDefault to store states, that I suggest to avoid. It's gonna be really hard to maintain
Mix of the two aforementioned techniques
If you plan to deliver >= iOS6 only support, you can check this
etc
The two I described are just what I feel are the usual and most discussed way of doing this. You find them in books, tutorials and they let you quite a lot of flexibility for anything you have to do.
If you need more explanatory links, let me know.
As I mentioned in a comment, you might want to look into iOS's state preservation API's. However, if you want to build your own system to do this it'd be pretty simple using some clever categories and dictionaries. Then you can serialize/deserialize your dictionaries using NSKeyedArchiver and NSKeyedUnarchiver.
eg:
#interface UIButton (MyAppCategory)
- (NSDictionary *)viewProperties;
- (void)configureFromProperties: (NSDictionary *) properties;
#end
#implementation UIButton (MyAppCategory)
- (NSDictionary *)viewProperties {
return #{ #"class" : NSStringFromClass([self class]),
#"frame" : [NSValue valueWithRect:self.frame],
#"titleLabelText" : self.titleLabel.text,
// etc...
};
}
- (void)configureFromProperties: (NSDictionary *) properties {
NSValue * value = properties[#"frame"];
if ([value isKindOfClass:[NSValue class]]) {
self.frame = value.rectValue;
}
NSSString * titleLabelText = properties[#"titleLabelText"];
if ([titleLabelText isKindOfClass:[NSString class]]) {
self.titleLabel.text = titleLabelText;
}
}
#end
// replicate the above pattern for other view objects you need to support
#implementation MyViewFactory
- (UIView)recreateViewFromProperties: (NSDictionary *) properties {
NSString * className = properties[#"class"];
if ([className isKindOfClass:[NSString class]]) {
Class viewClass = NSClassFromString(className);
id viewObject = [[viewClass alloc] init];
if ([viewObject respondsToSelector:#selector(configureFromProperties:)]]) {
[viewObject performSelector:#selector(configureFromProperties:) withObject:properties];
return viewObject;
}
}
return nil;
}
// exercise for the reader: iterate your views and use the viewProperties: method to collect your views' configuration info...
#end
If you want to allow for future session editing and loading etc. I would suggest designing a data structure and create a core data model out of it.
Some structure holding the session metadata e.g. sessionID, creationDate, dictionary of key:imageName value:imageFrame (CGRect wrapped in NSValue, use setObjectForKey).
Loading images for the session would work by calling the keys into an array using e.g.[sessionImageDictionary allKeys], iterating through the keys and asynchronously (NSOperationQueue with maxConcurrentOperationCount) loading the image at some Macro path to e.g. the library directory, and appending the key, which is the imageName.
In the same iteration you can set its frame by calling [sessionImageDictionary valueForKey:[arrayOfKeys objectAtIndex:currentIteration]; Converting the previously stored NSValue back to CGRect.
The datastructure all depends on the amount of features you want, but the good thing is it allows for expansion and with core data as the backing store, you could do things like sync between devices, enable multiple sessions for loading and saving like a "My projects" feature. It will help if lets say the user builds up a library of images (all stored in your apps library directory) and then the user uses the same image in the same session or in multiple sessions, only one copy of the image needs to exist, with zero duplicate write outs to disk and the core data object will have the filename stored in the session.
The most important part would be building a correct Core-Data model and writing an extractor that can accept these custom subclasses, strip out the data to create, populate and save an NSManagedObject to the persistent store.
Your best option is to use UIDocument with NSFileWrapper folder. Then you can store all your files in one folder which is saved automatically when the contents change.
Refer to:http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/DocumentBasedAppPGiOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011149-CH1-SW1

iCloud + CoreData - how to avoid pre-filled data duplication?

I have a problem with an iCloud shoebox application and hope some-one can help me (I've spent many hours fighting it in vain).
The App: - A simple library style application - containing set of categories (Cat1 .. CatN) each containing items (Item1...ItemM). I used Apple's iPhoneCoreDataRecipes to set up iCloud CoreData stack.
Everything works almost perfect with iCloud except - there should be a number of pre-filled empty categories the user can start using once he has opened the app for the first time (he can also be offline at that time). And here's the devil.
Here's what I do - Once my persistentStoreCoordinator is setup I send notification
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName: #"RefetchAllDatabaseData"
object: self
userInfo: nil];
});
which is received by my MasterViewController. When the notification is received MasterViewController checks the number of categories in the storage. If the number of available categories equals 0 - the pre-filled categories are inserted.
FYI - I use NSMergeByPropertyObjectTrumpMergePolicy for my ManagedObjectContext
The problem: This works well for the 1st device. But for the 2nd device the default categories from iCloud are often received later than persistentStoreCoordinator has been setup (and default categories inserted by 2nd device). In the end I have 2 sets of categories with the same names on both devices.
Any ideas how this can be solved?
Tried solutions: I tried 2 strategies to solve this. Both start in the same way. After I call
[moc mergeChangesFromContextDidSaveNotification: note];
I call
[self materializeKeysWithUserInfo: note.userInfo forContext: moc];
many thanks to Jose Ines Cantu Arrambide from https://devforums.apple.com/thread/126670?start=400&tstart=0 for his reference code - In essence
materializeKeysWithUserInfo:forContext:
get managedObjectIds from note.userInfo and retrieves corresponding objects from ManagedObjectContext putting them into a dictionary.
Strategy 1:
All my categories have creation time-stamps.
On insert from iCloud, get pairs of categories with same name if any
Select older duplicate categories
move their items to newer duplicate categories
delete older duplicate categories
These strategy effectively removes duplicates on both devices even before they appear in the UI BUT
1) the items from 1st device are getting lost on the 2nd device - when they come to the 2nd device their parent category is absent and their category field equal nil so I don't know where to put them.
2) in some short time the items that got lost on the 2nd device are also getting lost on the first due to conflicts.
3) some items originating from the 2nd device are also lost due to conflicts.
I tried to prefer older categories against newer but it didn't give any effect
Strategy 2:
All my categories have creation time-stamps.
All categories have obsolete boolean field set to NO on creation
On insert from iCloud, get pairs of categories with same name if any
Select older duplicate categories
move their items to newer duplicate categories
mark older categories with obsolete = YES
These strategy almost always removes duplicates on both devices even before they appear in the UI BUT
the majority (or all) of the items from both devices are getting lost due to a bunch of conflicts on categories and items.
Some concluding thoughts:
It looks like these strategy doesn't work as we start simultaneously changing content ob both devices whereas iCloud is not suitable for such pattern.
In my tests I had both devices running simultaneously. I cannot neglect a case when a happy user who has just bought his 2nd iDevice installs my app on the 2nd device (with tre 1st device running the app) and get lost all his items in the matter of minutes.
Any ideas how this situation can be solved? Do you think iCloud + CoreData is ready for production?
Strategy 3
I've tried to put a pre-filled database (copying it from bundle) to the appropriate path. It worked out partly - I have no more pre-filled categories duplication BUT the items added to the pre-filled categories do not synchronize across the devices.
iCloud is not aware of the data that exists in the database prior to iCloud setup - my 2nd device receives items, inserted on the 1st device in pre-filled categories, with category = nil.
Items in additionally categories (as well as categories themselves) inserted into the storage after iCloud setup do synchronize properly.
Strategy 1 with some modifications appeared to be a working solutions (with some flaws though).
Legend:
1st device - started online without any content in the iCloud
2nd device - started later than first and OFFLINE. Then it gets online after some items added
So here's the updated strategy:
All my categories have creation time-stamps
The categories cannot be renamed (only added or deleted - this is crucial)
All my items have a string categoryName field which gets its value upon item creation and updated whenever item is moved to a different category - this redundant information helps to achieve success;
On insertion of new Categories:
On insert from iCloud, I get pairs of categories with same name if any
Select newer duplicate categories (they will most probably have less items than old ones so we will have less dance in iCloud)
Move their items if any to older duplicate categories
Delete newer duplicate categories
On insertion of new Items - if the item belongs to deleted category:
CoreData tries to merge it and fails as there's no parent category any more (lots of errors in console). It promisses to insert it later.
After some short time it does merge and insert the item into storage but with NIL category
Here we pick our item up, find out it's parent category from categoryName and put it to the correct category
VOILA! - no duplicates & everybody happy
A couple of notes:
I get a dance of items belonging to the 2nd device (those that will come with nil category to the 1st device) on both devices. After a couple of minutes everything is stabilized
No items is lost though
The dance happens only on the first iCloud sync of the 2nd (or any other subsequent device)
If the 2nd device is started online for the first time the chance that duplicate categories case appears is about 25% only - tested on 3G connection - so dance should not affect the majority of users

Resources