Swift WKWebView doesn't get initialised

I'm trying to get a rich text editor for iOS in Swift working. I do however get the following error:


Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value


When running this piece of code:


public var text: String? {
    didSet {
        guard let text = text else { return }
        if editorView.isLoading {
            textToLoad = text
        } else {
            editorView.evaluateJavaScript("richeditor.insertText(\"\(text.htmlEscapeQuotes)\");", completionHandler: nil)
            placeholderLabel.isHidden = !text.htmlToPlainText.isEmpty
        }
    }
}


The error appears at line 04.

This is why I suppose that the editorView doesn't get initialised. It should however be as you can see in the init method:


private var editorView: WKWebView!
  public override init(frame: CGRect = .zero) {

      placeholderLabel.textColor = UIColor.lightGray.withAlphaComponent(0.65)

      guard let bundlePath = Bundle(for: type(of: self)).path(forResource: "Resources", ofType: "bundle"),
          let bundle = Bundle(path: bundlePath),
          let scriptPath = bundle.path(forResource: "RichTextEditor", ofType: "js"),
          let scriptContent = try? String(contentsOfFile: scriptPath, encoding: String.Encoding.utf8),
          let htmlPath = bundle.path(forResource: "RichTextEditor", ofType: "html"),
          let html = try? String(contentsOfFile: htmlPath, encoding: String.Encoding.utf8)
          else { fatalError("Unable to find javscript/html for text editor") }

      let configuration = WKWebViewConfiguration()
      configuration.userContentController.addUserScript(
          WKUserScript(source: scriptContent,
                      injectionTime: .atDocumentEnd,
                      forMainFrameOnly: true
          )
      )

      editorView = WKWebView(frame: .zero, configuration: configuration)





      super.init(frame: frame)

      [RichTextEditor.textDidChange, RichTextEditor.heightDidChange].forEach {
          configuration.userContentController.add(WeakScriptMessageHandler(delegate: self), name: $0)
      }

      editorView.navigationDelegate = self
      ...
      editorView.scrollView.delegate = self

      addSubview(placeholderLabel)




      ...

      addSubview(editorView)
      editorView.translatesAutoresizingMaskIntoConstraints = false
      NSLayoutConstraint.activate([...])

      editorView.loadHTMLString(html, baseURL: nil)
  }



The class in which this happens has the following signature:



public class RichTextEditor: UIView, WKScriptMessageHandler, WKNavigationDelegate, UIScrollViewDelegate {



Any input on how I can fix this issue is welcome! Thanks.

Accepted Reply

UIView
has two designated initialisers:
  • init(frame:)
    is called when you instantiate the view programmatically
  • init?(coder:)
    is called when the view is instantiated from a nib (or storyboard)

If you need to customise initialisation then you would typically override both. For example:

class MyView : UIView {

    override init(frame: CGRect) {
        self.greeting = "Hello Cruel World!"
        super.init(frame: frame)
    }

    required init?(coder decoder: NSCoder) {
        self.greeting = "Hello Cruel World!"
        super.init(coder: decoder)
    }

    let greeting: String
}

If that’s not necessary — for example, if you know that this view will only be instantiated one way and not the other — you can have one of the overrides just call

fatalError
. For example:
class MyView : UIView {

    override init(frame: CGRect) {
        fatalError()
    }

    required init?(coder decoder: NSCoder) {
        self.greeting = "Hello Cruel World!"
        super.init(coder: decoder)
    }

    let greeting: String
}

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Are you sure your initialiser is running? For example, if you load

RichTextEditor
from a nib or a storyboard,
init(frame:)
doesn’t run but instead
init?(coder:)
. So set a breakpoint on the line that sets up
editorView
and see if it’s actually called.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for you answer! I discovered that there's another init() method that gets called (first):


    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }


I tried to put a breakpoint on the editorView setup line, and it indeed doesn't get called, so it seems like the init that we need doesn't get called. When I remove the above 'required' initialiser, it gives me an error. (''required' initializer 'init(coder:)' must be provided by subclass of 'UIView'") Any idea on how I can fix this? Thanks!

UIView
has two designated initialisers:
  • init(frame:)
    is called when you instantiate the view programmatically
  • init?(coder:)
    is called when the view is instantiated from a nib (or storyboard)

If you need to customise initialisation then you would typically override both. For example:

class MyView : UIView {

    override init(frame: CGRect) {
        self.greeting = "Hello Cruel World!"
        super.init(frame: frame)
    }

    required init?(coder decoder: NSCoder) {
        self.greeting = "Hello Cruel World!"
        super.init(coder: decoder)
    }

    let greeting: String
}

If that’s not necessary — for example, if you know that this view will only be instantiated one way and not the other — you can have one of the overrides just call

fatalError
. For example:
class MyView : UIView {

    override init(frame: CGRect) {
        fatalError()
    }

    required init?(coder decoder: NSCoder) {
        self.greeting = "Hello Cruel World!"
        super.init(coder: decoder)
    }

    let greeting: String
}

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for your answer! I've been in touch in the meantime with the developer of the code and he told me that the code was not written for use with storyboards. I fixed the issue by just moving all the code from

override init(frame: CGRect)

to

required init?(coder decoder: NSCoder)


Thanks again for your support and have a nice day!