API调用流程:①申请开通API调用权限
②获取API调用鉴权参数
③完成数据调用


请求参数
返回数据
示例代码
请求示例
{{req_example}}
返回示例
{{res_result}}
错误码
配置环境
1.在module的gradle中配置
ndk { abiFilters "armeabi","armeabi-v7a" }
2.添加依赖(版本号可以替换为高版本或者低版本,但是不能保证兼容性,替换第三方库版本号后,请务必认真测试)
compile(name: 'diandu-vx.x.x', ext: 'aar') compile(name: 'rjfoxit-vx.x.x', ext: 'aar') implementation 'com.android.support:recyclerview-v7:27.1.1' implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.liulishuo.okdownload:okdownload:1.0.4' implementation 'com.liulishuo.okdownload:sqlite:1.0.4' implementation 'com.liulishuo.okdownload:okhttp:1.0.4' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'org.greenrobot:eventbus:3.0.0' implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.google.code.gson:gson:2.6.2' implementation('jameson.io.library:library:0.0.1') { exclude group: "com.android.support", module: "support-v4" } implementation 'com.shuyu:gsyVideoPlayer-java:6.0.2' implementation 'com.rjsz.frame:xunfei:1.0.2' implementation 'com.github.bumptech.glide:glide:4.8.0'
代码混淆
-keepattributes *Annotation* -keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe methods; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } -keepnames class com.liulishuo.okdownload.core.connection.DownloadOkHttp3Connection -keep class com.liulishuo.okdownload.core.breakpoint.BreakpointStoreOnSQLite { public com.liulishuo.okdownload.core.breakpoint.DownloadStore createRemitSelf(); public com.liulishuo.okdownload.core.breakpoint.BreakpointStoreOnSQLite(android.content.Context); } -dontwarn com.liulishuo.** -keep class com.liulishuo.okdownload.** {*;} -keep class com.rjsz.frame.download.**{*;} -keep class com.rjsz.frame.diandu.PRDownloaderManager{ public *; } -keep class com.rjsz.frame.diandu.PRSDKManager{ public *; } -keep class com.rjsz.frame.diandu.PRViewManager{ public *; } -keep class com.rjsz.frame.diandu.config.PRStateCode{ *; } -keep class com.rjsz.frame.diandu.callback.ReqCallBack{ *; } -keep class com.rjsz.booksdk.bean.* { *; } -keep class com.rjsz.frame.diandu.event.* { *; } -keep class com.rjsz.frame.diandu.bean.* { *; } -keep class com.rjsz.frame.diandu.webview.bean.* { *; } -keep class com.rjsz.frame.diandu.utils.PRFileUtil { *; } -keep class com.rjsz.frame.pepbook.bean.*{ *; } -keep class com.rjsz.frame.pepbook.config.*{ *; } -keep class com.rjsz.frame.pepbook.download.*{ *; } -keep class com.rjsz.frame.pepbook.event.*{ *; } -dontwarn com.edmodo.cropper.** -keep class com.edmodo.cropper.** {*;} -keep class com.foxit.sdk.** { *; } -keep class com.foxit.uiextensions.** { *; } -dontwarn com.foxit.sdk.** -dontwarn com.foxit.uiextensions.** -dontwarn com.iflytek.** -keep class com.iflytek.**{*;}
初始化
在程序的入口处(Application),调用如下方法: 1.PRSDKManager.getInstance().init(this, appKey); /** * @param context * @param appKey appKey分配的平台key 必填 */ public void init(this, appKey) 2. 在程序的入口处,调用如下方法: /** * @param context Application * @param appKey appKey分配的平台key 必填 * @param isShowCuntomBuyTip 是否自定义购买提示isShowCuntomBuyTip若为true 参见方法说明14,自定义用户提示交互 */ PRConfig config = new PRConfig.Builder() . setAppKey(appKey)// appKey分配的平台key 必填 . setIsShowCustomBuyTip (isShowCuntomBuyTip) //是否自定义购买提示 .build(); PRSDKManager.getInstance().init(context,config);
方法说明
1.同步订单接口
/** * 第三方用户鉴权、绑定设备. * 对接平台可在APP启动或用户订单状态发生变化时调用该方法 * @param userId 用户ID * @param reqCallBack 网络请求回调 * success网络请求成功后回调(包括当前用户购买的书本和绑定的设备列表) * fail网络请求失败后回调 */ PRSDKManager.getInstance().syncOrder(userId, ReqCallBack );
2.获取用户绑定设备列表
在同步订单接口成功的回调里回传的json里含有此用户的所有绑定设备列表。 或同步订单后通过此方法获得PRSDKManager.getInstance().getDevices()
3.解绑用户设备
/** * @param userId (用户id) * @param list(集合,里面放有设备id) 如List String list = new ArrayList<>(); list.add(dev_id_1); 解绑设备id为dev_id_1的设备 list.add(dev_id_2); 解绑设备id为dev_id_2的设备 * @param ReqCallBack (回调接口) */ PRSDKManager.getInstance().removeDevices(userId, list, ReqCallBack)
4.根据bookid获取书籍信息
/** * 根据book_id返回教材信息 */ PRSDKManager.getInstance().getBookItemById(userId, book_id, ReqCallBack)
5.书本关闭获取页码,所属单元,标题
@Subscribe(threadMode = ThreadMode.MAIN) public void getBookUnitAndTitle(CloseInfo info){}
6.书本关闭获取用户点击次数和时长
/** * 关闭课本时回调 * @param infos */ @Subscribe(threadMode = ThreadMode.MAIN) public void getTrackInfo(PlayTrackInfo clickReadInfo){}
7.获取书籍列表
/** * * @param userId 用户id */ PRSDKManager.getInstance().getAllBookList(userId, ReqCallBack)
8.打开课本
/** * * @param context (上下文) * @param textbooksBean (BookList.TextbooksBean对象) * @param boolean(是否为体验模式) * @param num;打开页码 只有已购买状态才成能成功打开该页码 */ PRViewManager.openBook(context, textbooksBean, boolean, int num); 或者 PRViewManager.openBook(context, textbooksBean, boolean);
9.下载课本
/** * * @param textbooksBean BookList.TextbooksBean对象) */ PRDownloaderManager.getInstance().download(textbooksBean); a)暂停下载 PRDownloaderManager.getInstance().pauseDownloadOfBookID(book_id); b)继续下载 PRDownloaderManager.getInstance().continueDownloadOfBookID(book_id); c)下载状态监听 @Subscribe(threadMode = ThreadMode.MAIN) public void downloadEvent(PRDownloadInfo downloadInfo) PRDownloadInfo包含的状态码: PRStateCode.STATE_ADD 6 开始下载 PRStateCode.STATE_ERROR 5 下载失败 PRStateCode.INTERFACE_ERROR 7 下载接口访问失败 PRStateCode.STATE_WAIT 1 暂停中 PRStateCode.STATE_DOWNLOAD 2 下载中 PRStateCode.STATE_SUCCESS 4 下载成功
10.删除课本
/** * * @param book_id (教材id) * @return true:删除成功 false:删除失败 */ boolean isDelete= PRDownloaderManager.getInstance().deleteBook(book_id);
11.获取缓存大小
/** * * @param context (上下文) * @return cacheSize (缓存大小 单位为字节) */ long cacheSize = PRSDKManager.getInstance().getCacheDirSize(context);
12.清除缓存
/** * * @param context (上下文) * @return isClean是否清除成功 */ boolean isClean = PRSDKManager.getInstance().deleteCacheDir(context);
13.用户点击前往购买的回调
/** * @param event 购买回调 */ @Subscribe(threadMode = ThreadMode.MAIN) public void handleSdkEvent(SdkEvent event) { if (event.eventType == SdkEvent.EVENT_EXPERIENCE_END) { //跳转购买页面 } }
14.自定义交互提示用户购买或激活
/** * @param event 提示购买回调 */ @Subscribe(threadMode = ThreadMode.MAIN) public void handleSdkEvent(SdkEvent event) { if (event.eventType == EVENT_SHOW_BUY_TIP) { Context mContext=event.activity; //根据mContext自定义展示方式 } }
15.判断书本是否已经下载
isDownloaded= PRDownloaderManager.getInstance().isDownloaded(book_id);
16.判断书本是否有更新
/** * * @param textbooksBean (BookList.TextbooksBean对象) * @return true:有更新 false:没有更新 */ hasUpdate= PRDownloaderManager.getInstance().hasUpdate(textbooksBean)
17.获取书本状态(是否已经订购)
hasBuy=PRSDKManager.getInstance().hasBuy(bookid)
18.获取目录
PRSDKManager.getInstance().getBookCatalogById(book_id,ReqCallBack)
19.自定义图片加载
设置图片加载器 /** * * @ param proxy 自定义图片加载器 继承BaseImageLoadProxy */ com.rjsz.frame.diandu.utils.ImageLoaderUtil.getInstance().setLoadImgProxy(BaseImageLoadProxy proxy)
20.自定义测评引擎
a)初始化时设置自定义测评引擎 new PRConfig.Builder().useCustomEvaluate(true) b)打开教材前设置语音测评引擎 /** * @ param thread自定义语音测评引擎 继承BaseEvaluateThread * 测评结果通过EvaluateListener listener 回调 */ com.rjsz.frame.diandu.thread.EvaluateManager.getInstance().setEvaluateThread(thread) c)BaseEvaluateThread: /** * 开始测评 * @param textStr 句子原文 * @param evaStr 讯飞中测评语句 * @param audioPath 测评中生成录音文件(需要MP3格式&根据路径存放在对应位置) */ void startVoiceTest(String textStr, String evaStr, String audioPath); /** * 结束测评 解析测评结果 需要回调listener.onResult()方法返回结果 * 或回调 listener.onError() */ void parseResult(); /** * @param listener 测评回调 通过该listener回调测评过程中的各种事件 */ void setListener(EvaluateListener listener); d)EvaluateListener:测评回调类 /** * @param bean 返回测评结果 */ void onResult(PREvaluateResult bean); /** * 测评报错 * @param errCode 错误码 * @param errStr 错误信息 */ void onError(int errCode,String errStr); /** * 录入音频大小 * @param i 范围[0-30] */ void onVolume(int i); e)PREvaluateResult:用于在句子中标记低分单词 Score:句子总得分List EvaluateScoreResult scoreResults;句子中每个词对应测评结果 f)EvaluateScoreResult score:当前单词得分 五分制(满分五分) word:当前单词内容 startIndex:当前单词在整句原文中的开始位置
配置环境
1.在 module 的 gradle 中配置
ndk { abiFilters "armeabi-v7a" }
2.添加依赖(版本号可以替换为高版本或者低版本,替换库版本号后,请务必认真测试)
compile(name: 'diandu-v2.1.0', ext: 'aar') compile(name: 'rjfoxit-v7.3.0', ext: 'aar') compile(name: 'xunfei-v1.0.2', ext: 'aar') androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' implementation 'androidx.multidex:multidex:2.0.0' implementation 'androidx.appcompat:appcompat:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.google.code.gson:gson:2.8.2' implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.liulishuo.okdownload:okdownload:1.0.7' implementation 'com.liulishuo.okdownload:sqlite:1.0.7' implementation 'com.liulishuo.okdownload:okhttp:1.0.7' implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' //2.1.0视频切片播放支持 implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.1.3-jitpack' implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:v8.1.3-jitpack' implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-armv7a:v8.1.3-jitpack' //2.1.0数学教材多级目录支持 implementation 'com.yanzhenjie.recyclerview:support:1.3.2' //2.1.0数学教材数学公式支持 implementation 'com.gitee.geduo_83:android_rich_textview:1.0.3' implementation 'com.github.softwee:codeview-android:1.2.0' //2.1.0 网络请求RxJava相关支持 implementation 'io.reactivex.rxjava2:rxjava:2.2.2' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.0.1'
代码混淆
-keepattributes *Annotation* -keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe methods; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } -keepnames class com.liulishuo.okdownload.core.connection.DownloadOkHttp3Connection -keep class com.liulishuo.okdownload.core.breakpoint.BreakpointStoreOnSQLite { public com.liulishuo.okdownload.core.breakpoint.DownloadStore createRemitSelf(); public com.liulishuo.okdownload.core.breakpoint.BreakpointStoreOnSQLite(android.content.Context); } -dontwarn com.liulishuo.** -keep class com.liulishuo.okdownload.** {*;} -keep class com.rjsz.frame.download.**{*;} -keep class com.rjsz.frame.diandu.PRDownloaderManager{ public *; } -keep class com.rjsz.frame.diandu.PRSDKManager{ public *; } -keep class com.rjsz.frame.diandu.PRViewManager{ public *; } -keep class com.rjsz.frame.diandu.config.PRStateCode{ *; } -keep class com.rjsz.frame.diandu.callback.ReqCallBack{ *; } -keep class com.rjsz.booksdk.bean.* { *; } -keep class com.rjsz.frame.diandu.event.* { *; } -keep class com.rjsz.frame.diandu.bean.* { *; } -keep class com.rjsz.frame.diandu.webview.bean.* { *; } -keep class com.rjsz.frame.diandu.utils.PRFileUtil { *; } -keep class com.rjsz.frame.pepbook.bean.*{ *; } -keep class com.rjsz.frame.pepbook.config.*{ *; } -keep class com.rjsz.frame.pepbook.download.*{ *; } -keep class com.rjsz.frame.pepbook.event.*{ *; } -dontwarn com.edmodo.cropper.** -keep class com.edmodo.cropper.** {*;} -keep class com.foxit.sdk.** { *; } -keep class com.foxit.uiextensions.** { *; } -dontwarn com.foxit.sdk.** -dontwarn com.foxit.uiextensions.** -dontwarn com.iflytek.** -keep class com.iflytek.**{*;}
初始化
在程序的入口处(Application),调用如下方法: 1. 在程序的入口处,调用如下方法: /** *@param context Application *@param appKey appKey 分配的平台 key 必填 *@param isShowCuntomBuyTip 是否自定义购买提示 isShowCuntomBuyTip 若为 true 参见方法说明 *14,自定义用户提示交互 * */ PRConfig config = new PRConfig.Builder() . setAppKey(appKey)// appKey 分配的平台 key 必填 . setIsShowCustomBuyTip (isShowCuntomBuyTip) //是否自定义购买提示 .useCustomEvaluate(true)//是否使用自定义语音测评引擎默认使用讯飞 .build(); PRSDKManager.getInstance().init(context,config);
方法说明
1.同步订单接口
/** *第三方用户鉴权、绑定设备. *对接平台可在 APP 启动或用户订单状态发生变化时调用该方法 *@param userId 用户 ID *@param reqCallBack 网络请求回调 *success 网络请求成功后回调(包括当前用户购买的书本和绑定的 *设备列表) *fail 网络请求失败后回调 */ PRSDKManager.getInstance().syncOrder(userId, ReqCallBack );
2.获取用户绑定设备列表
在同步订单接口成功的回调里回传的 json 里含有此用户的所有绑定设备列表。
3.解绑用户设备
/** *@param userId (用户 id) *@param list(集合,里面放有设备 id) *如 List String list = new ArrayList<>(); *list.add(dev_id_1); 解绑设备 id 为 dev_id_1 的设备 *list.add(dev_id_2); 解绑设备 id 为 dev_id_2 的设备 *@param ReqCallBack (回调接口返回状态) */ PRSDKManager.getInstance().removeDevices(userId, list, ReqCallBack)
4.根据 bookid 获取书籍信息
/** *根据 book_id 返回教材信息 * */ PRSDKManager.getInstance().getBookItemById(userId, book_id, ReqCallBack)
5.书本关闭获取页码,所属单元,标题
@Subscribe(threadMode = ThreadMode.MAIN) public void getBookUnitAndTitle(CloseInfo info){}
6.书本关闭获取用户点击次数和时长
/** *关闭课本时回调 *@param infos */ @Subscribe(threadMode = ThreadMode.MAIN) public void getTrackInfo(PlayTrackInfo clickReadInfo){}
7.获取书籍列表
/** *@param userId 用户 id */ PRSDKManager.getInstance().getAllBookList(userId, ReqCallBack)
8.打开课本
/** *@param context (上下文) *@param textbooksBean (BookList.TextbooksBean 对象) *@param boolean(是否为体验模式) *@param num;打开页码 只有已购买状态才成能成功打开该页码 */ PRViewManager.openBook(context, textbooksBean, boolean, int num); 或者 PRViewManager.openBook(context, textbooksBean, boolean);
9.下载课本
/** *@param textbooksBean BookList.TextbooksBean 对象) */ PRDownloaderManager.getInstance().download(textbooksBean); a)暂停下载 PRDownloaderManager.getInstance().pauseDownloadOfBookID(book_id); b)继续下载 PRDownloaderManager.getInstance().continueDownloadOfBookID(book_id); c)下载状态监听 @Subscribe(threadMode = ThreadMode.MAIN) public void downloadEvent(PRDownloadInfo downloadInfo) PRDownloadInfo 包含的状态码: PRStateCode.STATE_ADD 6 开始下载 PRStateCode.STATE_ERROR 5 下载失败 PRStateCode.INTERFACE_ERROR 7 下载接口访问失败 PRStateCode.STATE_WAIT 1 暂停中 PRStateCode.STATE_DOWNLOAD 2 下载中 PRStateCode.STATE_SUCCESS 4 下载成功
10.删除课本
/** *@param book_id (教材 id) *@return true:删除成功 false:删除失败 */ boolean isDelete= PRDownloaderManager.getInstance().deleteBook(book_id);
11.获取缓存大小
/** *@param context (上下文) *@return cacheSize (缓存大小 单位为字节) */ long cacheSize = PRSDKManager.getInstance().getCacheDirSize(context);
12.清除缓存
/** *@param context (上下文) *@return isClean 是否清除成功 */ boolean isClean = PRSDKManager.getInstance().deleteCacheDir(context);
13.用户点击前往购买的回调
/** *@param event 购买回调 */ @Subscribe(threadMode = ThreadMode.MAIN) public void handleSdkEvent(SdkEvent event) { if (event.eventType == SdkEvent.EVENT_EXPERIENCE_END) { //跳转购买页面 } }
14.自定义交互提示用户购买或激活
初始化时设置自定义购买弹框 new PRConfig.Builder(). setIsShowCustomBuyTip (true) /** *@param event 提示购买回调 */ @Subscribe(threadMode = ThreadMode.MAIN) public void handleSdkEvent(SdkEvent event) { if (event.eventType == EVENT_SHOW_BUY_TIP) { Context mContext=event.activity; //根据 mContext 自定义展示方式 } }
15.判断书本是否已经下载
isDownloaded= PRDownloaderManager.getInstance().isDownloaded(book_id);
16.判断书本是否有更新
/** *@param textbooksBean (BookList.TextbooksBean 对象) *@return true:有更新 false:没有更新 */ hasUpdate= PRDownloaderManager.getInstance().hasUpdate(textbooksBean)
17.获取书本状态(是否已经订购)
hasBuy=PRSDKManager.getInstance().hasBuy(bookid)
18.获取目录
PRSDKManager.getInstance().getBookCatalogById(book_id,ReqCallBack)
19.自定义图片加载
设置图片加载器 /** * @ param proxy 自定义图片加载器 继承 BaseImageLoadProxy */ com.rjsz.frame.diandu.utils.ImageLoaderUtil.getInstance().setLoadImgProxy(proxy)
20.自定义测评引擎
a)初始化时设置自定义测评引擎 new PRConfig.Builder().useCustomEvaluate(true) b)打开教材前设置语音测评引擎 /** *@ param thread 自定义语音测评引擎 继承 BaseEvaluateThread *测评结果通过 EvaluateListener listener 回调 */ com.rjsz.frame.diandu.thread.EvaluateManager.getInstance().setEvaluateThread(thread) c)BaseEvaluateThread: /** *开始测评 *@param textStr 句子原文 *@param evaStr 讯飞中测评语句 *@param audioPath 测评中生成录音文件(需要 MP3 格式&根据路径存放在对应位置) */ void startVoiceTest(String textStr, String evaStr, String audioPath); /** *结束测评 解析测评结果 需要回调 listener.onResult()方法返回结果 *或回调 listener.onError() */ void parseResult(); /** *@param listener 测评回调 通过该 listener 回调测评过程中的各种事件 */ void setListener(EvaluateListener listener); d)EvaluateListener:测评回调类 /** *@param bean 返回测评结果 */ void onResult(PREvaluateResult bean); /** *测评报错 *@param errCode 错误码 *@param errStr 错误信息 */ void onError(int errCode,String errStr); /** *录入音频大小 *@param i 范围[0-30] */ void onVolume(int i); e)PREvaluateResult:用于在句子中标记低分单词 Score:句子总得分 List EvaluateScoreResult scoreResults;句子中每个词对应测评结果 f)EvaluateScoreResult score:当前单词得分 五分制(满分五分) word:当前单词内容 startIndex:当前单词在整句原文中的开始位置
系统要求
由于依赖的福昕RDK已经升级到iOS10.0+,故人教点读SDK兼容的最低版本为iOS 10.0
使用说明
1.Cocoapods
在podfile中添加如下代码,然后pod install: pod 'PEPNetworking', :git => 'https://github.com/PEPDigitalPublishing/PEPNetworking.git' pod 'PEPBigData', :git => 'https://github.com/PEPDigitalPublishing/PEPBigData.git' pod 'PEPReaderSDK', :git => 'https://github.com/PEPDigitalPublishing/PEPReaderSDK.git' # 讯飞语音SDK pod 'PEPiFlyMSC', :git => 'https://github.com/PEPDigitalPublishing/PEPiFlyMSC.git' pod 'SSZipArchive' pod 'iosMath' 指定版本号 podfile中可以通过如下方式指定版本号: pod 'PEPReaderSDK', { :git => 'https://github.com/PEPDigitalPublishing/PEPReaderSDK.git', :tag => '2.0.0' }
2.手动接入
1.从Releases中下载指定版本,然后将其中的PEPReaderSDK.framework和PDFReaderSDKDiandu.xcassets导入项目中。 2.查看PEPReaderSDK.podspec中声明的依赖库,并保证这些依赖库也同样导入到项目中
FAQ
1. Include of non-modular header inside framework module xxxxxx 在项目的Build Setting中搜索“allow non-modular”,将出现的栏目中的“No”改为“Yes” 2. 福昕RDK的导入位置 FoxitRDK.framework是动态库,应导入到Embedded Binaries中,而不是Linked Frameworks And Libraries中 3. App打包上传App Store时报错(ERROR ITMS-90080、90087、90209、90125) 由于FoxitRDK.framework合并了真机和模拟器编译出的二进制文件,即在真机和模拟器上都能运行,且是动态库,以嵌入的方式(Embedded Binaries)导入项目的,故而Xcode在打包时无法自动去除x86_64和i386的二进制文件,需要增加脚本。 解决方案: TARGETS -> Build Phases -> 点击左上角加号选择New Run Script Phase-> 然后复制粘贴下面代码: APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}" # This script loops through the frameworks embedded in the application and # removes unused architectures. find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK do FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable) FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME" echo "Executable is $FRAMEWORK_EXECUTABLE_PATH" EXTRACTED_ARCHS=() for ARCH in $ARCHS do echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME" lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH" EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH") done echo "Merging extracted architectures: ${ARCHS}" lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}" rm "${EXTRACTED_ARCHS[@]}" echo "Replacing original executable with thinned version" rm "$FRAMEWORK_EXECUTABLE_PATH" mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH" done