Parse Open Graph images when parsing metadata from an HTML page.
This commit is contained in:
parent
e08acc9837
commit
ff7695c290
|
@ -10,19 +10,22 @@
|
|||
|
||||
@class RSHTMLMetadataFeedLink;
|
||||
@class RSHTMLMetadataAppleTouchIcon;
|
||||
|
||||
@class RSHTMLOpenGraphProperties;
|
||||
@class RSHTMLOpenGraphImage;
|
||||
@class RSHTMLTag;
|
||||
|
||||
@interface RSHTMLMetadata : NSObject
|
||||
|
||||
- (instancetype)initWithURLString:(NSString *)urlString dictionaries:(NSArray <NSDictionary *> *)dictionaries;
|
||||
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags;
|
||||
|
||||
@property (nonatomic, readonly) NSString *baseURLString;
|
||||
@property (nonatomic, readonly) NSArray <NSDictionary *> *dictionaries;
|
||||
@property (nonatomic, readonly) NSArray <RSHTMLTag *> *tags;
|
||||
|
||||
@property (nonatomic, readonly) NSString *faviconLink;
|
||||
@property (nonatomic, readonly) NSArray <RSHTMLMetadataAppleTouchIcon *> *appleTouchIcons;
|
||||
@property (nonatomic, readonly) NSArray <RSHTMLMetadataFeedLink *> *feedLinks;
|
||||
|
||||
@property (nonatomic, readonly) RSHTMLOpenGraphProperties *openGraphProperties;
|
||||
@end
|
||||
|
||||
|
||||
|
@ -43,3 +46,24 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface RSHTMLOpenGraphProperties : NSObject
|
||||
|
||||
// TODO: the rest. At this writing (Nov. 26, 2017) I just care about og:image.
|
||||
// See http://ogp.me/
|
||||
|
||||
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags;
|
||||
|
||||
@property (nonatomic, readonly) NSArray <RSHTMLOpenGraphImage *> *images;
|
||||
|
||||
@end
|
||||
|
||||
@interface RSHTMLOpenGraphImage : NSObject
|
||||
|
||||
@property (nonatomic, readonly) NSString *url;
|
||||
@property (nonatomic, readonly) NSString *secureURL;
|
||||
@property (nonatomic, readonly) NSString *mimeType;
|
||||
@property (nonatomic, readonly) CGFloat width;
|
||||
@property (nonatomic, readonly) CGFloat height;
|
||||
@property (nonatomic, readonly) NSString *altText;
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
|
||||
#import <RSParser/RSHTMLMetadata.h>
|
||||
#import <RSParser/RSParserInternal.h>
|
||||
#import <RSParser/RSHTMLTag.h>
|
||||
|
||||
static NSString *urlStringFromDictionary(NSDictionary *d);
|
||||
static NSString *absoluteURLStringWithRelativeURLString(NSString *relativeURLString, NSString *baseURLString);
|
||||
static NSString *absoluteURLStringWithDictionary(NSDictionary *d, NSString *baseURLString);
|
||||
static NSArray *objectsOfClassWithDictionaries(Class class, NSArray *dictionaries, NSString *baseURLString);
|
||||
static NSArray *objectsOfClassWithTags(Class class, NSArray *tags, NSString *baseURLString);
|
||||
static NSString *relValue(NSDictionary *d);
|
||||
static BOOL typeIsFeedType(NSString *type);
|
||||
|
||||
|
@ -33,24 +34,23 @@ static NSString *kTypeKey = @"type";
|
|||
|
||||
@interface RSHTMLMetadataAppleTouchIcon ()
|
||||
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString;
|
||||
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface RSHTMLMetadataFeedLink ()
|
||||
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString;
|
||||
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation RSHTMLMetadata
|
||||
|
||||
|
||||
#pragma mark - Init
|
||||
|
||||
- (instancetype)initWithURLString:(NSString *)urlString dictionaries:(NSArray <NSDictionary *> *)dictionaries {
|
||||
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags {
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -58,17 +58,19 @@ static NSString *kTypeKey = @"type";
|
|||
}
|
||||
|
||||
_baseURLString = urlString;
|
||||
_dictionaries = dictionaries;
|
||||
_faviconLink = [self resolvedLinkFromFirstDictionaryWithMatchingRel:kShortcutIconRelValue];
|
||||
_tags = tags;
|
||||
_faviconLink = [self resolvedLinkFromFirstLinkTagWithMatchingRel:kShortcutIconRelValue];
|
||||
if (_faviconLink == nil) {
|
||||
_faviconLink = [self resolvedLinkFromFirstDictionaryWithMatchingRel:kIconRelValue];
|
||||
_faviconLink = [self resolvedLinkFromFirstLinkTagWithMatchingRel:kIconRelValue];
|
||||
}
|
||||
|
||||
NSArray *appleTouchIconDictionaries = [self appleTouchIconDictionaries];
|
||||
_appleTouchIcons = objectsOfClassWithDictionaries([RSHTMLMetadataAppleTouchIcon class], appleTouchIconDictionaries, urlString);
|
||||
NSArray *appleTouchIconTags = [self appleTouchIconTags];
|
||||
_appleTouchIcons = objectsOfClassWithTags([RSHTMLMetadataAppleTouchIcon class], appleTouchIconTags, urlString);
|
||||
|
||||
NSArray *feedLinkDictionaries = [self feedLinkDictionaries];
|
||||
_feedLinks = objectsOfClassWithDictionaries([RSHTMLMetadataFeedLink class], feedLinkDictionaries, urlString);
|
||||
NSArray *feedLinkTags = [self feedLinkTags];
|
||||
_feedLinks = objectsOfClassWithTags([RSHTMLMetadataFeedLink class], feedLinkTags, urlString);
|
||||
|
||||
_openGraphProperties = [[RSHTMLOpenGraphProperties alloc] initWithURLString:urlString tags:tags];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -76,15 +78,18 @@ static NSString *kTypeKey = @"type";
|
|||
|
||||
#pragma mark - Private
|
||||
|
||||
- (NSDictionary *)firstDictionaryWithMatchingRel:(NSString *)valueToMatch {
|
||||
- (RSHTMLTag *)firstLinkTagWithMatchingRel:(NSString *)valueToMatch {
|
||||
|
||||
// Case-insensitive.
|
||||
|
||||
for (NSDictionary *oneDictionary in self.dictionaries) {
|
||||
for (RSHTMLTag *tag in self.tags) {
|
||||
|
||||
NSString *oneRelValue = relValue(oneDictionary);
|
||||
if (tag.type != RSHTMLTagTypeLink) {
|
||||
continue;
|
||||
}
|
||||
NSString *oneRelValue = relValue(tag.attributes);
|
||||
if (oneRelValue && [oneRelValue compare:valueToMatch options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
return oneDictionary;
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,28 +97,36 @@ static NSString *kTypeKey = @"type";
|
|||
}
|
||||
|
||||
|
||||
- (NSArray *)appleTouchIconDictionaries {
|
||||
- (NSArray *)appleTouchIconTags {
|
||||
|
||||
NSMutableArray *dictionaries = [NSMutableArray new];
|
||||
NSMutableArray *tags = [NSMutableArray new];
|
||||
|
||||
for (NSDictionary *oneDictionary in self.dictionaries) {
|
||||
for (RSHTMLTag *tag in self.tags) {
|
||||
|
||||
NSString *oneRelValue = relValue(oneDictionary).lowercaseString;
|
||||
if (tag.type != RSHTMLTagTypeLink) {
|
||||
continue;
|
||||
}
|
||||
NSString *oneRelValue = relValue(tag.attributes).lowercaseString;
|
||||
if ([oneRelValue isEqualToString:kAppleTouchIconValue] || [oneRelValue isEqualToString:kAppleTouchIconPrecomposedValue]) {
|
||||
[dictionaries addObject:oneDictionary];
|
||||
[tags addObject:tag];
|
||||
}
|
||||
}
|
||||
|
||||
return dictionaries;
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)feedLinkDictionaries {
|
||||
- (NSArray *)feedLinkTags {
|
||||
|
||||
NSMutableArray *dictionaries = [NSMutableArray new];
|
||||
NSMutableArray *tags = [NSMutableArray new];
|
||||
|
||||
for (NSDictionary *oneDictionary in self.dictionaries) {
|
||||
for (RSHTMLTag *tag in self.tags) {
|
||||
|
||||
if (tag.type != RSHTMLTagTypeLink) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSDictionary *oneDictionary = tag.attributes;
|
||||
NSString *oneRelValue = relValue(oneDictionary).lowercaseString;
|
||||
if (![oneRelValue isEqualToString:kAlternateKey]) {
|
||||
continue;
|
||||
|
@ -128,20 +141,19 @@ static NSString *kTypeKey = @"type";
|
|||
continue;
|
||||
}
|
||||
|
||||
[dictionaries addObject:oneDictionary];
|
||||
[tags addObject:tag];
|
||||
}
|
||||
|
||||
return dictionaries;
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)resolvedLinkFromFirstDictionaryWithMatchingRel:(NSString *)relValue {
|
||||
- (NSString *)resolvedLinkFromFirstLinkTagWithMatchingRel:(NSString *)relValue {
|
||||
|
||||
NSDictionary *d = [self firstDictionaryWithMatchingRel:relValue];
|
||||
return absoluteURLStringWithDictionary(d, self.baseURLString);
|
||||
RSHTMLTag *tag = [self firstLinkTagWithMatchingRel:relValue];
|
||||
return absoluteURLStringWithDictionary(tag.attributes, self.baseURLString);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -184,19 +196,19 @@ static NSString *absoluteURLStringWithDictionary(NSDictionary *d, NSString *base
|
|||
}
|
||||
|
||||
|
||||
static NSArray *objectsOfClassWithDictionaries(Class class, NSArray *dictionaries, NSString *baseURLString) {
|
||||
static NSArray *objectsOfClassWithTags(Class class, NSArray *tags, NSString *baseURLString) {
|
||||
|
||||
NSMutableArray *objects = [NSMutableArray new];
|
||||
|
||||
for (NSDictionary *oneDictionary in dictionaries) {
|
||||
for (RSHTMLTag *tag in tags) {
|
||||
|
||||
id oneObject = [[class alloc] initWithDictionary:oneDictionary baseURLString:baseURLString];
|
||||
id oneObject = [[class alloc] initWithTag:tag baseURLString:baseURLString];
|
||||
if (oneObject) {
|
||||
[objects addObject:oneObject];
|
||||
}
|
||||
}
|
||||
|
||||
return [objects copy];
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
||||
|
@ -209,14 +221,14 @@ static BOOL typeIsFeedType(NSString *type) {
|
|||
|
||||
@implementation RSHTMLMetadataAppleTouchIcon
|
||||
|
||||
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString {
|
||||
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString {
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *d = tag.attributes;
|
||||
_urlString = absoluteURLStringWithDictionary(d, baseURLString);
|
||||
_sizes = [d rsparser_objectForCaseInsensitiveKey:kSizesKey];
|
||||
_rel = [d rsparser_objectForCaseInsensitiveKey:kRelKey];
|
||||
|
@ -224,20 +236,19 @@ static BOOL typeIsFeedType(NSString *type) {
|
|||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation RSHTMLMetadataFeedLink
|
||||
|
||||
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)d baseURLString:(NSString *)baseURLString {
|
||||
- (instancetype)initWithTag:(RSHTMLTag *)tag baseURLString:(NSString *)baseURLString {
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *d = tag.attributes;
|
||||
_urlString = absoluteURLStringWithDictionary(d, baseURLString);
|
||||
_title = [d rsparser_objectForCaseInsensitiveKey:kTitleKey];
|
||||
_type = [d rsparser_objectForCaseInsensitiveKey:kTypeKey];
|
||||
|
@ -245,6 +256,130 @@ static BOOL typeIsFeedType(NSString *type) {
|
|||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RSHTMLOpenGraphImage ()
|
||||
|
||||
@property (nonatomic, readwrite) NSString *url;
|
||||
@property (nonatomic, readwrite) NSString *secureURL;
|
||||
@property (nonatomic, readwrite) NSString *mimeType;
|
||||
@property (nonatomic, readwrite) CGFloat width;
|
||||
@property (nonatomic, readwrite) CGFloat height;
|
||||
@property (nonatomic, readwrite) NSString *altText;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RSHTMLOpenGraphImage
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@interface RSHTMLOpenGraphProperties ()
|
||||
|
||||
@property (nonatomic) NSMutableArray *ogImages;
|
||||
@end
|
||||
|
||||
@implementation RSHTMLOpenGraphProperties
|
||||
|
||||
- (instancetype)initWithURLString:(NSString *)urlString tags:(NSArray <RSHTMLTag *> *)tags {
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_ogImages = [NSMutableArray new];
|
||||
|
||||
[self parseTags:tags];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (RSHTMLOpenGraphImage *)currentImage {
|
||||
|
||||
return self.ogImages.lastObject;
|
||||
}
|
||||
|
||||
|
||||
- (RSHTMLOpenGraphImage *)pushImage {
|
||||
|
||||
RSHTMLOpenGraphImage *image = [RSHTMLOpenGraphImage new];
|
||||
[self.ogImages addObject:image];
|
||||
return image;
|
||||
}
|
||||
|
||||
- (RSHTMLOpenGraphImage *)ensureImage {
|
||||
|
||||
RSHTMLOpenGraphImage *image = [self currentImage];
|
||||
if (image != nil) {
|
||||
return image;
|
||||
}
|
||||
return [self pushImage];
|
||||
}
|
||||
|
||||
|
||||
- (NSArray *)images {
|
||||
|
||||
return self.ogImages;
|
||||
}
|
||||
|
||||
static NSString *ogPrefix = @"og:";
|
||||
static NSString *ogImage = @"og:image";
|
||||
static NSString *ogImageURL = @"og:image:url";
|
||||
static NSString *ogImageSecureURL = @"og:image:secure_url";
|
||||
static NSString *ogImageType = @"og:image:type";
|
||||
static NSString *ogImageWidth = @"og:image:width";
|
||||
static NSString *ogImageHeight = @"og:image:height";
|
||||
static NSString *ogImageAlt = @"og:image:alt";
|
||||
static NSString *ogPropertyKey = @"property";
|
||||
static NSString *ogContentKey = @"content";
|
||||
|
||||
- (void)parseTags:(NSArray *)tags {
|
||||
|
||||
for (RSHTMLTag *tag in tags) {
|
||||
|
||||
if (tag.type != RSHTMLTagTypeMeta) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *propertyName = tag.attributes[ogPropertyKey];
|
||||
if (!propertyName || ![propertyName hasPrefix:ogPrefix]) {
|
||||
continue;
|
||||
}
|
||||
NSString *content = tag.attributes[ogContentKey];
|
||||
if (!content) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([propertyName isEqualToString:ogImage]) {
|
||||
RSHTMLOpenGraphImage *image = [self currentImage];
|
||||
if (!image || image.url) { // Most likely case, since og:image will probably appear before other image attributes.
|
||||
image = [self pushImage];
|
||||
}
|
||||
image.url = content;
|
||||
}
|
||||
|
||||
else if ([propertyName isEqualToString:ogImageURL]) {
|
||||
[self ensureImage].url = content;
|
||||
}
|
||||
else if ([propertyName isEqualToString:ogImageSecureURL]) {
|
||||
[self ensureImage].secureURL = content;
|
||||
}
|
||||
else if ([propertyName isEqualToString:ogImageType]) {
|
||||
[self ensureImage].mimeType = content;
|
||||
}
|
||||
else if ([propertyName isEqualToString:ogImageAlt]) {
|
||||
[self ensureImage].altText = content;
|
||||
}
|
||||
else if ([propertyName isEqualToString:ogImageWidth]) {
|
||||
[self ensureImage].width = [content floatValue];
|
||||
}
|
||||
else if ([propertyName isEqualToString:ogImageHeight]) {
|
||||
[self ensureImage].height = [content floatValue];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
#import <RSParser/RSSAXParser.h>
|
||||
#import <RSParser/RSParserInternal.h>
|
||||
#import <RSParser/ParserData.h>
|
||||
|
||||
#import <RSParser/RSHTMLTag.h>
|
||||
|
||||
@interface RSHTMLMetadataParser () <RSSAXHTMLParserDelegate>
|
||||
|
||||
@property (nonatomic, readonly) ParserData *parserData;
|
||||
@property (nonatomic, readwrite) RSHTMLMetadata *metadata;
|
||||
@property (nonatomic) NSMutableArray *dictionaries;
|
||||
@property (nonatomic) NSMutableArray *tags;
|
||||
@property (nonatomic) BOOL didFinishParsing;
|
||||
|
||||
@end
|
||||
|
@ -50,7 +50,7 @@
|
|||
}
|
||||
|
||||
_parserData = parserData;
|
||||
_dictionaries = [NSMutableArray new];
|
||||
_tags = [NSMutableArray new];
|
||||
|
||||
[self parse];
|
||||
|
||||
|
@ -66,7 +66,7 @@
|
|||
[parser parseData:self.parserData.data];
|
||||
[parser finishParsing];
|
||||
|
||||
self.metadata = [[RSHTMLMetadata alloc] initWithURLString:self.parserData.url dictionaries:[self.dictionaries copy]];
|
||||
self.metadata = [[RSHTMLMetadata alloc] initWithURLString:self.parserData.url tags:self.tags];
|
||||
}
|
||||
|
||||
|
||||
|
@ -84,7 +84,6 @@ static NSString *kRelKey = @"rel";
|
|||
return [d rsparser_objectForCaseInsensitiveKey:kSrcKey];
|
||||
}
|
||||
|
||||
|
||||
- (void)handleLinkAttributes:(NSDictionary *)d {
|
||||
|
||||
if (RSParserStringIsEmpty([d rsparser_objectForCaseInsensitiveKey:kRelKey])) {
|
||||
|
@ -94,9 +93,15 @@ static NSString *kRelKey = @"rel";
|
|||
return;
|
||||
}
|
||||
|
||||
[self.dictionaries addObject:d];
|
||||
RSHTMLTag *tag = [RSHTMLTag linkTagWithAttributes:d];
|
||||
[self.tags addObject:tag];
|
||||
}
|
||||
|
||||
- (void)handleMetaAttributes:(NSDictionary *)d {
|
||||
|
||||
RSHTMLTag *tag = [RSHTMLTag metaTagWithAttributes:d];
|
||||
[self.tags addObject:tag];
|
||||
}
|
||||
|
||||
#pragma mark - RSSAXHTMLParserDelegate
|
||||
|
||||
|
@ -104,6 +109,8 @@ static const char *kBody = "body";
|
|||
static const NSInteger kBodyLength = 5;
|
||||
static const char *kLink = "link";
|
||||
static const NSInteger kLinkLength = 5;
|
||||
static const char *kMeta = "meta";
|
||||
static const NSInteger kMetaLength = 5;
|
||||
|
||||
- (void)saxParser:(RSSAXHTMLParser *)SAXParser XMLStartElement:(const xmlChar *)localName attributes:(const xmlChar **)attributes {
|
||||
|
||||
|
@ -116,14 +123,20 @@ static const NSInteger kLinkLength = 5;
|
|||
return;
|
||||
}
|
||||
|
||||
if (!RSSAXEqualTags(localName, kLink, kLinkLength)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(localName, kLink, kLinkLength)) {
|
||||
NSDictionary *d = [SAXParser attributesDictionary:attributes];
|
||||
if (!RSParserObjectIsEmpty(d)) {
|
||||
[self handleLinkAttributes:d];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(localName, kMeta, kMetaLength)) {
|
||||
NSDictionary *d = [SAXParser attributesDictionary:attributes];
|
||||
if (!RSParserObjectIsEmpty(d)) {
|
||||
[self handleMetaAttributes:d];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// RSHTMLTag.h
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 11/26/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
extern NSString *RSHTMLTagNameLink; // @"link"
|
||||
extern NSString *RSHTMLTagNameMeta; // @"meta"
|
||||
|
||||
typedef NS_ENUM(NSInteger, RSHTMLTagType) {
|
||||
RSHTMLTagTypeLink,
|
||||
RSHTMLTagTypeMeta
|
||||
};
|
||||
|
||||
@interface RSHTMLTag : NSObject
|
||||
|
||||
- (instancetype)initWithType:(RSHTMLTagType)type attributes:(NSDictionary *)attributes;
|
||||
|
||||
+ (RSHTMLTag *)linkTagWithAttributes:(NSDictionary *)attributes;
|
||||
+ (RSHTMLTag *)metaTagWithAttributes:(NSDictionary *)attributes;
|
||||
|
||||
@property (nonatomic, readonly) RSHTMLTagType type;
|
||||
@property (nonatomic, readonly) NSDictionary *attributes;
|
||||
|
||||
@end
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// RSHTMLTag.m
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 11/26/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RSHTMLTag.h"
|
||||
|
||||
NSString *RSHTMLTagNameLink = @"link";
|
||||
NSString *RSHTMLTagNameMeta = @"meta";
|
||||
|
||||
@implementation RSHTMLTag
|
||||
|
||||
- (instancetype)initWithType:(RSHTMLTagType)type attributes:(NSDictionary *)attributes {
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_type = type;
|
||||
_attributes = attributes;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (RSHTMLTag *)linkTagWithAttributes:(NSDictionary *)attributes {
|
||||
|
||||
return [[self alloc] initWithType:RSHTMLTagTypeLink attributes:attributes];
|
||||
}
|
||||
|
||||
+ (RSHTMLTag *)metaTagWithAttributes:(NSDictionary *)attributes {
|
||||
|
||||
return [[self alloc] initWithType:RSHTMLTagTypeMeta attributes:attributes];
|
||||
}
|
||||
|
||||
@end
|
|
@ -44,6 +44,7 @@
|
|||
#import <RSParser/RSHTMLMetadata.h>
|
||||
#import <RSParser/RSHTMLLinkParser.h>
|
||||
#import <RSParser/RSSAXHTMLParser.h> // For writing your own HTML parser.
|
||||
#import <RSParser/RSHTMLTag.h>
|
||||
|
||||
// Utilities
|
||||
|
||||
|
|
|
@ -60,6 +60,9 @@
|
|||
84469D401EFF29A9004A6B28 /* FeedParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */; };
|
||||
84469D421EFF2B2D004A6B28 /* JSONTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D411EFF2B2D004A6B28 /* JSONTypes.swift */; };
|
||||
84469D441F002CEF004A6B28 /* JSONFeedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84469D431F002CEF004A6B28 /* JSONFeedParser.swift */; };
|
||||
845213251FCB3C76003B6E93 /* coco.html in Resources */ = {isa = PBXBuildFile; fileRef = 845213241FCB3C75003B6E93 /* coco.html */; };
|
||||
845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */ = {isa = PBXBuildFile; fileRef = 845213261FCB4042003B6E93 /* RSHTMLTag.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 845213271FCB4042003B6E93 /* RSHTMLTag.m */; };
|
||||
84628AAD1FCA10AE00566A9B /* allthis.atom in Resources */ = {isa = PBXBuildFile; fileRef = 84628AAC1FCA10AE00566A9B /* allthis.atom */; };
|
||||
849A03D01F0081EA00122600 /* DaringFireball.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C51F0081EA00122600 /* DaringFireball.html */; };
|
||||
849A03D11F0081EA00122600 /* DaringFireball.rss in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C61F0081EA00122600 /* DaringFireball.rss */; };
|
||||
|
@ -157,6 +160,9 @@
|
|||
84469D3F1EFF29A9004A6B28 /* FeedParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedParserError.swift; path = Feeds/FeedParserError.swift; sourceTree = "<group>"; };
|
||||
84469D411EFF2B2D004A6B28 /* JSONTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONTypes.swift; path = Feeds/JSON/JSONTypes.swift; sourceTree = "<group>"; };
|
||||
84469D431F002CEF004A6B28 /* JSONFeedParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONFeedParser.swift; path = Feeds/JSON/JSONFeedParser.swift; sourceTree = "<group>"; };
|
||||
845213241FCB3C75003B6E93 /* coco.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = coco.html; sourceTree = "<group>"; };
|
||||
845213261FCB4042003B6E93 /* RSHTMLTag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSHTMLTag.h; sourceTree = "<group>"; };
|
||||
845213271FCB4042003B6E93 /* RSHTMLTag.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSHTMLTag.m; sourceTree = "<group>"; };
|
||||
84628AAC1FCA10AE00566A9B /* allthis.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = allthis.atom; sourceTree = "<group>"; };
|
||||
849A03C51F0081EA00122600 /* DaringFireball.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DaringFireball.html; sourceTree = "<group>"; };
|
||||
849A03C61F0081EA00122600 /* DaringFireball.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = DaringFireball.rss; sourceTree = "<group>"; };
|
||||
|
@ -285,6 +291,8 @@
|
|||
84469D041EFA307E004A6B28 /* RSHTMLMetadataParser.m */,
|
||||
84469D051EFA307E004A6B28 /* RSSAXHTMLParser.h */,
|
||||
84469D061EFA307E004A6B28 /* RSSAXHTMLParser.m */,
|
||||
845213261FCB4042003B6E93 /* RSHTMLTag.h */,
|
||||
845213271FCB4042003B6E93 /* RSHTMLTag.m */,
|
||||
);
|
||||
path = HTML;
|
||||
sourceTree = "<group>";
|
||||
|
@ -348,6 +356,7 @@
|
|||
849A03CC1F0081EA00122600 /* OneFootTsunami.atom */,
|
||||
849A03E71F01F88600122600 /* ScriptingNews.json */,
|
||||
849A03CD1F0081EA00122600 /* scriptingNews.rss */,
|
||||
845213241FCB3C75003B6E93 /* coco.html */,
|
||||
849A03CE1F0081EA00122600 /* sixcolors.html */,
|
||||
84628AAC1FCA10AE00566A9B /* allthis.atom */,
|
||||
849A03CF1F0081EA00122600 /* Subs.opml */,
|
||||
|
@ -420,6 +429,7 @@
|
|||
84D81BDC1EFA28E700652332 /* RSParser.h in Headers */,
|
||||
84469D0B1EFA307E004A6B28 /* RSHTMLMetadataParser.h in Headers */,
|
||||
84469CFC1EFA3069004A6B28 /* RSSAXParser.h in Headers */,
|
||||
845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */,
|
||||
84E7E69F1F85780D0046719D /* ParserData.h in Headers */,
|
||||
84469D071EFA307E004A6B28 /* RSHTMLLinkParser.h in Headers */,
|
||||
84469D0D1EFA307E004A6B28 /* RSSAXHTMLParser.h in Headers */,
|
||||
|
@ -522,6 +532,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
845213251FCB3C76003B6E93 /* coco.html in Resources */,
|
||||
849A03D51F0081EA00122600 /* KatieFloyd.rss in Resources */,
|
||||
849A03D81F0081EA00122600 /* scriptingNews.rss in Resources */,
|
||||
840FDCBA1F02186D0041F61B /* DaringFireball.json in Resources */,
|
||||
|
@ -569,6 +580,7 @@
|
|||
84469D0C1EFA307E004A6B28 /* RSHTMLMetadataParser.m in Sources */,
|
||||
84469D0A1EFA307E004A6B28 /* RSHTMLMetadata.m in Sources */,
|
||||
84469D171EFA30A2004A6B28 /* NSString+RSParser.m in Sources */,
|
||||
845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */,
|
||||
84469D2C1EFA3134004A6B28 /* RSParsedArticle.m in Sources */,
|
||||
84285AAA1F006456002E8708 /* RSParsedFeedTransformer.swift in Sources */,
|
||||
84469D2E1EFA3134004A6B28 /* RSParsedFeed.m in Sources */,
|
||||
|
|
|
@ -111,4 +111,13 @@ class HTMLMetadataTests: XCTestCase {
|
|||
let _ = RSHTMLMetadataParser.htmlMetadata(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testCocoOGImage() {
|
||||
|
||||
let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/")
|
||||
let metadata = RSHTMLMetadataParser.htmlMetadata(with: d)
|
||||
let openGraphData = metadata.openGraphProperties!
|
||||
let image = openGraphData.images.first!
|
||||
XCTAssert(image.url == "https://cdn.theatlantic.com/assets/media/img/mt/2017/11/1033101_first_full_length_trailer_arrives_pixars_coco/facebook.jpg?1511382177")
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue