Pranay
Pranay

Reputation: 486

Is it possible to load a nib file from a swift package?

I have created an UICollectionView Xib file and class file as a swift package. The methods in class file are accessible but I'm getting an error while loading the nib file in my project.

Link to the package: https://github.com/PranayChander/package1

Link to project: https://github.com/PranayChander/packman

import UIKit import package1

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.register(UINib(nibName: "PackageCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "cell")
    }
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! PackageCollectionViewCell
        cell.configureCell(title: "Pram", subtitle: "dasd")
        return cell
    }
}


import UIKit

open class PackageCollectionViewCell: UICollectionViewCell {
    @IBOutlet open weak var titleLabel: UILabel!
    @IBOutlet open weak var subtitleLabel: UILabel!
    
    open override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    open func configureCell(title: String, subtitle: String) {
        self.titleLabel.text = title
        self.subtitleLabel.text = subtitle
    }
}

Stach Trace: 2020-12-21 14:20:37.806065+0530 packman[4341:195092] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </Users/pranaychander/Library/Developer/CoreSimulator/Devices/B98131CA-B121-42B2-A0F5-FC48066C3179/data/Containers/Bundle/Application/95A42061-D137-4F35-94CE-58B8915189C6/packman.app> (loaded)' with name 'PackageCollectionViewCell'' *** First throw call stack: ( 0 CoreFoundation 0x00007fff20420af6 __exceptionPreprocess + 242 1 libobjc.A.dylib 0x00007fff20177e78 objc_exception_throw + 48 2 CoreFoundation 0x00007fff204209d4 -[NSException initWithCoder:] + 0 3 UIKitCore 0x00007fff24290813 -[UINib instantiateWithOwner:options:] + 495 4 UIKitCore 0x00007fff23d864db -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 907 5 UIKitCore 0x00007fff23d86bdb -[UICollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath:] + 88 6 packman 0x000000010e766d19 $s7packman14ViewControllerC010collectionB0_13cellForItemAtSo012UICollectionB4CellCSo0iB0C_10Foundation9IndexPathVtF + 313 7 packman 0x000000010e766ef5 $s7packman14ViewControllerC010collectionB0_13cellForItemAtSo012UICollectionB4CellCSo0iB0C_10Foundation9IndexPathVtFTo + 165 8 UIKitCore 0x00007fff23d71546 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:notify:] + 410 9 UIKitCore 0x00007fff23d713a6 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 31 10 UIKitCore 0x00007fff23d769cc -[UICollectionView _updateVisibleCellsNow:] + 6148 11 UIKitCore 0x00007fff23d7bc48 -[UICollectionView layoutSubviews] + 351 12 UIKitCore 0x00007fff24bf25b8 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2924 13 QuartzCore 0x00007fff27aa2c33 -[CALayer layoutSublayers] + 258 14 QuartzCore 0x00007fff27aa91a5 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 575 15 QuartzCore 0x00007fff27ab4f47 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 65 16 QuartzCore 0x00007fff279f4408 _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 496 17 QuartzCore 0x00007fff27a2b1ef _ZN2CA11Transaction6commitEv + 783 18 UIKitCore 0x00007fff246ae47e __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 81 19 CoreFoundation 0x00007fff2038f120 CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK + 12 20 CoreFoundation 0x00007fff2038e534 __CFRunLoopDoBlocks + 434 21 CoreFoundation 0x00007fff20388f44 __CFRunLoopRun + 899 22 CoreFoundation 0x00007fff203886d6 CFRunLoopRunSpecific + 567 23 GraphicsServices 0x00007fff2bededb3 GSEventRunModal + 139 24 UIKitCore 0x00007fff24690e0b -[UIApplication _run] + 912 25 UIKitCore 0x00007fff24695cbc UIApplicationMain + 101 26 libswiftUIKit.dylib 0x00007fff54d1e5f2 $s5UIKit17UIApplicationMainys5Int32VAD_SpySpys4Int8VGGSgSSSgAJtF + 98 27 packman 0x000000010e7677da $sSo21UIApplicationDelegateP5UIKitE4mainyyFZ + 122 28 packman 0x000000010e76774e $s7packman11AppDelegateC5$mainyyFZ + 46 29 packman 0x000000010e767829 main + 41 30 libdyld.dylib 0x00007fff202593e9 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </Users/pranaychander/Library/Developer/CoreSimulator/Devices/B98131CA-B121-42B2-A0F5-FC48066C3179/data/Containers/Bundle/Application/95A42061-D137-4F35-94CE-58B8915189C6/packman.app> (loaded)' with name 'PackageCollectionViewCell'' terminating with uncaught exception of type NSException CoreSimulator 732.18.6 - Device: iPhone 11 Pro (B98131CA-B121-42B2-A0F5-FC48066C3179) - Runtime: iOS 14.3 (18C61) - DeviceType: iPhone 11 Pro

enter image description here

Upvotes: 5

Views: 4053

Answers (4)

Trevor
Trevor

Reputation: 1144

  1. As others already mentioned, the bundle needs to be specified as .module. If you are accessing this from outside of the spm library a little extension on Bundle might be helpful.
extension Bundle {
    public static let blah = Bundle.module
}
  1. The nib needs to be updated to deselect Inherits Module From Target and manually select the module where the view is located.

enter image description here

Upvotes: 10

Carl Burnham
Carl Burnham

Reputation: 43

If you are strictly releasing your framework for SPM then you can use the Bundle.module listed above as recommended. But, if you are like me and others and still need to support Pods, etc. I pulled in the Bundle.module to my framework and used MyFramework.module as my Bundle.

public class MyFramework: NSObject {
    static let bundleName = "MyFramework"

    public static let bundle = Bundle(for: Self.self)!

    /// Returns the resource bundle associated with the current Swift module. This is required for SPM use
    public static var module: Bundle = {
        let bundleName = "MyFramework" // May be "MyFramework_MyFramework" for you

        let candidates = [
            // Bundle should be present here when the package is linked into an App.
            Bundle.main.resourceURL,

            // Bundle should be present here when the package is linked into a framework.
            Bundle(for: MyFramework.self).resourceURL,

            // For command-line tools.
            Bundle.main.bundleURL
        ]

        for candidate in candidates {
            let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
            if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
                return bundle
            }
        }
        return MyFramework.bundle
    }()
}

Once you have that you can just use MyFramework.module

Upvotes: 1

heyfrank
heyfrank

Reputation: 5647

Apple says in the article Bundling Resources with a Swift Package

Always use Bundle.module when you access resources. A package shouldn’t make assumptions about the exact location of a resource.

So you should use this static variable as bundle parameter.

Upvotes: 7

shallowThought
shallowThought

Reputation: 19602

Your code looks in the apps bundle for the NIB and can not find it (as it is in the frameworks bundle):

Could not load NIB in bundle: 'NSBundle </Users/pranaychander/Library/Developer/CoreSimulator/Devices/B98131CA-B121-42B2-A0F5-FC48066C3179/data/Containers/Bundle/Application/95A42061-D137-4F35-94CE-58B8915189C6/packman.app>

Either use the identifier of the framework to get the correct bundle :

let bundle = Bundle(identifier: "com.bundleID.of.the.framework")
collectionView.register(UINib(nibName: "PackageCollectionViewCell", bundle: bundle)....

Or provide a factory function in the frameworks API to get the ready made view.

Upvotes: 3

Related Questions