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
IssueHandlerAn 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
Stringwith the keyClientKeyin 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 UIntFind 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 nilGet 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
Stringwith the keyClientKeyin 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 nilThe 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 nilGet issue details from database for a specific global id -
addIssueOnNewsstand(issueId: String) Add issue on Newsstand
-
getActiveDownloads()
returns NSArrayGet 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
Stringwith the keyClientKeyin 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 nilThe 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
StringGlobal id of a volume - this is unique for each volume -
title
StringTitle of the volume -
subtitle
StringSubtitle of the volume -
volumeDesc
StringDescription of the volume -
assetFolder
StringFolder saving all the assets for the volume -
coverImageId
StringGlobal id of the asset which is the cover image of the volume -
publisher
StringPublisher of the volume -
publishedDate
NSDatePublished date for the volume -
releaseDate
StringRelease date for the volume -
keywords
StringKeywords for the volume (comma separated string) -
metadata
StringCustom metadata of the volume (JSON string - dictionary) -
published
BoolWhether 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 nilThis method returns the Volume object for the most recent volume in the database (sorted by publish date) -
searchVolumesWith(keywords: [String])
returns Array<Volume> or nilThis method accepts an array of keywords and returns all volumes with specific keywords -
getVolumes()
returns Array<Volume> or nilThis method returns all volumes -
getVolume(volumeId: String)
returns Volume or nilThis method inputs the global id of a volume and returns the Volume object
###Instance methods (public)
-
getValue(key:)
returns AnyObject/id or nilThis 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
StringGlobal id of an issue - this is unique for each issue -
appleId
StringSKU or Apple Id for an issue -
title
StringTitle of the issue -
issueDesc
StringDescription of the issue -
assetFolder
StringFolder saving all the assets for the issue -
coverImageId
StringGlobal id of the asset which is the cover image of the issue -
iconImageURL
StringFile URL for the icon image -
publishedDate
NSDatePublished date for the issue -
lastUpdateDate
StringLast updated date for the issue -
displayDate
StringDisplay date for an issue -
metadata
StringCustom metadata of the issue (JSON string - dictionary) -
volumeId
StringGlobal 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 nilThis method returns the Issue object for the most recent issue in the database (sorted by publish date) -
getIssueFor(appleId: String)
returns Issue or nilThis 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 nilThis 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 nilThis 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 nilThis 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
StringGlobal id of an article - this is unique for each article -
title
StringArticle title -
articleDesc
StringArticle description -
slug
StringArticle slug -
dek
StringArticle dek -
body
StringArticle content -
permalink
StringPermanent link to the article -
url
StringArticle URL -
sourceURL
StringURL to the article's source -
authorName
StringArticle author's name -
authorURL
StringLink to the article author's profile -
section
StringSection under which the article falls -
articleType
StringType of article -
keywords
StringKeywords which the article falls under (comma separated string) -
commentary
StringArticle commentary -
date
NSDateArticle published date -
metadata
StringArticle metadata (JSON string - dictionary) -
placement
IntegerPlacement of the article in an issue -
mainImageURL
StringURL for the article's feature image -
thumbImageURL
StringURL for the article's thumbnail image -
isPublished
BOOLWhether the article is published or in draft -
isFeatured
BOOLWhether the article is featured for the given issue or not -
issueId
StringGlobal id for the issue the article belongs to. This can be blank for independent articles -
appleId
StringApple 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 nilThis 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 nilThis method returns the value for a specific key from the custom metadata of the article -
replacePatternsWithAssets()
returns NSStringThis method replaces the asset placeholders in the body of the Article with actual assets using HTML codes Images are replaced withtags, 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
StringGlobal id of an asset - this is unique for each asset -
caption
StringCaption for the asset - used in the final rendered HTML -
source
StringSource attribution for the asset -
squareURL
StringFile URL for the asset's square thumbnail -
originalURL
StringFile URL for the original asset -
mainPortraitURL
StringFile URL for the portrait image of the asset -
mainLandscapeURL
StringFile URL for the landscape image of the asset -
iconURL
StringFile URL for the icon image -
metadata
StringCustom metadata for the asset -
type
StringAsset type. Defaults to a photo. Can be image, sound, video or custom -
placement
IntegerPlacement of an asset for an article or issue -
articleId
StringGlobal id for the article with which the asset is associated. Can be blank if this is an issue's asset -
issue
IssueIssue 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
StringGlobal 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 AssetThis 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 UIntThis 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 nilThis 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 StringThis method returns the path of the asset file (using theoriginalURLproperty) 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 Stringreturns the global id of the current volume -
retrieveCurrentIssue()
returns Stringreturns the global id of the current issue -
retrieveCurrentArticle()
returns Stringreturns the global id of the current article -
retrieveCurrentAsset()
returns Stringreturns the global id of the current active asset -
getReadingPercentageFor(articleId: String)
returns Floatretrieves 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
StringApple id/SKU of the purchase made - article, issue or volume -
globalId
StringGlobal id of the purchase made - article, issue or volume -
mode
StringMode of purchase - Web (could be any - Stripe or any other), IAP -
type
StringType of purchase - article, issue or volume. Used to determine what type of a record this is -
purchaseDate
NSDatePurchase date -
expirationDate
StringExpiration date - only used for subscriptions -
userIdentity
StringIdentity 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
Stringwith the keyClientKeyin 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 nilThe 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 nilThis 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 nilThis 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 BoolThis 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 nilThis 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
PurchaseDetailViewControllera. 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
Purchaseclass object -
AccessControlDemo.swift This class is the implementation of the AccessControl protocol. It also implements the
SKPaymentTransactionObservermethod -
NetworkManager.swift This class is used for handling all network calls to the subscriber API (used mostly by the
AccessControlDemoclass)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