For iOS 13:
Use the .shadowColor property
If this property is nil or contains the clear color, the bar displays no shadow
For instance:
let navigationBar = navigationController?.navigationBar
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.shadowColor = .clear
navigationBar?.scrollEdgeAppearance = navigationBarAppearance
For iOS 12 and below:
To do this, you should set a custom shadow image. But for the shadow image to be shown you also need to set a custom background image, quote from Apple's documentation:
For a custom shadow image to be shown, a custom background image must
also be set with the setBackgroundImage(_:for:) method. If the default
background image is used, then the default shadow image will be used
regardless of the value of this property.
So:
let navigationBar = navigationController!.navigationBar
navigationBar.setBackgroundImage(#imageLiteral(resourceName: "BarBackground"),
                                                        for: .default)
navigationBar.shadowImage = UIImage()
Above is the only "official" way to hide it. Unfortunately, it removes bar's translucency.
I don't want background image, just color##
You have those options:
- Solid color, no translucency: -  navigationBar.barTintColor = UIColor.redColor()
 navigationBar.isTranslucent = false
 navigationBar.setBackgroundImage(UIImage(), for: .default)
 navigationBar.shadowImage = UIImage()
 
- Create small background image filled with color and use it. 
- Use 'hacky' method described below. It will also keep bar translucent. 
How to keep bar translucent?##
To keep translucency you need another approach, it looks like a hack but works well. The shadow we're trying to remove is a hairline UIImageView somewhere under UINavigationBar. We can find it and hide/show it when needed.
Instructions below assume you need hairline hidden only in one controller of your UINavigationController hierarchy.
- Declare instance variable: - private var shadowImageView: UIImageView?
 
- Add method which finds this shadow (hairline) - UIImageView:
 - private func findShadowImage(under view: UIView) -> UIImageView? {
    if view is UIImageView && view.bounds.size.height <= 1 {
        return (view as! UIImageView)
    }
    for subview in view.subviews {
        if let imageView = findShadowImage(under: subview) {
            return imageView
        }
    }
    return nil
}
 
- Add/edit - viewWillAppear/viewWillDisappearmethods:
 - override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if shadowImageView == nil {
        shadowImageView = findShadowImage(under: navigationController!.navigationBar)
    }
    shadowImageView?.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    shadowImageView?.isHidden = false
}
 
The same method should also work for UISearchBar hairline,
and (almost) anything else you need to hide :)
Many thanks to @Leo Natan for the original idea!