Last active
October 23, 2019 08:47
-
-
Save coryalder/d0ed60f14cb7f473a21a to your computer and use it in GitHub Desktop.
Amazon Product Advertising API + Alamofire
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AmazonProduct.swift | |
// Requires SHXMLParser for parsing Amazon's XML responses (https://github.com/simhanature/SHXMLParser) | |
// | |
// Created by Cory Alder on 2015-01-11. | |
// Copyright (c) 2015 Davander Mobile Corporation. All rights reserved. | |
// | |
// partly inspired by RWMAmazonProductAdvertisingManager | |
import Alamofire | |
import Foundation | |
import SHXMLParser | |
enum AmazonProductAdvertising: String { | |
case StandardRegion = "webservices.amazon.com" | |
case AWSAccessKey = "AWSAccessKeyId" | |
case TimestampKey = "Timestamp" | |
case SignatureKey = "Signature" | |
case VersionKey = "Version" | |
case AssociateTagKey = "AssociateTag" | |
case CurrentVersion = "2011-08-01" | |
} | |
let AWSDateISO8601DateFormat3 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" | |
func amazonRequest(parameters: [String: AnyObject]? = nil, serializer ser: AmazonSerializer?) -> Alamofire.Request { | |
let serializer: AmazonSerializer = { | |
if let s = ser { | |
return s | |
} else { | |
let s = AmazonSerializer(key: aws_key_default, secret: aws_secret_default) | |
s.useSSL = false | |
return s | |
} | |
}() | |
let URL = serializer.endpointURL() | |
let e = Alamofire.ParameterEncoding.Custom(serializer.serializerBlock()) | |
let mutableURLRequest = NSMutableURLRequest(URL: URL) | |
mutableURLRequest.HTTPMethod = Alamofire.Method.GET.rawValue | |
let encoded: URLRequestConvertible = e.encode(mutableURLRequest, parameters: parameters).0 | |
return Alamofire.request(encoded) | |
} | |
class AmazonSerializer { | |
let accessKey: String | |
let secret: String | |
var region: String = AmazonProductAdvertising.StandardRegion.rawValue | |
var formatPath: String = "/onca/xml" | |
var useSSL = true | |
init(key: String, secret sec: String) { | |
self.secret = sec | |
self.accessKey = key | |
} | |
func endpointURL() -> NSURL { | |
let scheme = useSSL ? "https" : "http" | |
let URL = NSURL(string: "\(scheme)://\(region)\(formatPath)") | |
return URL! | |
} | |
func serializerBlock() -> (URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?) { | |
return { (req, params) -> (NSMutableURLRequest, NSError?) in | |
// TODO: if params == nil error out ASAP | |
var mutableParameters = params! | |
let timestamp = AmazonSerializer.ISO8601FormatStringFromDate(NSDate()) | |
if mutableParameters[AmazonProductAdvertising.AWSAccessKey.rawValue] == nil { | |
mutableParameters[AmazonProductAdvertising.AWSAccessKey.rawValue] = self.accessKey | |
} | |
mutableParameters[AmazonProductAdvertising.VersionKey.rawValue] = AmazonProductAdvertising.CurrentVersion.rawValue; | |
mutableParameters[AmazonProductAdvertising.TimestampKey.rawValue] = timestamp; | |
var canonicalStringArray = [String]() | |
// alphabetize | |
let sortedKeys = Array(mutableParameters.keys).sort {$0 < $1} | |
for key in sortedKeys { | |
canonicalStringArray.append("\(key)=\(mutableParameters[key]!)") | |
} | |
let canonicalString = canonicalStringArray.joinWithSeparator("&") | |
let encodedCanonicalString = CFURLCreateStringByAddingPercentEscapes( | |
nil, | |
canonicalString, | |
nil, | |
":,",//"!*'();:@&=+$,/?%#[]", | |
CFStringBuiltInEncodings.UTF8.rawValue | |
) | |
let method = req.URLRequest.HTTPMethod | |
let signature = "\(method)\n\(self.region)\n\(self.formatPath)\n\(encodedCanonicalString)" | |
let encodedSignatureData = signature.hmacSHA256(self.secret) | |
var encodedSignatureString = encodedSignatureData.base64EncodedString() | |
encodedSignatureString = CFURLCreateStringByAddingPercentEscapes( | |
nil, | |
encodedSignatureString, | |
nil, | |
"+=",//"!*'();:@&=+$,/?%#[]", | |
CFStringBuiltInEncodings.UTF8.rawValue | |
) as String | |
let newCanonicalString = "\(encodedCanonicalString)&\(AmazonProductAdvertising.SignatureKey.rawValue)=\(encodedSignatureString)" | |
let absString = req.URLRequest.URL!.absoluteString | |
let urlString = req.URLRequest.URL?.query != nil ? "\(absString)&\(newCanonicalString)" : "\(absString)?\(newCanonicalString)" | |
let request = req.URLRequest.mutableCopy() as! NSMutableURLRequest | |
request.URL = NSURL(string: urlString) | |
return (request, nil) | |
} | |
} | |
private class func ISO8601FormatStringFromDate(date: NSDate) -> NSString { | |
let dateFormatter = NSDateFormatter() | |
dateFormatter.timeZone = NSTimeZone(name: "GMT") | |
dateFormatter.dateFormat = AWSDateISO8601DateFormat3//"YYYY-MM-dd'T'HH:mm:ss'Z'" | |
dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") | |
return dateFormatter.stringFromDate(date) | |
} | |
} | |
// XML ResponseSerializer | |
extension Request { | |
class func XMLResponseSerializer() -> GenericResponseSerializer<Dictionary<String,AnyObject>> { | |
return GenericResponseSerializer { | |
request, response, data in | |
guard data != nil else { | |
let failureReason = "Data could not be serialized. Input data was nil." | |
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason) | |
return .Failure(data, error) | |
} | |
let parser = SHXMLParser() | |
if let document = parser.parseData(data) as? [String: AnyObject] { | |
return .Success(document) | |
} else { | |
let failureReason = "Data could not be serialized. for some unknwon reason" | |
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason) | |
return .Failure(data, error as NSError) | |
} | |
} | |
} | |
public func responseXML(completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Dictionary<String, AnyObject>?, ErrorType?) -> Void) -> Self { | |
let ser = Request.XMLResponseSerializer() | |
return response(queue: dispatch_get_main_queue(), responseSerializer: ser) { | |
req, res, xmlResult in | |
switch xmlResult { | |
case .Failure( _, let err): completionHandler(req, res, nil, err) | |
case .Success(let xml): completionHandler(req, res, xml, nil) | |
} | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let associate_tag_default = "weio-20" | |
let aws_key_default = "your amazon key" | |
let aws_secret_default = "your amazon secret" | |
let asin = "B00DVDMRX6" // an amazon id to search for | |
let serializer = AmazonSerializer(key: aws_key_default, secret: aws_secret_default) | |
let amazonParams = [ | |
"Service" : "AWSECommerceService", | |
"Operation" : "ItemLookup", | |
"ResponseGroup" : "Images,ItemAttributes", | |
"IdType" : "ASIN", | |
"ItemId" : asin, | |
"AssociateTag" : associate_tag_default, | |
"Condition" : "All" | |
] | |
amazonRequest(parameters: amazonParams, serializer: serializer).responseXML { (req, res, data, error) -> Void in | |
println("Got results! \(data)") | |
} | |
Thanks!
Though I ended up not using Alamofire it did helped a lot to understand the concept.
On line 112 you are missing the extension to string in order to hash the code.
I've added it from another source and it worked great! (just for other reference).
extension String {
func hmacSHA256(key: String) -> String {
let cKey = key.cString(using: String.Encoding.utf8)
let cData = self.cString(using: String.Encoding.utf8)
var result = [CUnsignedChar](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), cKey!, Int(strlen(cKey!)), cData!, Int(strlen(cData!)), &result)
let hmacData:NSData = NSData(bytes: result, length: (Int(CC_SHA256_DIGEST_LENGTH)))
let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)
return String(hmacBase64)
}
}
Did anyone manage to get this running?
It looks so useful! Unable to get it running on xcode 9.1 swift 3, so many errors :-(
Hey can you update it to swift 4
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I am reading this code and I am wondering if it still works with out the hmacsha256. I will be trying it later.