How to import a private framework in Xcode 8.3 wit

2019-02-07 14:31发布

问题:

I am trying to use _CDBatterySaver to turn on low power mode simply using

    [[_CDBatterySaver batterySaver] setMode:1];

I know there isn't the same type of directory as previous Xcode so those methods don't work.

I have also tried just importing the ".h" file but that doesn't work.

It is part of the CoreDuet framework (downloaded from GitHub)

Thanks

回答1:

You will need a .tbd file to link against.

Apple stopped shipping these for private frameworks starting with the iOS 9.3 SDK, but you can generate them yourself with a bit of effort:

  1. Get an IPSW for some recent iOS version (ipsw.me or the iPhone wiki have nice lists).
  2. Unzip the IPSW and mount the root file system (the .dmg that is multiple GB).
  3. Find the shared library cache for your architecture in /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm*.
  4. Using one of various tools (I prefer jtool), extract CoreDuet from it, with e.g.:

    jtool -e CoreDuet /path/to/dyld_shared_cache
    
  5. Optional: Do step 4 for multiple architectures, then combine the extracted files to a fat binary:

    lipo -output CoreDuet dyld_shared_cache_*.CoreDuet
    
  6. Using either machotbd or my own tool, tbdumpnotes, create a .tbd file from the library/framework you just extracted, e.g.:

    tbdump CoreDuet > CoreDuet.tbd
    

    notes 1. I wrote it, so I'm obviously affiliated.
            2. It's a beta, and it's currently a bit over-zealous with symbols, printing even
                the ones it shouldn't - but so far it has always worked fine for me.

  7. Create a folder structure like so:

    some_path/
        CoreDuet.framework/
            CoreDuet.tbd
    
  8. Either add CoreDuet.framework to your frameworks in XCode, or use these compiler flags:

    -Fsome_path -framework CoreDuet
    
  9. Profit.

Also, if steps 1-6 are too troublesome for you and you merely need the _CDBatterySaver symbol, then you can just use this for your CoreDuet.tbd and be done with it:

---
archs:           [ armv7, armv7s, arm64 ]
platform:        ios
install-name:    /System/Library/PrivateFrameworks/CoreDuet.framework/CoreDuet
exports:
  - archs:           [ armv7, armv7s, arm64 ]
    objc-classes:    [ __CDBatterySaver ]
...


回答2:

Based on Siguza answer

Newer versions of Xcode uses the tbd v2 format. Here is a .tbd stub file that will compile with the latest version of Xcode.

--- !tapi-tbd-v2
archs:           [ armv7, armv7s, arm64 ]
uuids:           [ 'armv7: CBD84526-2D11-4CF3-83A7-4408DA532D3E', 'armv7s: 37ACF720-FA66-4B2D-8411-09D0B607E1A0',
                   'arm64: 19F91B79-9FA2-4944-9E68-3E632C938AE3' ]
platform:        ios
install-name:    /System/Library/PrivateFrameworks/CoreDuet.framework/CoreDuet
objc-constraint: none
exports:
  - archs:           [ armv7, armv7s, arm64 ]
    objc-classes:    [ __CDBatterySaver ]
    objc-ivars:      [ __CDBatterySaver._connection ]
...


回答3:

There's an official tool to generate .tbd files these days, tapi (Text-based API). Not sure when it was released exactly, but it's been around at least as long as Xcode 9.

To generate a .tbd for a binary, you do something like the following. Here I'm using the private SafariShared.framework on OSX. If you need a .tbd for an iOS framework, you'll have to follow the beginning of Siguza's answer to get ahold of a binary, but otherwise, the rest of this process will work.

  1. Make an empty directory for the framework stub named, e.g., "SafariShared.framework":

    $ md SafariShared.framework
    
  2. Run a command like the following to generate the stub:

    $ xcrun tapi stubify -o SafariShared.framework/SafariShared.tbd /System/Library/PrivateFrameworks/SafariShared.framework/SafariShared
    

    The -o argument is the output file and the final argument is the path to the binary (note that it is not the path to the .framework itself; you'll get an error if you do that). Replace both as needed.

  3. There's one potential pitfall. Stub files can contain a list of the programs that are allowed to link with that binary, and if your program isn't listed in that list, the linker will fail. Open up the .tbd in your favourite text editor and look for lines like the following:

    allowable-clients: [ <a bunch of bundle IDs>, ... ]
    

    Delete all of those (there may not be any) and save the file.

Now you're good to go! The stub .framework directory you made in step 1 will work just like a real framework in Xcode. In your Xcode project's general settings, you can click the plus in "Linked Frameworks and Libraries" and add the stub directory.

(You may also have to add the directory containing the stub .framework to the "Framework Search Paths" project setting. It seems flaky as to whether or not that will happen automatically.)