Dumpling is a framework using which you can publish issues/articles as apps easily by connecting with the Magnet API or using the zip files created by Linechef.
The framework lets you get an issue's details in 2 ways
- Parse through a zip file for an issue (in the application bundle)
- Get an issue id (or an individual article's id) and retrieve the information from Magnet
Dumpling has been written with Swift 1.2 and is very easy to use and integrate. This short guide is to help you understand the usage of Dumpling in your projects and the methods available to you with the framework.
-
The Dumpling framework looks for AFNetworking libraries for making calls to the Magnet API. AFNetworking is included as a part of the framework
-
Dumpling uses Realm as the database. The Realm library and headers are included directly inside the framework so publishers do not have to include it separately
-
Dumpling uses ZipArchive for unarchiving zip files. The ZipArchive .a and header file are included in the project
This is the main class and the starting point of Dumpling
###Properties
- issueHandler
IssueHandler
An instance of IssueHandler class - this uses the same folder and client key as the VolumeHandler
###Methods
-
init(folder: NSString) Initializes the VolumeHander with the given folder. This is where the database and assets will be saved
NOTE: The initializer requires a Client key. If you wish to pass a client key to the initializer, use one of the below initializers. This method will look for a
String
with the keyClientKey
in your project's Info.plist
<key>ClientKey</key>
<string>Your Key Here</string>
-
init(clientkey: NSString) Initializes the VolumeHandler with the Documents directory. This is where the database and assets will be saved. The key is used for making calls to the Magnet API
-
init(folder: NSString, clientkey: NSString) Initializes the VolumeHandler with a custom directory. This is where the database and assets will be saved. The key is your Client API key provided by 29.io
-
getCurrentSchemaVersion()
returns UInt
Find current schema version -
addVolumeFromAPI(globalId: NSString) The method uses the global id of a volume, gets its content from the Magnet API and adds it to the database
-
addVolumeFor(appleId: String) The method uses the SKU/Apple id of a volume, gets its content from the Magnet API and adds it to the database
-
getVolume(volumeId: String)
returns Volume or nil
Get volume details from database for a specific global id -
listVolumes() The method is for testing only. It prints the available volumes for a client api key
-
addAllVolumes(page: Int) This method gets all the volumes for a given client key and adds them to the database. Current limit is set to 20. Pagination starts at 0
-
addAllVolumes() This method gets the last 20 volumes for a given client key and adds them to the database
This class handles adding issues to the database. If using a VolumeHandler, you do not need to initialize IssueHandler and can use the instance provided by VolumeHandler
###Methods
-
init(folder: NSString) Initializes the IssueHandler with the given folder. This is where the database and assets will be saved
NOTE: The initializer requires a Client key. If you wish to pass a client key to the initializer, use one of the below initializers. This method will look for a
String
with the keyClientKey
in your project's Info.plist
<key>ClientKey</key>
<string>Your Key Here</string>
-
init(clientkey: NSString) Initializes the IssueHandler with the Documents directory. This is where the database and assets will be saved. The key is used for making calls to the Magnet API
-
init(folder: NSString, clientkey: NSString) Initializes the IssueHandler with a custom directory. This is where the database and assets will be saved. The key is your Client API key provided by 29.io
-
addIssueZip(appleId: NSString) The method uses an Apple id, gets a zip file from the project Bundle with the name appleId.zip, extracts its contents and adds the issue, articles and assets to the database
-
addIssueFromAPI(issueId: String, volumeId: String) The method uses the global id of an issue, gets its content from the Magnet API and adds it to the database. The volume id is optional and if provided, saves the global id of the volume this issue is associated with
-
listIssues() The method is for testing only. It prints the available issues for a client api key
-
searchIssueFor(appleId: String)
returns Issue or nil
The method searches for an issue with a specific Apple ID. If the issue is not available in the database, the issue will be downloaded from the Magnet API and added to the DB -
getIssue(issueId: NSString)
returns Issue or nil
Get issue details from database for a specific global id -
addIssueOnNewsstand(issueId: String) Add issue on Newsstand
-
getActiveDownloads()
returns NSArray
Get issue ids whose download not complete yet
This class created for managing independent articles
###Methods
-
init(folder: NSString) Initializes the ArticleHandler with the given folder. This is where the database and assets will be saved
NOTE: The initializer requires a Client key. If you wish to pass a client key to the initializer, use one of the below initializers. This method will look for a
String
with the keyClientKey
in your project's Info.plist
<key>ClientKey</key>
<string>Your Key Here</string>
-
init(clientkey: NSString) Initializes the ArticleHandler with the Documents directory. This is where the database and assets will be saved. The key is used for making calls to the Magnet API
-
init(folder: NSString, clientkey: NSString) Initializes the ArticleHandler with a custom directory. This is where the database and assets will be saved. The key is your Client API key provided by 29.io
-
addArticleFromAPI(globalId: String) The method uses the global id of an article, gets its content from the Magnet API and adds it to the database as an independent article
-
addArticleWith(appleId: String) The method uses an SKU/Apple id of an article, gets its content from the Magnet API and adds it to the database
-
addAllArticles(page: Int) Gets all the articles for a client key from the API and adds it to the database. Current limit is set to 20. Pagination starts at 0
-
getAllArticles(page: Int, count: Int)
returns Array<Article> or nil
The method returns a paginated list of independent articles from the database. If you need all articles, specify count as 0. The page index starts at 0
Realm object for Volumes. Also has methods for directly dealing with volumes
###Properties
-
globalId
String
Global id of a volume - this is unique for each volume -
title
String
Title of the volume -
subtitle
String
Subtitle of the volume -
volumeDesc
String
Description of the volume -
assetFolder
String
Folder saving all the assets for the volume -
coverImageId
String
Global id of the asset which is the cover image of the volume -
publisher
String
Publisher of the volume -
publishedDate
NSDate
Published date for the volume -
releaseDate
String
Release date for the volume -
keywords
String
Keywords for the volume (comma separated string) -
metadata
String
Custom metadata of the volume (JSON string - dictionary) -
published
Bool
Whether the volume is published or not
###Class methods (public)
-
deleteVolume(globalId: NSString) This method uses the global id for a volume and deletes it from the database. All the volume's issues articles, assets, issue assets and article assets are deleted from the database and the file system
-
getNewestVolume()
returns Volume or nil
This method returns the Volume object for the most recent volume in the database (sorted by publish date) -
searchVolumesWith(keywords: [String])
returns Array<Volume> or nil
This method accepts an array of keywords and returns all volumes with specific keywords -
getVolumes()
returns Array<Volume> or nil
This method returns all volumes -
getVolume(volumeId: String)
returns Volume or nil
This method inputs the global id of a volume and returns the Volume object
###Instance methods (public)
-
getValue(key:)
returns AnyObject/id or nil
This method returns the value for a specific key from the custom metadata of the volume -
saveVolume() This method saves a volume back to the database
-
getNewerVolumes()
returns Array<Issue>
This method returns all volumes whose publish date is newer than the published date of current volume -
getOlderVolumes()
returns Array<Issue>
This method returns all volumes whose publish date is older than the published date of current volume
Realm object for Issues. Also has methods for directly dealing with issues
###Properties
-
globalId
String
Global id of an issue - this is unique for each issue -
appleId
String
SKU or Apple Id for an issue -
title
String
Title of the issue -
issueDesc
String
Description of the issue -
assetFolder
String
Folder saving all the assets for the issue -
coverImageId
String
Global id of the asset which is the cover image of the issue -
iconImageURL
String
File URL for the icon image -
publishedDate
NSDate
Published date for the issue -
lastUpdateDate
String
Last updated date for the issue -
displayDate
String
Display date for an issue -
metadata
String
Custom metadata of the issue (JSON string - dictionary) -
volumeId
String
Global id of the volume to which the issue belongs (can be blank if this is an independent issue)
###Class methods (public)
-
deleteIssue(appleId: NSString) This method uses the SKU/Apple id for an issue and deletes it from the database. All the issue's articles, assets, article assets are deleted from the database and the file system
-
getNewestIssue()
returns Issue or nil
This method returns the Issue object for the most recent issue in the database (sorted by publish date) -
getIssueFor(appleId: String)
returns Issue or nil
This method takes in an SKU/Apple id and returns the Issue object associated with it (or nil if not found in the database) -
getIssues(volumeId: String)
returns Array<Issue> or nil
This method takes in the global id of a volume and returns all issues associated with it (or nil if not found in the database) -
getIssue(issueId: String)
returns Issue or nil
This method takes in the global id of an issue and returns the Issue object associated with it (or nil if not found in the database)
###Instance methods (public)
-
getValue(key:)
returns AnyObject/id or nil
This method returns the value for a specific key from the custom metadata of the issue -
getOlderIssues()
returns Array<Issue>
This method returns all issues whose publish date is older than the published date of current issue -
getNewerIssues()
returns Array<Issue>
This method returns all issues whose publish date is newer than the published date of current issue -
saveIssue() This method saves an issue back to the database
Realm object for Articles. Also has methods for directly dealing with articles
###Properties
-
globalId
String
Global id of an article - this is unique for each article -
title
String
Article title -
articleDesc
String
Article description -
slug
String
Article slug -
dek
String
Article dek -
body
String
Article content -
permalink
String
Permanent link to the article -
url
String
Article URL -
sourceURL
String
URL to the article's source -
authorName
String
Article author's name -
authorURL
String
Link to the article author's profile -
section
String
Section under which the article falls -
articleType
String
Type of article -
keywords
String
Keywords which the article falls under (comma separated string) -
commentary
String
Article commentary -
date
NSDate
Article published date -
metadata
String
Article metadata (JSON string - dictionary) -
placement
Integer
Placement of the article in an issue -
mainImageURL
String
URL for the article's feature image -
thumbImageURL
String
URL for the article's thumbnail image -
isPublished
BOOL
Whether the article is published or in draft -
isFeatured
BOOL
Whether the article is featured for the given issue or not -
issueId
String
Global id for the issue the article belongs to. This can be blank for independent articles -
appleId
String
Apple id/SKU fir the article. Will be blank if the article cannot be sold individually
###Class methods (public)
-
deleteArticlesFor(issueId: NSString) This method accepts an issue's global id and deletes all articles from the database which belong to that issue
-
getArticlesFor(issueId: String?, type: String?, excludeType: String?, count: Int, page: Int)
returns Array<Article>
This method accepts an issue's global id, type of article to be found and type of article to be excluded. It retrieves all articles which meet these conditions and returns them in an array. All parameters are optional. At least one of the parameters is needed when making this call. The parameters follow AND conditions -
issueId: NSString?, key: String, value: String, count: Int, page: Int)
returns Array<Article>
This method accepts an issue's global id, a key and a value to be searched. It retrieves all articles which belong to the issue (or any regardless of issue if nil) and have a specific value for the given key. The key and value params are necessary. If you do not want paginated results, pass count as 0 -
searchArticlesWith(keywords: [String], issueId: String?)
returns Array<Article>
This method accepts an issue's global id and returns all articles for an issue (or if nil, all issues) with specific keywords -
getFeaturedArticlesFor(issueId: NSString)
returns Array<Article>
This method accepts an issue's global id and returns all articles for the issue which are featured -
getArticle(articleId: String?, appleId: String?)
returns Article or nil
This method accepts an article's global id or SKU/Apple id and returns the Article object or nil if not found. At least one of the params should be non-empty -
setAssetPattern(newPattern: String) This method accepts a regular expression which should be used to identify placeholders for assets in an article body. The default asset pattern is
<!-- \\[ASSET: .+\\] -->
###Instance methods (public)
-
getValue(key:)
returns AnyObject/id or nil
This method returns the value for a specific key from the custom metadata of the article -
replacePatternsWithAssets()
returns NSString
This method replaces the asset placeholders in the body of the Article with actual assets using HTML codes Images are replaced with tags, Audio with tag and videos with -
getNewerArticles()
returns Array<Article>
This method returns all articles for an issue whose publish date is newer than the published date of current article -
getOlderArticles()
returns Array<Article>
This method returns all articles for an issue whose publish date is before the published date of current article -
saveArticle() This method can be called on an Article object to save it back to the database
Realm object for Assets. Also has methods for directly dealing with assets
###Properties
-
globalId
String
Global id of an asset - this is unique for each asset -
caption
String
Caption for the asset - used in the final rendered HTML -
source
String
Source attribution for the asset -
squareURL
String
File URL for the asset's square thumbnail -
originalURL
String
File URL for the original asset -
mainPortraitURL
String
File URL for the portrait image of the asset -
mainLandscapeURL
String
File URL for the landscape image of the asset -
iconURL
String
File URL for the icon image -
metadata
String
Custom metadata for the asset -
type
String
Asset type. Defaults to a photo. Can be image, sound, video or custom -
placement
Integer
Placement of an asset for an article or issue -
articleId
String
Global id for the article with which the asset is associated. Can be blank if this is an issue's asset -
issue
Issue
Issue object for the issue with which the asset is associated. Can be a default Issue object if the asset is for an independent article -
volumeId
String
Global id of the volume with which the asset is associated. Can be blank if this is an issue or article asset
###Class methods (public)
-
deleteAsset(assetId: NSString) This method accepts the global id of an asset and deletes it from the database. The file for the asset is also deleted
-
getFirstAssetFor(issueId: String, articleId: String, volumeId: String)
returns Asset
This method uses the global id for a volume and/or issue and/or article and returns its first image asset (i.e. placement = 1, type = image) -
getNumberOfAssetsFor(issueId: String, articleId: String, volumeId: String?)
returns UInt
This method uses the global id for a volume and/or issue and/or article and returns the number of assets it has -
getAssetsFor(issueId: String, articleId: String, volumeId: String?, type: String?)
returns Array<Asset>
This method uses the global id for a volume and/or issue and/or article and the assets in an array. It takes in an optional type parameter. If specified, only assets of that type will be returned -
getAsset(assetId: String)
returns Asset or nil
This method inputs the global id of an asset and returns the Asset object -
getPlaylistFor(issueId: String, articleId: String)
returns Array<Asset>
This method inputs the global id of an issue and/or article and returns all sound assets for it in an array
###Instance methods (public)
-
saveAsset() This method lets you save an Asset object back to the database in case some changes are made to it
-
getAssetPath()
returns String
This method returns the path of the asset file (using theoriginalURL
property) for the current object. Use this method to access the file path instead of directly usingoriginalURL
Class which helps store and retrieve user reading status
###Class methods (public)
-
saveVolume(volumeId: String) This method accepts the global id of a volume and saves it to user defaults. If nil, will remove saved volume (used when switching between volumes or you want to remove saved state)
-
saveIssue(issueId: String) This method accepts the global id of an issue and saves it to user defaults. If nil, will remove saved issue (used when switching between issues or you want to remove saved state)
-
saveArticle(articleId: String) This method accepts the global id of an article and saves it to user defaults. If nil, will remove saved article (used when switching between articles or user is not viewing any article as of now)
-
saveAsset(assetId: String) This method accepts the global id of an asset and saves it to user defaults. If nil, will remove saved asset (used when moving away from viewing an asset or if you wish to remove saved state)
-
saveReadingPercentageFor(articleId: String, readingPercentage: Float) This method saves the reading status for given article in percentage. The percentage value is calculated as current y position of article content (scroll position) / height of article container
-
retrieveCurrentVolume()
returns String
returns the global id of the current volume -
retrieveCurrentIssue()
returns String
returns the global id of the current issue -
retrieveCurrentArticle()
returns String
returns the global id of the current article -
retrieveCurrentAsset()
returns String
returns the global id of the current active asset -
getReadingPercentageFor(articleId: String)
returns Float
retrieves last saved reading percentage for given article. To use this value, multiply the return value with the article content height and set it as the content offset for the view holding the content -
getDictionaryForCloud()
returns Dictionary<String, AnyObject>
This method returns a dictionary containing saved values for current issue, article, asset and reading percentage for an article. The keys used are CurrentVolume, CurrentIssue, CurrentArticle, CurrentAsset and ArticlePercent-<article_id_here> -
saveDictionaryToUserDefaults(savedValues: Dictionary<String, AnyObject>) saves specific values from a key for current issue, current article, current asset and article reading percentage back to user defaults (for using while app is active). The keys used should be the same as above
Dumpling issues notifications at various stages of a download. This information can be used by apps to update progress and app data. The notifications issued by Dumpling include
-
issueDownloadComplete This notification is fired when an issue's data is downloaded from Magnet and saved back to the database. The articles and assets might still be downloading at this stage
-
articlesDownloadComplete This notification is fired when the data for all articles in an issue are downloaded and saved back to the database. It is not necessary that all assets have been downloaded at this point
-
downloadComplete This notification is fired when everthing related to a volume or an independent issue or article is downloaded (issues, articles, assets)
-
allDownloadsComplete This notification is fired when all articles/volumes added via the respective ArticleHandler/VolumeHandler methods are downloaded completely
//For zipped files
let appleId = "org.bomb.mag.issue.20150101"
var docPaths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
var docsDir: NSString = docPaths[0] as! NSString
var volumeHandler = VolumeHandler(folder: docsDir)
//This nil check is needed. The initializer might return a nil if it doesn't find
//"ClientKey" in Info.plist
if volumeHandler != nil {
//Issue from a ZIP
volumeHandler.issueHandler.addIssueZip(appleId)
//For volumes from API
volumeHandler.addVolumeFromAPI("555a27de352c7d6d5b888c3e") //The volume's global id
}
-
If you wish to publish your issue on Newsstand, make sure your project's plist has the UINewsstandApp key with a true value. You will also need to add NewsstandKit.framework to your project
-
In order to use iCloud for syncing reading status, add CloudKit.framework to your project, turn on iCloud in the target's Capabilities section for Key-value storage. The sample project (DumplingDemo) uses the default container for storing and retrieving values. If you wish to use a custom container, the code will change accordingly
-
If you turn on App Groups for multiple projects and instantiate IssueHandler, VolumeHandler and ArticleHandler with the appropriate shared folder, you can read the data across multiple apps. To do this, you will need an app id with App Groups enabled and set the app group in Capabilities for all projects sharing the data
#Purchases
This section lists all the classes available for handling in-app purchases and purchases made through the web
Realm object for Purchases
###Properties
-
appleId
String
Apple id/SKU of the purchase made - article, issue or volume -
globalId
String
Global id of the purchase made - article, issue or volume -
mode
String
Mode of purchase - Web (could be any - Stripe or any other), IAP -
type
String
Type of purchase - article, issue or volume. Used to determine what type of a record this is -
purchaseDate
NSDate
Purchase date -
expirationDate
String
Expiration date - only used for subscriptions -
userIdentity
String
Identity used for syncing web purchases (as of now, an email)
This is a class for handling purchases (retrieving and adding)
###Methods
-
init(folder: NSString) Initializes the PurchaseHandler with the given folder. This is where the database exists
NOTE: The initializer requires a Client key. If you wish to pass a client key to the initializer, use one of the below initializers. This method will look for a
String
with the keyClientKey
in your project's Info.plist
<key>ClientKey</key>
<string>Your Key Here</string>
-
init(clientkey: NSString) Initializes the PurchaseHandler with the Documents directory. This is where the database exists
-
init(folder: NSString, clientkey: NSString) Initializes the PurchaseHandler with a custom directory. This is where the database exists. The key is your Client API key provided by 29.io
NOTE: The Client key is not used as of now by the PurchaseHandler but might be in the future.
-
addPurchase(purchase: Purchase) This method adds a purchase to the database
-
getPurchases(userId: String?)
returns Array<Purchase> or nil
The method returns an array of purchases made on this device - for a specific user or all purchases (if userId is nil) -
getPurchasesFor(key: String, value: String, userId: String?)
returns Array<Purchase> or nil
This method accepts a Purchase's key and value for purchase search. It retrieves all purchases which meet these conditions and returns them in an array. The key and value are needed. userId is optional -
getPurchase(purchase: Purchase)
returns Volume, Issue, Article or nil
This method accepts a purchase object and returns the associated article, issue or volume object
This is a protocol for managing access control to various purchases. The client app will implement this
###Methods
-
purchaseItem(object: AnyObject) This method makes an in-app purchase and calls the Subscriber API to verify if the purchase receipt is valid or not. If valid, the content is unlocked and made available to the user
-
restorePurchases(userId: AnyObject?) This method restores all in-app purchases for the current logged in user. If any issues purchased are not available, they will be downloaded, saved to the database and made available. This method checks both in-app purchases as well as web purchases (if userId is not nil)
-
restoreWebPurchases(userId: AnyObject) This method retrieves all web purchases for the user whose id is passed. If any issues purchased are not available, they will be downloaded, saved to the database and made available
-
isAvailable(appleId: String, userId: AnyObject?)
returns Bool
This method checks if a user has access to a given issue (based on Apple id/SKU). If the userId is provided, the app will check for access permissions through both in-app purchase and web purchases. Otherwise it will only check against in-app purchases -
listPurchases(userId: AnyObject?)
returns Array<String> or nil
This method returns an array of skus for Purchases made by the current logged in user (or IAPs + web purchases if userId is not nil) -
syncPurchases(userId: AnyObject) This method syncs all purchases saved in the database to the server for given user identity. All purchases which do not have a user id (i.e. have been purchased on the device through IAPs) will also be marked as purchased by this user on the server
##Sample Implementation A sample implementation of in-app purchases is provided in the demo. For the purpose of this demo, we are assuming all products purchased are Articles.
The classes of interest for the IAP implementation are
-
PurchasesViewController.swift This class is where the IAP product info is loaded and listed in a table view. When you select a row, it will add the row's product id to the payment queue. On successful verification of the purchase, you will be taken to
PurchaseDetailViewController
a. getProductInfo() In this method make sure you replace the sample product ids with actual product ids for your in-app purchases
b. restorePurchases(sender:) This method has commented calls for various methods provided in the AccessControl class
-
PurchaseDetailViewController.swift This class downloads a purchased article and adds it to the database by creating a
Purchase
class object -
AccessControlDemo.swift This class is the implementation of the AccessControl protocol. It also implements the
SKPaymentTransactionObserver
method -
NetworkManager.swift This class is used for handling all network calls to the subscriber API (used mostly by the
AccessControlDemo
class)a. The baseURL used in the
init()
method will change. Right now it is pointing to the test serverb. The secretKey used to generate the header is hard-coded for now to test against the staging server. This will change in the final implementation. 29.io will provide the secretKey
###To-dos for getting the demo to work
-
Set the correct team under General tab of the target and change the bundle identifier
-
Under Capabilities tab for the target, turn on In-app purchases
-
Replace the ClientKey in the Info.plist with your client key
-
Replace the SharedSecret in the Info.plist with your Apple in-app purchases Shared Secret generated in itunesconnect.apple.com
-
In the PurchasesViewController, change the product identifiers in the
getProductInfo()
method