- iOS 13.0 or later.
- Xcode 12.0 or later and Swift 5.0 or later.
- Objective-C and Swift projects are supported.
- Device
arm64and simulatorx86_64/arm64architectures are supported. - Broker App is normally portrait-only. When a specific SDK page requests landscape, forward the orientation mask as described below.
LBWhaleAppSDK provides two integration paths for iOS developers:
- CocoaPods (recommended)
- Manual integration
Extract LBWhaleAppSDK-XXX.zip, place the SDK directory in the project, and add:
source 'https://github.com/volcengine/volcengine-specs.git'
source 'https://github.com/aliyun/aliyun-specs.git'
pod 'LBWhaleAppSDK', :path => './LBWhaleAppSDK'Then run pod install and open the generated workspace.
- Extract
LBWhaleAppSDK-XXX.zip. - Drag
LBWhaleAppSDK.xcframeworkinto the Xcode project. - In General → Frameworks, Libraries, and Embedded Content, select Embed & Sign.
- Add all system frameworks listed in the delivered package to Build Phases → Link Binary With Libraries.
Import the umbrella header in code:
#import <LBWhaleAppSDK/LBWhaleApp.h>Swift projects should import it from the bridging header instead.
Initialize the SDK before opening any SDK page.
LBWhaleAppConfigMultilingual *appName =
[[LBWhaleAppConfigMultilingual alloc] initWithEn:@"My Trading App"
zhCN:@"我的交易应用"
zhHK:@"我的交易應用"];
LBWhaleAppConfig *config = [[LBWhaleAppConfig alloc]
initAppKey:@"<app-key>"
appSecret:@"<app-secret>"
appId:@"<app-id>"
appName:appName
defaultAccountChannel:@"<account-channel>"
webDomainPrefix:@"<web-domain-prefix>"
token:@"<client-token>"
refreshToken:@"<client-refresh-token>"
delegate:self
extraCustomConfig:nil];
config.theme = LBWhaleAppConfigThemeAuto;
config.language = LBWhaleAppConfigLanguageEn;
config.priceColorType = LBWhaleAppConfigPriceColorFollowUserSetting;
config.env = LBWhaleAppConfigEnvProd;
config.sdkUIDelegate = self;
[LBWhaleApp startWithConfig:config];Use only the environment and credentials delivered for the project. Do not hard-code real appSecret, token, or refresh token values in the repository.
All LBWhaleAppDelegate methods are optional. Implement only the callbacks the project needs, in SDK lifecycle order.
Callbacks that carry an NSError always use the domain @"LBWhaleApp". Common codes:
NSError.code |
Meaning | Handling |
|---|---|---|
400 |
Missing required configuration or duplicate start | Correct LBWhaleAppConfig and start again |
401 |
Unrecoverable authentication failure or state-dependent API used before startup | Destroy the SDK when needed and require Client sign-in |
403xxx |
Invalid API Key or project configuration | Verify the delivered project configuration with Whale |
The SDK checks and renews tokens internally; Broker App does not call check-token or refresh-token APIs.
- (void)lbWhaleAppDidFinishLaunching {
// SDK-dependent UI may now be opened.
}
- (void)lbWhaleAppStartFailedWithError:(NSError *)error {
// 400: invalid or incomplete integration configuration.
// 401 / 403xxx: authentication or API-key failure.
// The SDK destroys itself for unrecoverable startup authentication failure.
[MyErrorTracker trackSanitizedError:error];
if (error.code != 400) {
[self redirectToLoginPage];
}
}
- (void)lbWhaleAppTokenDidChange:(NSString *)token {
// Notification only. WhaleAppSDK has already renewed the session and
// continues to manage validity and subsequent renewal.
}
- (void)lbWhaleAppDidRunInError:(NSError *)error {
if (error.code == 401) {
[LBWhaleApp logoutAndDestroy];
[self redirectToLoginPage];
}
}
- (BOOL)lbWhaleAppSDKCanOpenURL:(NSString *)url {
// Return YES only when Broker App can route this URL.
return [MyAppRouter canOpenURL:url];
}
- (void)lbWhaleAppSDKDidRequestOpenURL:(NSString *)url {
// Called after lbWhaleAppSDKCanOpenURL: returns YES, or when the SDK
// cannot resolve a URL by itself.
[MyAppRouter openURL:url];
}
- (void)lbWhaleAppWillDestroy {
// Persist necessary non-sensitive state before SDK pages close.
}Validate schemes, domains, and route allowlists before opening a URL that lbWhaleAppSDKDidRequestOpenURL: forwards.
lbWhaleAppRefreshTokenExpiredWithResolver: is called only after the SDK’s normal renewal path fails. Its default project behavior should abandon retry and return Broker App to a logged-out state. Only return new credentials when the project has explicitly agreed on a separate way to obtain a completely new Client session.
- (void)lbWhaleAppRefreshTokenExpiredWithResolver:
(void(^)(NSString *token, NSString *refreshToken))resolveCompleted {
resolveCompleted(nil, nil);
}The callback waits 30 seconds by default. Override the timeout with LBAPPSDKConfigKeyRefreshTokenResolverTimeout in extraCustomConfig only when required.
Set LBWhaleAppUIDelegate on config.sdkUIDelegate to observe when the first SDK page opens and when the last SDK page closes. The callbacks track whether SDK pages exist, not whether the App is foregrounded.
config.sdkUIDelegate = self;
- (void)lbWhaleAppEnterSdkPage {
// First live SDK page opened (0 → 1).
}
- (void)lbWhaleAppExitSdkPage {
// Last live SDK page closed (1 → 0).
}Call routing methods only after lbWhaleAppDidFinishLaunching.
[LBWhaleApp pushURL:@"lb://page/main"];
[LBWhaleApp presentURL:@"lb://page/discovery/search-stocks"];
NSDictionary *parameters = @{ @"id": @"ST/US/AAPL" };
[LBWhaleApp pushURL:@"lb://page/stock/detail"
parameters:parameters
animated:YES
completed:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
[MyErrorTracker trackSanitizedError:error];
}
}];pushURL: and presentURL: each provide variants for animated, parameters, and a completion callback.
Use [LBWhaleApp logoutAndDestroy] when the Client signs out or authentication becomes unrecoverable. It releases SDK memory and network resources. The public configuration object also declares -[LBWhaleAppConfig logout], which clears the current credentials without destroying the SDK. The source integration does not define a complete account-switch sequence, so use this method only after Whale confirms the applicable SDK version and call sequence for the project.
[LBWhaleApp logoutAndDestroy];Read the orientation mask the current SDK page requires. A return value of 0 means the active page is not an SDK page, and Broker App decides the supported orientation.
+ (NSUInteger)requiredAppInterfaceOrientationMask;Forward the mask from AppDelegate so SDK pages can render in the required orientation:
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
NSUInteger mask = [LBWhaleApp requiredAppInterfaceOrientationMask];
return mask > 0 ? mask : UIInterfaceOrientationMaskPortrait;
}Choose one delivery path for each project. See Message-push integration for server setup and the standard message shape. The two paths are independent — integrate the one that matches the project’s push infrastructure.
Whale delivers messages to Broker App through the SDK’s built-in channel. Broker App only starts the push service and reports the device token; the SDK owns notification display and click routing after startup (NotificationErrorNotStarted means the SDK must be started before routing).
Start the push service independently of SDK startup, ideally when the App launches.
[[LBWhaleApp pushService]
startWithKey:@"<push-key>"
andSecret:@"<push-secret>"
andCompleted:^(NSError *error) {
if (error) [MyErrorTracker trackSanitizedError:error];
}];Forward the APNs device token once it arrives. If the SDK has not started, it caches the token and binds it to the Client after startup. Pass YES for disableMultiDevices to deliver push only to the most recently signed-in device.
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[[LBWhaleApp pushService]
updateDeviceToken:deviceToken
disableMultiDevices:NO
callback:^(NSError *error) {
if (error) [MyErrorTracker trackSanitizedError:error];
}];
}Forward launchOptions from didFinishLaunchingWithOptions: so the SDK can resolve a cold-start notification tap.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSError *error = [[LBWhaleApp pushService] handleLaunchOptions:launchOptions];
if (error.code == NotificationErrorNotStarted) {
// Start the SDK, then let it process this notification once ready.
} else if (error.code == NotificationErrorNonSDK) {
// Not a Whale notification; Broker App handles it.
}
return YES;
}On iOS 10 and later, forward both UNUserNotificationCenterDelegate methods to the SDK.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
NSError *error = [[LBWhaleApp pushService]
handleSystemDelegateUserNotificationCenter:center
willPresentNotification:notification];
if (!error) {
completionHandler(UNNotificationPresentationOptionNone);
return;
}
// NotificationErrorNotStarted: start the SDK before retrying.
// NotificationErrorNonSDK: Broker App handles this notification.
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
NSError *error = [[LBWhaleApp pushService]
handleSystemDelegateUserNotificationCenter:center
didReceiveNotificationResponse:response];
if (!error) {
completionHandler();
return;
}
// NotificationErrorNotStarted / NotificationErrorNonSDK: same handling as above.
}Broker App handles APNs registration and delivery on its own channel, and passes only Whale messages to the SDK. Use this path when push data does not come from standard APNs, when Broker App must pre-process or validate the payload before the SDK sees it, or when an existing push system should not take on the SDK’s built-in channel.
After Broker App receives a message on its own channel, call checkIsLBNotificationWithUserInfo: to confirm it originated from Whale before forwarding it to the SDK.
NSDictionary *userInfo = notification.request.content.userInfo;
if ([LBWhaleAppPushService checkIsLBNotificationWithUserInfo:userInfo]) {
// Forward to the SDK; see below.
} else {
// Not a Whale message; Broker App handles it.
}When the App is foregrounded and a Whale message arrives, call sdkShowTipWithNotificationInfo:routerHandler: to let the SDK display the tip. routerHandler returns the route when the Client taps the tip.
NSError *error = [[LBWhaleApp pushService]
sdkShowTipWithNotificationInfo:userInfo
routerHandler:^(NSString *routerURL) {
[MyAppRouter openURL:routerURL];
}];Implementing routerHandler disables the SDK’s own navigation. Call routerURL inside the block to keep the expected tap-to-navigate behavior.
After the Client taps a notification, call handleNotificationClickWithInfo:routerHandler: to let the SDK resolve the route. routerHandler behaves the same as for the foreground tip.
NSError *error = [[LBWhaleApp pushService]
handleNotificationClickWithInfo:response.notification.request.content.userInfo
routerHandler:^(NSString *routerURL) {
[MyAppRouter openURL:routerURL];
}];The SDK’s entry point. Provides startup, logout, and page routing.
| Member | Purpose |
|---|---|
sdkVersion |
SDK version |
pushService |
Shared LBWhaleAppPushService instance |
config |
Current startup configuration |
sdkStatus |
NotStarted, Starting, Started, or IsDestroying |
startWithConfig: |
Start the SDK asynchronously |
logoutAndDestroy |
Log out and release SDK resources |
pushURL:... / presentURL:... |
Open an SDK page, optionally with parameters, animation, and completion |
requiredAppInterfaceOrientationMask |
Orientation required by the current SDK page, or 0 outside SDK pages |
The complete routing declarations are:
+ (void)pushURL:(NSString *)url;
+ (void)presentURL:(NSString *)url;
+ (void)pushURL:(NSString *)url animated:(BOOL)animated;
+ (void)presentURL:(NSString *)url animated:(BOOL)animated;
+ (void)pushURL:(NSString *)url
parameters:(NSDictionary * _Nullable)parameters
completed:(void (^ _Nullable)(BOOL isSuccessed, NSError * _Nullable error))completed;
+ (void)presentURL:(NSString *)url
parameters:(NSDictionary * _Nullable)parameters
completed:(void (^ _Nullable)(BOOL isSuccessed, NSError * _Nullable error))completed;
+ (void)pushURL:(NSString *)url
parameters:(NSDictionary * _Nullable)parameters
animated:(BOOL)animated
completed:(void (^ _Nullable)(BOOL isSuccessed, NSError * _Nullable error))completed;
+ (void)presentURL:(NSString *)url
parameters:(NSDictionary * _Nullable)parameters
animated:(BOOL)animated
completed:(void (^ _Nullable)(BOOL isSuccessed, NSError * _Nullable error))completed;Startup configuration containing identity credentials, UI options, and the event-callback delegate. Build it with initAppKey:... and pass it to [LBWhaleApp startWithConfig:]. theme, language, and priceColorType can change at any time before or after startup.
@interface LBWhaleAppConfig : NSObject
@property (nonatomic, copy, readonly) NSString *appKey;
@property (nonatomic, copy, readonly) NSString *appSecret;
@property (nonatomic, copy, readonly) NSString *appId;
@property (nonatomic, strong, readonly) LBWhaleAppConfigMultilingual *appName;
@property (nonatomic, copy, readonly) NSString *defaultAccountChannel;
@property (nonatomic, copy, readonly) NSString *webDomainPrefix;
@property (nonatomic, copy, readonly) NSString *token;
@property (nonatomic, copy, readonly) NSString *refreshToken;
@property (nonatomic, weak, readonly, nullable) id<LBWhaleAppDelegate> delegate;
@property (nonatomic, weak, nullable) id<LBWhaleAppUIDelegate> sdkUIDelegate;
@property (nonatomic, assign) LBWhaleAppConfigTheme theme;
@property (nonatomic, assign) LBWhaleAppConfigLanguage language;
@property (nonatomic, assign) LBWhaleAppConfigPriceColor priceColorType;
@property (nonatomic, assign) LBWhaleAppConfigEnv env;
@property (nonatomic, copy, readonly, nullable) NSDictionary *extraCustomConfig;
- (instancetype)initAppKey:(NSString *)appKey
appSecret:(NSString *)appSecret
appId:(NSString *)appId
appName:(LBWhaleAppConfigMultilingual *)appName
defaultAccountChannel:(NSString *)defaultAccountChannel
webDomainPrefix:(NSString *)webDomainPrefix
token:(NSString *)token
refreshToken:(NSString *)refreshToken
delegate:(nullable id<LBWhaleAppDelegate>)delegate
extraCustomConfig:(nullable NSDictionary *)extraCustomConfig;
/// Clears the current Client credentials to switch accounts without destroying the SDK.
- (void)logout;
@endextraCustomConfig supports these public keys:
| Key | Value | Purpose |
|---|---|---|
LBAPPSDKConfigKeyDisableIDFA |
NSNumber boolean |
Disable IDFA access and the ATT prompt |
LBAPPSDKConfigDebugEggDisable |
NSNumber boolean |
Disable the system debug panel |
LBAPPSDKConfigKeyDeviceId |
NSString |
Supply a project-defined device identifier |
LBAPPSDKConfigKeyFileDirectory |
NSString |
Override the SDK cache directory |
LBAPPSDKConfigKeyRefreshTokenResolverTimeout |
NSNumber seconds |
Change the exceptional renewal callback timeout |
LBAPPSDKConfigKeyColor |
nested dictionary | Customize supported push-tip colors |
LBAPPSDKConfigKeyFontCrypto |
UIFontDescriptor |
Replace the market-data digit font |
LBAPPSDKConfigKeyFontMonospaced |
UIFontDescriptor |
Replace the regular monospaced font |
LBAPPSDKConfigKeyFontMonospacedBold |
UIFontDescriptor |
Replace the bold monospaced font |
Color maps use LBAPPSDKConfigKeyColorLight and LBAPPSDKConfigKeyColorDark. Supported push-tip keys include default background, title, content, and icon colors, plus order-completed, order-failed, order-limit, and other-message overrides.
The exact public keys are LBAPPSDKConfigKeyPushTipView, LBAPPSDKConfigKeyPTVDefaultBackground, LBAPPSDKConfigKeyPTVDefaultTitle, LBAPPSDKConfigKeyPTVDefaultContent, LBAPPSDKConfigKeyPTVDefaultIcon, LBAPPSDKConfigKeyPTVOrderCompleted, LBAPPSDKConfigKeyPTVOrderFailed, LBAPPSDKConfigKeyPTVOrderLimit, LBAPPSDKConfigKeyPTVOther, LBAPPSDKConfigKeyPTVLimitTitleColor, LBAPPSDKConfigKeyPTVLimitContentColor, and LBAPPSDKConfigKeyPTVLimitIconColor.
The SDK supports custom font configuration so Broker App can replace SDK-embedded fonts. Configure the font mapping in extraCustomConfig when creating LBWhaleAppConfig.
UIFontDescriptor *customMonospacedFont =
[UIFontDescriptor fontDescriptorWithName:@"YourCustomFont-Monospaced" size:0];
UIFontDescriptor *customMonospacedBoldFont =
[UIFontDescriptor fontDescriptorWithName:@"YourMonospaceFont-MonospacedBold" size:0];
UIFontDescriptor *customCryptoFont =
[UIFontDescriptor fontDescriptorWithName:@"YourCryptoFont" size:0];
NSDictionary *extraConfig = @{
LBAPPSDKConfigKeyFontMonospaced: customMonospacedFont,
LBAPPSDKConfigKeyFontMonospacedBold: customMonospacedBoldFont,
LBAPPSDKConfigKeyFontCrypto: customCryptoFont
};
LBWhaleAppConfig *config = [[LBWhaleAppConfig alloc]
initAppKey:@"<app-key>"
appSecret:@"<app-secret>"
appId:@"<app-id>"
appName:appName
defaultAccountChannel:@"<account-channel>"
webDomainPrefix:@"<web-domain-prefix>"
token:@"<client-token>"
refreshToken:@"<client-refresh-token>"
delegate:self
extraCustomConfig:extraConfig];- Register the custom font in the App’s
Info.plist, or register it in code, before use. - Use the font’s PostScript name or full name, not its file name.
- The SDK falls back to its bundled font when the specified font is unavailable.
- Set the font descriptor size to
0; the SDK selects an appropriate size for each context.
Multilingual App name configuration.
@interface LBWhaleAppConfigMultilingual : NSObject
@property (nonatomic, copy, readonly) NSString *en;
@property (nonatomic, copy, readonly, nullable) NSString *zhCN;
@property (nonatomic, copy, readonly, nullable) NSString *zhHK;
@property (nonatomic, readonly) NSString *currentLanguageText;
- (instancetype)initWithEn:(NSString *)en
zhCN:(nullable NSString *)zhCN
zhHK:(nullable NSString *)zhHK;
@endtypedef NS_ENUM(NSInteger, LBWhaleAppConfigPriceColor) {
LBWhaleAppConfigPriceColorFollowUserSetting, // Follow the Client's in-SDK setting (default).
LBWhaleAppConfigPriceColorRedUpGreenDown, // Force red-up, green-down.
LBWhaleAppConfigPriceColorGreenUpRedDown // Force green-up, red-down.
};typedef NS_ENUM(NSInteger, LBWhaleAppConfigTheme) {
LBWhaleAppConfigThemeAuto, // Follow the system setting (default).
LBWhaleAppConfigThemeLight,
LBWhaleAppConfigThemeDark
};typedef NS_ENUM(NSInteger, LBWhaleAppConfigLanguage) {
LBWhaleAppConfigLanguageEn,
LBWhaleAppConfigLanguageZhCN,
LBWhaleAppConfigLanguageZhHK
};typedef NS_ENUM(NSInteger, LBWhaleAppConfigEnv) {
LBWhaleAppConfigEnvProd, // Production (default).
LBWhaleAppConfigEnvSit,
LBWhaleAppConfigEnvTest
};Do not select a non-production environment unless the Whale project team supplies matching configuration.
SDK event-callback delegate. Every method is optional.
@protocol LBWhaleAppDelegate <NSObject>
@optional
/// The SDK renewed the session; the new token is informational only.
- (void)lbWhaleAppTokenDidChange:(nonnull NSString *)token;
/// The SDK raised a runtime error after startup.
- (void)lbWhaleAppDidRunInError:(nonnull NSError *)error;
/// The SDK failed to start.
- (void)lbWhaleAppStartFailedWithError:(nonnull NSError *)error;
/// The SDK finished starting.
- (void)lbWhaleAppDidFinishLaunching;
/// The SDK cannot resolve this URL by itself.
- (void)lbWhaleAppSDKDidRequestOpenURL:(NSString *)openUrl;
/// Return YES when Broker App can open this URL.
- (BOOL)lbWhaleAppSDKCanOpenURL:(NSString *)url;
/// The SDK is about to destroy itself.
- (void)lbWhaleAppWillDestroy;
/// The SDK's own token renewal failed; call resolveCompleted with new
/// credentials, or with (nil, nil) before the timeout to fail the request.
- (void)lbWhaleAppRefreshTokenExpiredWithResolver:(void(^)(NSString *token, NSString *refreshToken))resolveCompleted;
/// Forwards SDK analytics events. Never add credentials or Client-sensitive information.
- (void)lbWhaleAppAnalyticsEventWithName:(NSString *)name properties:(NSDictionary *)properties;
@endSDK page lifecycle delegate, set through LBWhaleAppConfig.sdkUIDelegate. Both methods fire on the main thread.
@protocol LBWhaleAppUIDelegate <NSObject>
/// The first SDK page opened.
- (void)lbWhaleAppEnterSdkPage;
/// The last SDK page closed.
- (void)lbWhaleAppExitSdkPage;
@endPush-service class. Access the shared instance through LBWhaleApp.pushService; no manual initialization is required.
@property (nonatomic, copy, readonly, nullable) NSString *pushAppKey;
@property (nonatomic, copy, readonly, nullable) NSString *pushAppSecret;
@property (nonatomic, strong, readonly, nullable) NSData *deviceToken;
@property (nonatomic, copy, readonly, nullable) NSString *pushDeviceId;
@property (nonatomic, assign, readonly) BOOL isStarted;
@property (nonatomic, assign, readonly) BOOL disableMultiDevices;
+ (BOOL)checkIsLBNotificationWithUserInfo:(NSDictionary *)userInfo;
- (void)startWithKey:(NSString *)appPushKey
andSecret:(NSString *)appPushSecret
andCompleted:(void (^ _Nullable)(NSError * _Nullable error))completed;
- (void)updateDeviceToken:(NSData *)deviceToken
disableMultiDevices:(BOOL)disableMultiDevices
callback:(void (^ _Nullable)(NSError * _Nullable error))callback;
- (NSError * _Nullable)handleLaunchOptions:(NSDictionary *)launchOptions;
- (NSError * _Nullable)handleSystemDelegateUserNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification;
- (NSError * _Nullable)handleSystemDelegateUserNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response;
- (NSError * _Nullable)sdkShowTipWithNotificationInfo:(NSDictionary *)notification
routerHandler:(void (^ _Nullable)(NSString *routerURL))routerHandler;
- (NSError * _Nullable)handleNotificationClickWithInfo:(NSDictionary *)response
routerHandler:(void (^ _Nullable)(NSString *routerURL))routerHandler;LBWhaleNotificationHandlingErrorCode contains NotificationErrorNotStarted, NotificationErrorNonSDK, and NotificationErrorPushStartFailed.
The delivered podspec declares these external dependencies:
spec.dependency 'RangersAPM/Crash', '>= 5.1.6'
spec.dependency 'RangersAPM/APMLog', '>= 5.1.6'
spec.dependency 'RangersAPM/CN', '>= 5.1.6'
spec.dependency 'RangersAPM/WatchDog', '>= 5.1.6'
spec.dependency 'RangersAPM/CloudCommand', '>= 5.1.6'
spec.dependency 'AlicloudPush', '>= 3.2.2'
spec.dependency 'IQKeyboardManager', '>= 6.5.9'They are not embedded in the SDK binary. If Broker App already uses a conflicting version, coordinate the resolved version with the Whale project team instead of packaging two incompatible implementations.