Fix concurrency warnings in Parser module.
This commit is contained in:
parent
f449a443d8
commit
e03ad03e60
@ -101,36 +101,37 @@ extension LocalAccountRefresher: DownloadSessionDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let parserData = ParserData(url: feed.url, data: data)
|
let parserData = ParserData(url: feed.url, data: data)
|
||||||
FeedParser.parse(parserData) { (parsedFeed, error) in
|
Task { @MainActor in
|
||||||
|
|
||||||
guard let account = feed.account, let parsedFeed = parsedFeed, error == nil else {
|
|
||||||
completion()
|
|
||||||
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Task { @MainActor in
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let articleChanges = try await account.update(feed: feed, with: parsedFeed)
|
let parsedFeed = try await FeedParser.parse(parserData)
|
||||||
|
|
||||||
if let httpResponse = response as? HTTPURLResponse {
|
guard let account = feed.account, let parsedFeed else {
|
||||||
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
|
|
||||||
}
|
|
||||||
feed.contentHash = dataHash
|
|
||||||
|
|
||||||
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
|
|
||||||
self.delegate?.localAccountRefresher(self, articleChanges: articleChanges) {
|
|
||||||
completion()
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
completion()
|
completion()
|
||||||
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
|
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let articleChanges = try await account.update(feed: feed, with: parsedFeed)
|
||||||
|
|
||||||
|
if let httpResponse = response as? HTTPURLResponse {
|
||||||
|
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
|
||||||
|
}
|
||||||
|
feed.contentHash = dataHash
|
||||||
|
|
||||||
|
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
|
||||||
|
self.delegate?.localAccountRefresher(self, articleChanges: articleChanges) {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
completion()
|
||||||
|
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func downloadSession(_ downloadSession: DownloadSession, shouldContinueAfterReceivingData data: Data, representedObject: AnyObject) -> Bool {
|
func downloadSession(_ downloadSession: DownloadSession, shouldContinueAfterReceivingData data: Data, representedObject: AnyObject) -> Bool {
|
||||||
let feed = representedObject as! Feed
|
let feed = representedObject as! Feed
|
||||||
|
@ -23,14 +23,25 @@ public struct InitialFeedDownloader {
|
|||||||
|
|
||||||
@MainActor public static func download(_ url: URL,_ completion: @escaping @Sendable (_ parsedFeed: ParsedFeed?) -> Void) {
|
@MainActor public static func download(_ url: URL,_ completion: @escaping @Sendable (_ parsedFeed: ParsedFeed?) -> Void) {
|
||||||
|
|
||||||
downloadUsingCache(url) { (data, response, error) in
|
Task {
|
||||||
guard let data = data else {
|
|
||||||
|
guard let downloadData = try? await downloadUsingCache(url) else {
|
||||||
|
completion(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let data = downloadData.data else {
|
||||||
completion(nil)
|
completion(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let parserData = ParserData(url: url.absoluteString, data: data)
|
let parserData = ParserData(url: url.absoluteString, data: data)
|
||||||
FeedParser.parse(parserData) { (parsedFeed, error) in
|
|
||||||
|
Task.detached {
|
||||||
|
guard let parsedFeed = try? await FeedParser.parse(parserData) else {
|
||||||
|
completion(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
completion(parsedFeed)
|
completion(parsedFeed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
__attribute__((swift_attr("@Sendable")))
|
||||||
@interface ParserData : NSObject
|
@interface ParserData : NSObject
|
||||||
|
|
||||||
@property (nonatomic, readonly) NSString *url;
|
@property (nonatomic, readonly) NSString *url;
|
||||||
|
@ -12,12 +12,8 @@ import ParserObjC
|
|||||||
// FeedParser handles RSS, Atom, JSON Feed, and RSS-in-JSON.
|
// FeedParser handles RSS, Atom, JSON Feed, and RSS-in-JSON.
|
||||||
// You don’t need to know the type of feed.
|
// You don’t need to know the type of feed.
|
||||||
|
|
||||||
public typealias FeedParserCallback = (_ parsedFeed: ParsedFeed?, _ error: Error?) -> Void
|
|
||||||
|
|
||||||
public struct FeedParser {
|
public struct FeedParser {
|
||||||
|
|
||||||
private static let parseQueue = DispatchQueue(label: "FeedParser parse queue")
|
|
||||||
|
|
||||||
public static func canParse(_ parserData: ParserData) -> Bool {
|
public static func canParse(_ parserData: ParserData) -> Bool {
|
||||||
|
|
||||||
let type = feedType(parserData)
|
let type = feedType(parserData)
|
||||||
@ -30,62 +26,26 @@ public struct FeedParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func mightBeAbleToParseBasedOnPartialData(_ parserData: ParserData) -> Bool {
|
public static func parse(_ parserData: ParserData) async throws -> ParsedFeed? {
|
||||||
|
|
||||||
let type = feedType(parserData, isPartialData: true)
|
let type = feedType(parserData)
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
case .jsonFeed, .rssInJSON, .rss, .atom, .unknown:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func parse(_ parserData: ParserData) throws -> ParsedFeed? {
|
case .jsonFeed:
|
||||||
|
return try JSONFeedParser.parse(parserData)
|
||||||
|
|
||||||
// This is generally fast enough to call on the main thread —
|
case .rssInJSON:
|
||||||
// but it’s probably a good idea to use a background queue if
|
return try RSSInJSONParser.parse(parserData)
|
||||||
// you might be doing a lot of parsing. (Such as in a feed reader.)
|
|
||||||
|
|
||||||
do {
|
case .rss:
|
||||||
let type = feedType(parserData)
|
return RSSParser.parse(parserData)
|
||||||
|
|
||||||
switch type {
|
case .atom:
|
||||||
|
return AtomParser.parse(parserData)
|
||||||
|
|
||||||
case .jsonFeed:
|
case .unknown, .notAFeed:
|
||||||
return try JSONFeedParser.parse(parserData)
|
return nil
|
||||||
|
|
||||||
case .rssInJSON:
|
|
||||||
return try RSSInJSONParser.parse(parserData)
|
|
||||||
|
|
||||||
case .rss:
|
|
||||||
return RSSParser.parse(parserData)
|
|
||||||
|
|
||||||
case .atom:
|
|
||||||
return AtomParser.parse(parserData)
|
|
||||||
|
|
||||||
case .unknown, .notAFeed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { throw error }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func parse(_ parserData: ParserData, _ completion: @escaping FeedParserCallback) {
|
|
||||||
|
|
||||||
parseQueue.async {
|
|
||||||
do {
|
|
||||||
let parsedFeed = try parse(parserData)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(parsedFeed, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(nil, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user