nmdias / FeedKit

An RSS, Atom and JSON Feed parser written in Swift
MIT License
1.19k stars 174 forks source link

Thread Crash XMLFeedParser Line 88 Unexpectedly found nil while unwrapping an Optional value #93

Closed neilt closed 5 years ago

neilt commented 5 years ago
* thread #37, queue = 'com.apple.root.user-initiated-qos', stop reason = Fatal error: Unexpectedly found nil while unwrapping an Optional value
    frame #0: 0x00007fff64e2cd50 libswiftCore.dylib`_swift_runtime_on_report
    frame #1: 0x00007fff64e8c788 libswiftCore.dylib`_swift_stdlib_reportFatalError + 168
    frame #2: 0x00007fff64d8d106 libswiftCore.dylib`function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded> of closure #2 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in Swift._fatalErrorMessage(_: Swift.StaticString, _: Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 70
    frame #3: 0x00007fff64d8d483 libswiftCore.dylib`function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded, Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage(_: Swift.StaticString, _: Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 307
    frame #4: 0x00007fff64b8ca03 libswiftCore.dylib`Swift._fatalErrorMessage(_: Swift.StaticString, _: Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 19
  * frame #5: 0x0000000100146bf5 RSS2PGGatherer`XMLFeedParser.parse(self=0x0000000119585800) at XMLFeedParser.swift:88:56
    frame #6: 0x0000000100147e63 RSS2PGGatherer`protocol witness for FeedParserProtocol.parse() in conformance XMLFeedParser at <compiler-generated>:0
    frame #7: 0x00000001000a1532 
RSS2PGGatherer`FeedParser.parse(self=0x0000000102225ce0) at FeedParser.swift:88:28
    frame #8: 0x00000001000a2922 RSS2PGGatherer`closure #1 in FeedParser.parseAsync(result=0x0000000100051620 RSS2PGGatherer`partial apply forwarder for closure #1 (FeedKit.Result) -> () in RSS2PGGatherer.RSSFeedRetrieval.retrieve(completionHandler: (Foundation.URL) -> (), errorHandler: (Swift.String) -> ()) -> () at <compiler-generated>, self=0x0000000102225ce0) at FeedParser.swift:118:25
    frame #9: 0x00000001000a29c5 RSS2PGGatherer`partial apply for closure #1 in FeedParser.parseAsync(queue:result:) at <compiler-generated>:0
    frame #10: 0x000000010004816d RSS2PGGatherer`thunk for @escaping @callee_guaranteed () -> () at <compiler-generated>:0
    frame #11: 0x00000001009dbe7c libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #12: 0x00000001009dcf1b libdispatch.dylib`_dispatch_client_callout + 8
    frame #13: 0x00000001009efa06 libdispatch.dylib`_dispatch_root_queue_drain + 816
    frame #14: 0x00000001009f02da libdispatch.dylib`_dispatch_worker_thread2 + 125
    frame #15: 0x0000000100a560b7 libsystem_pthread.dylib`_pthread_wqthread + 583
    frame #16: 0x0000000100a55e01 libsystem_pthread.dylib`start_wqthread + 13

XMLFeedParser line 88.

(lldb) po self.rssFeed
nil

(lldb) po self.atomFeed
nil

(lldb) po self.data
▿ Optional<Data>
  ▿ some : 21 bytes
    - count : 21
    ▿ pointer : 0x00000001195550e0
      - pointerValue : 4719988960
    ▿ bytes : 21 elements
      - 0 : 60
      - 1 : 114
      - 2 : 115
      - 3 : 115
      - 4 : 32
      - 5 : 118
      - 6 : 101
      - 7 : 114
      - 8 : 115
      - 9 : 105
      - 10 : 111
      - 11 : 110
      - 12 : 61
      - 13 : 34
      - 14 : 50
      - 15 : 46
      - 16 : 48
      - 17 : 34
      - 18 : 32
      - 19 : 47
      - 20 : 62
(lldb) po String(data: self.data!, encoding: .utf8)
▿ Optional<String>
  - some : "<rss version=\"2.0\" />"

FeedParser line 117

(lldb) po self.url
▿ Optional<URL>
  ▿ some : http://apps.shareholder.com/rss/rss.aspx?channels=274&companyid=NDAQ
    - _url : http://apps.shareholder.com/rss/rss.aspx?channels=274&companyid=NDAQ
neilt commented 5 years ago

The following code fixes the issue for me. Two potential problems with this code.

1) I did not delve into the error handling code to determine if it was appropriate to use the internal error or if it would have been better to define a new error.

2) The case for .atom appears to potentially have the same problem, but none of my current feeds revealed a problem so I left it alone.

diff --git a/Sources/FeedKit/Parser/XMLFeedParser.swift b/Sources/FeedKit/Parser/XMLFeedParser.swift
index 33b48fc..0597083 100644
--- a/Sources/FeedKit/Parser/XMLFeedParser.swift
+++ b/Sources/FeedKit/Parser/XMLFeedParser.swift
@@ -85,9 +85,15 @@ class XMLFeedParser: NSObject, XMLParserDelegate, FeedParserProtocol {

         switch feedType {
         case .atom: return Result.atom(self.atomFeed!)
-        case .rdf, .rss: return Result.rss(self.rssFeed!)
+        case .rdf, .rss:
+            if let rssFeed = self.rssFeed {
+                return Result.rss(rssFeed)
+            }
+            else {
+                return Result.failure(ParserError.internalError(reason: "No RSS Feed.").value)
+            }
         }
-        
+
     }

     /// Redirects characters found between XML elements to their proper model
nmdias commented 5 years ago

Hi, @neilt

I was unable to replicate, but safeguarding against a possible uninitializel model seems more than reasonable, so the force unwrap was removed and an appropriate result is now returned.

Thanks

neilt commented 5 years ago

Thanks.