react-native-firework-sdk 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +5 -3
- package/android/src/main/java/com/fireworksdk/bridge/components/videofeed/StoryBlockFragment.kt +129 -0
- package/android/src/main/java/com/fireworksdk/bridge/components/videofeed/StoryBlockFrameLayout.kt +84 -0
- package/android/src/main/java/com/fireworksdk/bridge/models/FWEventName.kt +2 -1
- package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfiguration.kt +18 -5
- package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfigurationDeserializer.kt +33 -9
- package/android/src/main/java/com/fireworksdk/bridge/models/FWShoppingCtaResult.kt +17 -0
- package/android/src/main/java/com/fireworksdk/bridge/models/FWShoppingCtaResultDeserializer.kt +33 -0
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWStoryBlockManager.kt +126 -45
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt +6 -3
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/models/FWVideoShoppingInterface.kt +2 -2
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWNavigatorModule.kt +9 -1
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt +81 -50
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/utils/FWEventUtils.kt +9 -2
- package/android/src/main/java/com/fireworksdk/bridge/utils/FWConfigUtil.kt +1 -1
- package/android/src/main/java/com/fireworksdk/bridge/utils/FWGlobalDataUtil.kt +4 -0
- package/android/src/main/java/com/fireworksdk/bridge/utils/FWLanguageUtil.kt +48 -16
- package/android/src/main/res/layout/fw_bridge_story_block.xml +24 -0
- package/ios/Components/StoryBlock.swift +33 -2
- package/ios/Components/StoryBlockManager.m +32 -0
- package/ios/Components/VideoFeed.swift +9 -29
- package/ios/Components/VideoFeedManager.m +11 -6
- package/ios/FireworkSdk.xcodeproj/project.pbxproj +378 -104
- package/ios/Models/NativeToRN/FireworkEventName.swift +3 -1
- package/ios/Models/RNToNative/RCTConvert+Shopping.swift +21 -0
- package/ios/Models/RNToNative/RCTConvert+VideoFeed.swift +27 -0
- package/ios/Modules/FWNavigatorModule/FWNavigatorModule.swift +5 -1
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule.m +1 -0
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule.swift +30 -0
- package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +13 -0
- package/ios/Modules/Shopping/ShoppingCTAResult.swift +16 -0
- package/ios/Modules/Shopping/ShoppingModule.m +2 -1
- package/ios/Modules/Shopping/ShoppingModule.swift +106 -30
- package/ios/Support/MultiHostStreaming/FWMultiHostStreaming.podspec +24 -0
- package/ios/Support/MultiHostStreaming/src/MultiHostStreamingSDK.swift +17 -0
- package/ios/Utils/AppLanguage/Bundle+FWSwizzle.swift +58 -0
- package/ios/Utils/AppLanguage/FWAppLanguageManager.swift +139 -0
- package/ios/Utils/AppLanguage/FWLanguageUtil.swift +43 -0
- package/ios/Utils/AppLanguage/NumberFormatter+FWSwizzle.swift +25 -0
- package/ios/Utils/AppLanguage/UIImageView+FWSwizzle.swift +91 -0
- package/ios/Utils/AppLanguage/UILabel+FWSwizzle.swift +98 -0
- package/ios/Utils/AppLanguage/UITextField+FWSwizzle.swift +97 -0
- package/ios/Utils/AppLanguage/UITextView+FWSwizzle.swift +97 -0
- package/ios/Utils/AppLanguage/UIView+FWSwizzle.swift +38 -0
- package/ios/Utils/AppLanguage/UIViewController+FWSwizzle.swift +32 -0
- package/ios/Utils/AppLanguage/UIWindow+FWSwizzle.swift +26 -0
- package/ios/Utils/AppLanguage/URLSession+FWSwizzle.swift +69 -0
- package/ios/Utils/{DispatchQueue+FWOnce.swift → Extensions/DispatchQueue+FWOnce.swift} +3 -3
- package/ios/Utils/{UINavigationController+FWSwizzle.swift → Extensions/Swizzle/UINavigationController+FWSwizzle.swift} +6 -8
- package/ios/Utils/Extensions/UIView+FWUIHierarchy.swift +47 -0
- package/ios/Utils/FWRTL/Classes/Manager/FWRTLManager.h +25 -0
- package/ios/Utils/FWRTL/Classes/Manager/FWRTLManager.m +75 -0
- package/ios/Utils/FWRTL/Classes/UICategories/CALayer+FWRTL.h +21 -0
- package/ios/Utils/FWRTL/Classes/UICategories/CALayer+FWRTL.m +124 -0
- package/ios/Utils/FWRTL/Classes/UICategories/FWRTLRemoteViewControllerAdaptor.h +11 -0
- package/ios/Utils/FWRTL/Classes/UICategories/FWRTLRemoteViewControllerAdaptor.m +86 -0
- package/ios/Utils/FWRTL/Classes/UICategories/FWRTLWhiteListManager.h +16 -0
- package/ios/Utils/FWRTL/Classes/UICategories/FWRTLWhiteListManager.m +55 -0
- package/ios/Utils/FWRTL/Classes/UICategories/UILabel+FWRTL.h +18 -0
- package/ios/Utils/FWRTL/Classes/UICategories/UILabel+FWRTL.m +39 -0
- package/ios/Utils/FWRTL/Classes/UICategories/UIView+FWRTL.h +54 -0
- package/ios/Utils/FWRTL/Classes/UICategories/UIView+FWRTL.m +141 -0
- package/ios/Utils/FWRTL/Classes/UICategories/UIWindow+FWRTL.h +16 -0
- package/ios/Utils/FWRTL/Classes/UICategories/UIWindow+FWRTL.m +20 -0
- package/ios/Utils/FWRTL/Classes/Utils/FWRTLDefinitions.h +52 -0
- package/ios/Utils/FWRTL/Classes/Utils/NSObject+FWRTLReloadBlock.h +19 -0
- package/ios/Utils/FWRTL/Classes/Utils/NSObject+FWRTLReloadBlock.m +49 -0
- package/ios/Utils/FWRTL/Classes/Utils/NSString+FWRTL.h +21 -0
- package/ios/Utils/FWRTL/Classes/Utils/NSString+FWRTL.m +38 -0
- package/ios/Utils/FWRTL/Classes/Utils/UIImage+FWRTL.h +18 -0
- package/ios/Utils/FWRTL/Classes/Utils/UIImage+FWRTL.m +43 -0
- package/ios/Utils/FWSwizzleLoader.m +6 -1
- package/ios/Utils/FWSwizzleLoader.swift +13 -0
- package/ios/Utils/FWSwizzleUtil.swift +17 -9
- package/ios/react_native_firework_sdk.h +1 -0
- package/ios/scripts/react_native_firework_sdk_pods.rb +31 -0
- package/lib/commonjs/FireworkSDK.js +21 -4
- package/lib/commonjs/FireworkSDK.js.map +1 -1
- package/lib/commonjs/VideoShopping.js +20 -37
- package/lib/commonjs/VideoShopping.js.map +1 -1
- package/lib/commonjs/components/StoryBlock.js +190 -125
- package/lib/commonjs/components/StoryBlock.js.map +1 -1
- package/lib/commonjs/components/VideoFeed.js +11 -1
- package/lib/commonjs/components/VideoFeed.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/FWEventName.js +2 -0
- package/lib/commonjs/models/FWEventName.js.map +1 -1
- package/lib/commonjs/models/ShoppingCTAResult.js +2 -0
- package/lib/commonjs/modules/FireworkSDKModule.js.map +1 -1
- package/lib/commonjs/modules/ShoppingModule.js.map +1 -1
- package/lib/module/FireworkSDK.js +21 -4
- package/lib/module/FireworkSDK.js.map +1 -1
- package/lib/module/VideoShopping.js +20 -39
- package/lib/module/VideoShopping.js.map +1 -1
- package/lib/module/components/StoryBlock.js +179 -131
- package/lib/module/components/StoryBlock.js.map +1 -1
- package/lib/module/components/VideoFeed.js +10 -1
- package/lib/module/components/VideoFeed.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/FWEventName.js +2 -0
- package/lib/module/models/FWEventName.js.map +1 -1
- package/lib/module/models/ShoppingCTAResult.js +2 -0
- package/lib/module/modules/FireworkSDKModule.js.map +1 -1
- package/lib/module/modules/ShoppingModule.js.map +1 -1
- package/lib/typescript/FireworkSDK.d.ts +7 -4
- package/lib/typescript/VideoShopping.d.ts +9 -11
- package/lib/typescript/components/StoryBlock.d.ts +19 -25
- package/lib/typescript/index.d.ts +6 -6
- package/lib/typescript/models/FWEventName.d.ts +2 -0
- package/lib/typescript/models/FWEvents.d.ts +14 -1
- package/lib/typescript/models/ProductInfoViewConfiguration.d.ts +36 -1
- package/lib/typescript/models/ShoppingCTAResult.d.ts +11 -0
- package/lib/typescript/modules/FireworkSDKModule.d.ts +1 -2
- package/lib/typescript/modules/ShoppingModule.d.ts +2 -1
- package/package.json +2 -2
- package/react-native-firework-sdk.podspec +26 -24
- package/src/FireworkSDK.ts +18 -5
- package/src/VideoShopping.ts +40 -53
- package/src/components/StoryBlock.tsx +199 -96
- package/src/components/VideoFeed.tsx +11 -0
- package/src/index.ts +15 -7
- package/src/models/FWEventName.ts +2 -0
- package/src/models/FWEvents.ts +14 -1
- package/src/models/ProductInfoViewConfiguration.ts +38 -1
- package/src/models/ShoppingCTAResult.ts +11 -0
- package/src/modules/FireworkSDKModule.ts +1 -2
- package/src/modules/ShoppingModule.ts +5 -5
- package/android/src/main/java/com/fireworksdk/bridge/constants/FWCommandConstant.kt +0 -6
- package/ios/Utils/UIView+ParentViewController.swift +0 -21
- package/lib/commonjs/models/AddToCartResult.js +0 -2
- package/lib/module/models/AddToCartResult.js +0 -2
- package/lib/typescript/models/AddToCartResult.d.ts +0 -10
- package/src/models/AddToCartResult.ts +0 -10
- /package/ios/Utils/{String+Color.swift → Extensions/String+Color.swift} +0 -0
- /package/ios/Utils/{UIView+Constraints.swift → Extensions/UIView+Constraints.swift} +0 -0
- /package/ios/Utils/{UIViewController+AttachChild.swift → Extensions/UIViewController+AttachChild.swift} +0 -0
- /package/lib/commonjs/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
- /package/lib/module/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
|
@@ -16,6 +16,7 @@ enum FWEventName: String, CaseIterable {
|
|
|
16
16
|
case shareBaseURLUpdated = "fw:share-base-url-updated" // emitted in JS side
|
|
17
17
|
case videoLaunchBehaviorUpdated = "fw:video-launch-behavior-updated" // emitted in JS side
|
|
18
18
|
case adBadgeConfigurationUpdated = "fw:ad-badge-configuration-updated" // emitted in JS side
|
|
19
|
+
case appLanguageUpdated = "fw:app-language-updated" // emitted in JS side
|
|
19
20
|
case logMessage = "fw:log-message"
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -35,9 +36,10 @@ enum VideoPlaybackSubEventName: String {
|
|
|
35
36
|
enum ShoppingEventName: String, CaseIterable {
|
|
36
37
|
case updateProductDetails = "fw:shopping:update-product-details"
|
|
37
38
|
case willDisplayProduct = "fw:shopping:will-display-product"
|
|
38
|
-
case
|
|
39
|
+
case ctaButtonClick = "fw:shopping:cta-button-click"
|
|
39
40
|
case clickCartIcon = "fw:shopping:click-cart-icon"
|
|
40
41
|
case logMessage = "fw:log-message"
|
|
42
|
+
case customLinkButtonClick = "fw:shopping:custom-link-button-click"
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
/// Live stream event
|
|
@@ -63,4 +63,25 @@ extension RCTConvert {
|
|
|
63
63
|
|
|
64
64
|
return result
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
static func buildShoppingCTAResult(_ result: [String: Any]?)
|
|
68
|
+
-> ShoppingCTAResult? {
|
|
69
|
+
guard let rResult = result else {
|
|
70
|
+
return nil
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let jsonData = try? JSONSerialization.data(withJSONObject: rResult, options: .prettyPrinted)
|
|
74
|
+
guard let rJsonData = jsonData else {
|
|
75
|
+
return nil
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
var ctaResult: ShoppingCTAResult?
|
|
79
|
+
do {
|
|
80
|
+
ctaResult = try JSONDecoder().decode(ShoppingCTAResult.self, from: rJsonData)
|
|
81
|
+
} catch let error {
|
|
82
|
+
print(error.localizedDescription)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return ctaResult
|
|
86
|
+
}
|
|
66
87
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
|
+
import FireworkVideo
|
|
9
10
|
|
|
10
11
|
extension VideFeedSourceType {
|
|
11
12
|
static var sourceTypeMapper: [String: VideFeedSourceType] {
|
|
@@ -87,4 +88,30 @@ extension RCTConvert {
|
|
|
87
88
|
|
|
88
89
|
return try? JSONDecoder().decode(AdConfiguration.self, from: rJsonData)
|
|
89
90
|
}
|
|
91
|
+
|
|
92
|
+
@nonobjc
|
|
93
|
+
public static func getFireworkVideoAdConfiguration(
|
|
94
|
+
_ adConfiguration: AdConfiguration?
|
|
95
|
+
) -> FireworkVideo.AdConfiguration? {
|
|
96
|
+
guard let feedAdConfiguration = adConfiguration else {
|
|
97
|
+
return nil
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
var resultAdConfiguration = FireworkVideo.AdConfiguration()
|
|
101
|
+
if let requiresAds = feedAdConfiguration.requiresAds {
|
|
102
|
+
resultAdConfiguration.requiresAds = requiresAds
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if let adsFetchTimeout = feedAdConfiguration.adsFetchTimeout {
|
|
106
|
+
resultAdConfiguration.adsFetchTimeout = adsFetchTimeout
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if let vastAttributes = feedAdConfiguration.vastAttributes {
|
|
110
|
+
resultAdConfiguration.vastAttributes = vastAttributes.map({ attribute in
|
|
111
|
+
return URLQueryItem(name: attribute.name ?? "", value: attribute.value ?? "")
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return resultAdConfiguration
|
|
116
|
+
}
|
|
90
117
|
}
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import FireworkVideo
|
|
11
11
|
|
|
12
|
+
private let FWExitButtonImageNames: [String] = [
|
|
13
|
+
"closeX",
|
|
14
|
+
"down-arrow-light"
|
|
15
|
+
]
|
|
16
|
+
|
|
12
17
|
private struct PlayerInfo {
|
|
13
18
|
enum PlayerMode: Int {
|
|
14
19
|
case fullscreen, floating
|
|
@@ -358,7 +363,6 @@ extension FWNavigatorModule {
|
|
|
358
363
|
if let rootVC = keyWindow.rootViewController,
|
|
359
364
|
let presentedVC = rootVC.presentedViewController {
|
|
360
365
|
let furthestAncestorController = getFurthestAncestorController(presentedVC)
|
|
361
|
-
|
|
362
366
|
if let playerVC = getPlayerViewController(furthestAncestorController) {
|
|
363
367
|
return PlayerInfo(playerVC: playerVC, mode: .fullscreen)
|
|
364
368
|
}
|
|
@@ -19,5 +19,6 @@ RCT_EXTERN_METHOD(setVideoPlaybackEventEnabled:(BOOL)enabled)
|
|
|
19
19
|
RCT_EXTERN_METHOD(setAdBadgeConfiguration:(NSDictionary *)config resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
20
20
|
RCT_EXTERN_METHOD(setAppComponentName:(NSString *)name resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
21
21
|
RCT_EXTERN_METHOD(trackPurchase:(NSDictionary *)parameters)
|
|
22
|
+
RCT_EXTERN_METHOD(changeAppLanguage:(NSString * __nullable)language resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
22
23
|
|
|
23
24
|
@end
|
|
@@ -44,6 +44,18 @@ class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
|
|
|
44
44
|
#if DEBUG
|
|
45
45
|
let formatter = DateFormatter()
|
|
46
46
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
|
|
47
|
+
let iOSSDKBundle = Bundle(for: FireworkVideoSDK.self)
|
|
48
|
+
|
|
49
|
+
let messageForLocalizations = """
|
|
50
|
+
[iOS] localizations information \(formatter.string(from: Date()))
|
|
51
|
+
AppleLanguages: \((UserDefaults.standard.object(forKey: "AppleLanguages") as? [String]) ?? [])
|
|
52
|
+
Bundle.main.localizations \(Bundle.main.localizations)
|
|
53
|
+
Bundle.main.preferredLocalizations \(Bundle.main.preferredLocalizations)
|
|
54
|
+
iOSSDKBundle.localizations \(iOSSDKBundle.localizations)
|
|
55
|
+
iOSSDKBundle.preferredLocalizations \(iOSSDKBundle.preferredLocalizations)
|
|
56
|
+
"""
|
|
57
|
+
print("[react-native-firework-sdk] [swift] \(messageForLocalizations)")
|
|
58
|
+
sendEvent(withName: FWEventName.logMessage.rawValue, body: ["message": messageForLocalizations])
|
|
47
59
|
|
|
48
60
|
let message = "[iOS] Call initializeSDK \(formatter.string(from: Date()))"
|
|
49
61
|
print("[react-native-firework-sdk] [swift] \(message)")
|
|
@@ -59,6 +71,7 @@ class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
|
|
|
59
71
|
FireworkVideoSDK.eventTracking.videoPlaybackDelegate = self
|
|
60
72
|
FireworkVideoSDK.eventTracking.feedDelegate = self
|
|
61
73
|
|
|
74
|
+
FWAppLanguageManager.shared.initializeManager()
|
|
62
75
|
resolver([:])
|
|
63
76
|
}
|
|
64
77
|
|
|
@@ -157,6 +170,23 @@ class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
|
|
|
157
170
|
)
|
|
158
171
|
}
|
|
159
172
|
|
|
173
|
+
@objc(changeAppLanguage:resolver:rejecter:)
|
|
174
|
+
func changeAppLanguage(
|
|
175
|
+
_ language: String?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock
|
|
176
|
+
) {
|
|
177
|
+
#if DEBUG
|
|
178
|
+
let formatter = DateFormatter()
|
|
179
|
+
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
|
|
180
|
+
let message = "[iOS] Call changeAppLanguage: \(language ?? "") \(formatter.string(from: Date()))"
|
|
181
|
+
sendEvent(withName: FWEventName.logMessage.rawValue, body: ["message": message])
|
|
182
|
+
#endif
|
|
183
|
+
|
|
184
|
+
DispatchQueue.main.async {
|
|
185
|
+
let result = FWAppLanguageManager.shared.changeAppLanguage(language)
|
|
186
|
+
resolver(result)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
160
190
|
// MARK: - FireworkVideoSDKDelegate
|
|
161
191
|
func fireworkVideoDidLoadSuccessfully() {
|
|
162
192
|
#if DEBUG
|
|
@@ -9,6 +9,7 @@ import Foundation
|
|
|
9
9
|
|
|
10
10
|
struct ProductInfoViewConfiguration: Codable {
|
|
11
11
|
var addToCartButton: AddToCartButtonConfiguration?
|
|
12
|
+
var ctaButton: CTAButtonConfiguration?
|
|
12
13
|
var linkButton: LinkButtonConfiguration?
|
|
13
14
|
|
|
14
15
|
struct AddToCartButtonConfiguration: Codable {
|
|
@@ -18,6 +19,18 @@ struct ProductInfoViewConfiguration: Codable {
|
|
|
18
19
|
var iOSFontInfo: FontInfo?
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
struct CTAButtonConfiguration: Codable {
|
|
23
|
+
var text: CTAButtonText?
|
|
24
|
+
var backgroundColor: String?
|
|
25
|
+
var textColor: String?
|
|
26
|
+
var fontSize: Double?
|
|
27
|
+
var iOSFontInfo: FontInfo?
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
enum CTAButtonText: String, Codable {
|
|
31
|
+
case addToCart, shopNow
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
struct LinkButtonConfiguration: Codable {
|
|
22
35
|
var isHidden: Bool?
|
|
23
36
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ShoppingCTAResult.swift
|
|
3
|
+
//
|
|
4
|
+
// Created by linjie jiang on 2023/3/20.
|
|
5
|
+
//
|
|
6
|
+
|
|
7
|
+
import Foundation
|
|
8
|
+
|
|
9
|
+
struct ShoppingCTAResult: Codable {
|
|
10
|
+
var res: ShoppingCTAResultRes?
|
|
11
|
+
var tips: String?
|
|
12
|
+
|
|
13
|
+
enum ShoppingCTAResultRes: String, Codable {
|
|
14
|
+
case success, fail
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -13,10 +13,11 @@ _RCT_EXTERN_REMAP_METHOD(init, initialize, NO)
|
|
|
13
13
|
RCT_EXTERN_METHOD(setCartIconVisible:(BOOL)visible)
|
|
14
14
|
RCT_EXTERN_METHOD(setCartItemCount:(int)itemCounts)
|
|
15
15
|
RCT_EXTERN_METHOD(updateVideoProducts:(NSArray *)products cbId:(nonnull NSNumber *)cbId)
|
|
16
|
-
RCT_EXTERN_METHOD(updateAddToCartStatus:(NSString *)res tips:(nullable NSString *)tips cbId:(nonnull NSNumber *)cbId)
|
|
17
16
|
RCT_EXTERN_METHOD(jumpToCartPage:(nonnull NSNumber *)cbId props:(NSDictionary *)props)
|
|
18
17
|
RCT_EXTERN_METHOD(setCustomClickCartIconEnabled:(BOOL)enabled resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
19
18
|
RCT_EXTERN_METHOD(setProductInfoViewConfiguration:(NSDictionary *)config)
|
|
20
19
|
RCT_EXTERN_METHOD(clearCallbackId:(nonnull NSNumber *)cbId eventName:(nonnull NSString *)name)
|
|
20
|
+
RCT_EXTERN_METHOD(setCustomClickLinkButtonEnabled:(BOOL)enabled resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
21
|
+
RCT_EXTERN_METHOD(updateShoppingCTAResult:(NSDictionary *)result cbId:(nonnull NSNumber *)cbId)
|
|
21
22
|
|
|
22
23
|
@end
|
|
@@ -10,11 +10,13 @@ import Foundation
|
|
|
10
10
|
|
|
11
11
|
weak var gCartViewController: FWCartViewController?
|
|
12
12
|
|
|
13
|
+
// swiftlint:disable file_length type_body_length
|
|
14
|
+
|
|
13
15
|
@objc(ShoppingModule)
|
|
14
16
|
class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewControllerProviding {
|
|
15
17
|
|
|
16
18
|
private var productInfoViewConfigurator: (Int, ProductInfoViewConfigurable)?
|
|
17
|
-
private var
|
|
19
|
+
private var shoppingCTAHandlerMap: [Int: ShoppingCTAHandler] = [:]
|
|
18
20
|
private var productHydratingMap: [Int: ProductHydrating] = [:]
|
|
19
21
|
|
|
20
22
|
private var cartViewController: FWCartViewController?
|
|
@@ -22,6 +24,7 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
|
|
|
22
24
|
private var itemCounts = 0
|
|
23
25
|
private var customClickCartIconEnabled = false
|
|
24
26
|
private var productInfoViewConfiguration: ProductInfoViewConfiguration?
|
|
27
|
+
private var customClickLinkButtonEnabled = false
|
|
25
28
|
|
|
26
29
|
override func supportedEvents() -> [String]! {
|
|
27
30
|
ShoppingEventName.allCases.map { $0.rawValue }
|
|
@@ -130,21 +133,6 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
|
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
@objc
|
|
134
|
-
func updateAddToCartStatus(_ res: String, tips: String?, cbId: NSNumber) {
|
|
135
|
-
guard let handler = addToCartHandlerMap[Int(truncating: cbId)] else {
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
addToCartHandlerMap.removeValue(forKey: Int(truncating: cbId))
|
|
140
|
-
|
|
141
|
-
if res == "success" {
|
|
142
|
-
handler(.feedbackOnly(.success(message: tips ?? "success")))
|
|
143
|
-
} else {
|
|
144
|
-
handler(.feedbackOnly(.failure(message: tips ?? "failure")))
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
136
|
@objc
|
|
149
137
|
func jumpToCartPage(_ cbId: NSNumber, props: NSDictionary) {
|
|
150
138
|
let properties: [String: Any] = (props as? [String: Any]) ?? [:]
|
|
@@ -186,9 +174,51 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
|
|
|
186
174
|
func clearCallbackId(_ cbId: NSNumber, eventName: String) {
|
|
187
175
|
if eventName == ShoppingEventName.updateProductDetails.rawValue {
|
|
188
176
|
productHydratingMap.removeValue(forKey: Int(truncating: cbId))
|
|
189
|
-
} else if eventName == ShoppingEventName.
|
|
190
|
-
|
|
177
|
+
} else if eventName == ShoppingEventName.ctaButtonClick.rawValue {
|
|
178
|
+
if let handler = shoppingCTAHandlerMap[Int(truncating: cbId)] {
|
|
179
|
+
handler(.none)
|
|
180
|
+
}
|
|
181
|
+
shoppingCTAHandlerMap.removeValue(forKey: Int(truncating: cbId))
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@objc
|
|
186
|
+
func setCustomClickLinkButtonEnabled(
|
|
187
|
+
_ enabled: Bool, resolver: @escaping RCTPromiseResolveBlock,
|
|
188
|
+
rejecter: @escaping RCTPromiseRejectBlock
|
|
189
|
+
) {
|
|
190
|
+
customClickLinkButtonEnabled = enabled
|
|
191
|
+
resolver([:])
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@objc
|
|
195
|
+
func updateShoppingCTAResult(_ result: [String: Any]?, cbId: NSNumber) {
|
|
196
|
+
guard let ctaResult = RCTConvert.buildShoppingCTAResult(result) else {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#if DEBUG
|
|
201
|
+
print("ctaResult res \(String(describing: ctaResult.res))")
|
|
202
|
+
#endif
|
|
203
|
+
|
|
204
|
+
guard let handler = shoppingCTAHandlerMap[Int(truncating: cbId)] else {
|
|
205
|
+
return
|
|
191
206
|
}
|
|
207
|
+
|
|
208
|
+
if let res = ctaResult.res,
|
|
209
|
+
let tips = ctaResult.tips,
|
|
210
|
+
tips.count > 0 {
|
|
211
|
+
switch res {
|
|
212
|
+
case .success:
|
|
213
|
+
handler(.feedbackOnly(.success(message: tips)))
|
|
214
|
+
case .fail:
|
|
215
|
+
handler(.feedbackOnly(.failure(message: tips)))
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
handler(.none)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
shoppingCTAHandlerMap.removeValue(forKey: Int(truncating: cbId))
|
|
192
222
|
}
|
|
193
223
|
|
|
194
224
|
// MARK: - FireworkVideoShoppingDelegate
|
|
@@ -253,15 +283,37 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
|
|
|
253
283
|
|
|
254
284
|
func fireworkShopping(
|
|
255
285
|
_ fireworkShopping: FireworkVideoShopping,
|
|
256
|
-
|
|
257
|
-
|
|
286
|
+
productVariantCTASelected item: SelectedProductVariant,
|
|
287
|
+
fromVideo video: VideoDetails,
|
|
288
|
+
ctaCompletionHandler: @escaping ShoppingCTAHandler
|
|
258
289
|
) {
|
|
259
290
|
let callbackId = ShoppingModule.generateCallbackId()
|
|
260
|
-
self.
|
|
261
|
-
|
|
291
|
+
self.shoppingCTAHandlerMap[callbackId] = ctaCompletionHandler
|
|
262
292
|
sendEvent(
|
|
263
|
-
withName: ShoppingEventName.
|
|
264
|
-
body: [
|
|
293
|
+
withName: ShoppingEventName.ctaButtonClick.rawValue,
|
|
294
|
+
body: [
|
|
295
|
+
"url": item.url?.absoluteString ?? "",
|
|
296
|
+
"productId": item.productID,
|
|
297
|
+
"unitId": item.unitID,
|
|
298
|
+
"callbackId": callbackId
|
|
299
|
+
])
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
func fireworkShopping(
|
|
303
|
+
_ fireworkShopping: FireworkVideoShopping,
|
|
304
|
+
didTapLinkButtonAt item: SelectedProductVariant,
|
|
305
|
+
withURL itemURL: String
|
|
306
|
+
) -> Bool {
|
|
307
|
+
if customClickLinkButtonEnabled {
|
|
308
|
+
sendEvent(
|
|
309
|
+
withName: ShoppingEventName.customLinkButtonClick.rawValue,
|
|
310
|
+
body: [
|
|
311
|
+
"url": itemURL,
|
|
312
|
+
"productId": item.productID,
|
|
313
|
+
"unitId": item.unitID
|
|
314
|
+
])
|
|
315
|
+
}
|
|
316
|
+
return customClickLinkButtonEnabled
|
|
265
317
|
}
|
|
266
318
|
|
|
267
319
|
// MARK: - CartViewControllerProviding
|
|
@@ -283,27 +335,52 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
|
|
|
283
335
|
}
|
|
284
336
|
|
|
285
337
|
extension ShoppingModule {
|
|
286
|
-
|
|
338
|
+
// swiftlint:disable:next cyclomatic_complexity
|
|
287
339
|
fileprivate static func hydrateProductViewConfig(
|
|
288
340
|
_ config: ProductInfoViewConfiguration,
|
|
289
341
|
_ productInfoViewConfigurator: ProductInfoViewConfigurable
|
|
290
342
|
) {
|
|
291
343
|
var productDetailsConfiguration = productInfoViewConfigurator.productDetailsConfiguration
|
|
292
344
|
|
|
293
|
-
if let
|
|
345
|
+
if let ctaButtonConfiguration = config.ctaButton {
|
|
346
|
+
if let text = ctaButtonConfiguration.text {
|
|
347
|
+
switch text {
|
|
348
|
+
case .addToCart:
|
|
349
|
+
productDetailsConfiguration.ctaButton.text = .addToCart
|
|
350
|
+
case .shopNow:
|
|
351
|
+
productDetailsConfiguration.ctaButton.text = .shopNow
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if let backgroundColor = ctaButtonConfiguration.backgroundColor,
|
|
355
|
+
backgroundColor.count > 0 {
|
|
356
|
+
productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = backgroundColor.uicolor()
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if let textColor = ctaButtonConfiguration.textColor,
|
|
360
|
+
textColor.count > 0 {
|
|
361
|
+
productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = textColor.uicolor()
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if let fontSize = ctaButtonConfiguration.fontSize {
|
|
365
|
+
let iOSFontInfo = ctaButtonConfiguration.iOSFontInfo ?? FontInfo()
|
|
366
|
+
productDetailsConfiguration.ctaButton.buttonConfiguration.font = iOSFontInfo.getFont(fontSize)
|
|
367
|
+
}
|
|
368
|
+
} else if let addToCartButtonConfiguration = config.addToCartButton {
|
|
369
|
+
productDetailsConfiguration.ctaButton.text = .addToCart
|
|
370
|
+
|
|
294
371
|
if let backgroundColor = addToCartButtonConfiguration.backgroundColor,
|
|
295
372
|
backgroundColor.count > 0 {
|
|
296
|
-
productDetailsConfiguration.
|
|
373
|
+
productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = backgroundColor.uicolor()
|
|
297
374
|
}
|
|
298
375
|
|
|
299
376
|
if let textColor = addToCartButtonConfiguration.textColor,
|
|
300
377
|
textColor.count > 0 {
|
|
301
|
-
productDetailsConfiguration.
|
|
378
|
+
productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = textColor.uicolor()
|
|
302
379
|
}
|
|
303
380
|
|
|
304
381
|
if let fontSize = addToCartButtonConfiguration.fontSize {
|
|
305
382
|
let iOSFontInfo = addToCartButtonConfiguration.iOSFontInfo ?? FontInfo()
|
|
306
|
-
productDetailsConfiguration.
|
|
383
|
+
productDetailsConfiguration.ctaButton.buttonConfiguration.font = iOSFontInfo.getFont(fontSize)
|
|
307
384
|
}
|
|
308
385
|
}
|
|
309
386
|
|
|
@@ -381,5 +458,4 @@ extension ShoppingModule {
|
|
|
381
458
|
}
|
|
382
459
|
return result
|
|
383
460
|
}
|
|
384
|
-
|
|
385
461
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
packageJsonPath = File.expand_path("#{__dir__}/../../../package.json")
|
|
4
|
+
package = JSON.parse(File.read(packageJsonPath))
|
|
5
|
+
|
|
6
|
+
Pod::Spec.new do |s|
|
|
7
|
+
s.name = 'FWMultiHostStreaming'
|
|
8
|
+
s.version = package['version']
|
|
9
|
+
s.summary = 'Firework Multi Host Streaming'
|
|
10
|
+
s.homepage = package['homepage']
|
|
11
|
+
s.license = 'Apache License, Version 2.0'
|
|
12
|
+
s.authors = package['author']
|
|
13
|
+
|
|
14
|
+
s.platforms = { ios: '13.0' }
|
|
15
|
+
s.source = { git: 'https://github.com/loopsocial/bogano.git', tag: "#{s.version}" }
|
|
16
|
+
|
|
17
|
+
s.swift_version = '5.0'
|
|
18
|
+
s.source_files = 'src/*.{h,m,mm,swift}', 'src/**/*.{h,m,mm,swift}'
|
|
19
|
+
|
|
20
|
+
s.dependency 'AgoraRtcEngine_iOS', '~> 4.1.1'
|
|
21
|
+
s.dependency 'SwiftProtobuf', '~> 1.21.0'
|
|
22
|
+
s.dependency 'FireworkVideoAgoraSupport', '~> 0.2.0'
|
|
23
|
+
s.dependency 'FireworkVideo'
|
|
24
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FireworkMultiHostStreaming.swift
|
|
3
|
+
//
|
|
4
|
+
// Created by linjie jiang on 3/31/23.
|
|
5
|
+
//
|
|
6
|
+
|
|
7
|
+
import Foundation
|
|
8
|
+
|
|
9
|
+
import FireworkVideo
|
|
10
|
+
import FireworkVideoAgoraSupport
|
|
11
|
+
|
|
12
|
+
@objc
|
|
13
|
+
public class MultiHostStreamingSDK: NSObject {
|
|
14
|
+
@objc public static func enableMultiHostStreaming() {
|
|
15
|
+
FireworkVideoSDK.enableMultiHostPlayback()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Bundle+FWSwizzle.swift
|
|
3
|
+
//
|
|
4
|
+
// Created by linjie jiang on 2023/2/7.
|
|
5
|
+
//
|
|
6
|
+
|
|
7
|
+
import Foundation
|
|
8
|
+
import FireworkVideo
|
|
9
|
+
|
|
10
|
+
public extension Bundle {
|
|
11
|
+
static func swizzleMethodsForBundle() {
|
|
12
|
+
FWSwizzleUtil.swizzleSelector(
|
|
13
|
+
cls: self,
|
|
14
|
+
originalSelector: #selector(Bundle.localizedString(forKey:value:table:)),
|
|
15
|
+
customSelector: #selector(Bundle.fw_localizedString(forKey:value:table:)))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@objc func fw_localizedString(
|
|
19
|
+
forKey key: String,
|
|
20
|
+
value: String?,
|
|
21
|
+
table tableName: String?
|
|
22
|
+
) -> String {
|
|
23
|
+
if let language = FWAppLanguageManager.shared.appLanguage,
|
|
24
|
+
let languageCode = FWAppLanguageManager.shared.appLanguageCode,
|
|
25
|
+
Bundle(for: FireworkVideoSDK.self) == self {
|
|
26
|
+
let iOSSDKBundle = Bundle(for: FireworkVideoSDK.self)
|
|
27
|
+
|
|
28
|
+
var languageBundlePath: String?
|
|
29
|
+
let defaultLanguageBundlePath = iOSSDKBundle.path(forResource: "Base", ofType: "lproj")
|
|
30
|
+
if languageCode == "en" {
|
|
31
|
+
languageBundlePath = defaultLanguageBundlePath
|
|
32
|
+
} else if let path = iOSSDKBundle.path(forResource: language, ofType: "lproj") {
|
|
33
|
+
languageBundlePath = path
|
|
34
|
+
} else if let path = iOSSDKBundle.path(forResource: languageCode, ofType: "lproj") {
|
|
35
|
+
languageBundlePath = path
|
|
36
|
+
} else {
|
|
37
|
+
let targeLanguageList = iOSSDKBundle.localizations.filter { item in
|
|
38
|
+
return item != "Base" && item != "en"
|
|
39
|
+
}
|
|
40
|
+
if let targeLanguage = targeLanguageList.first(where: { item in
|
|
41
|
+
let targeLanguageCode = FWLanguageUtil.getLanguageCode(item)
|
|
42
|
+
return languageCode == targeLanguageCode
|
|
43
|
+
}) {
|
|
44
|
+
languageBundlePath = iOSSDKBundle.path(forResource: targeLanguage, ofType: "lproj")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if let resultLanguageBundlePath = languageBundlePath ?? defaultLanguageBundlePath,
|
|
49
|
+
let resultLanguageBundle = Bundle(path: resultLanguageBundlePath) {
|
|
50
|
+
return resultLanguageBundle.fw_localizedString(forKey: key, value: value, table: tableName)
|
|
51
|
+
} else {
|
|
52
|
+
return self.fw_localizedString(forKey: key, value: value, table: tableName)
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
return self.fw_localizedString(forKey: key, value: value, table: tableName)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FWAppLanguageManager.swift
|
|
3
|
+
//
|
|
4
|
+
// Created by linjie jiang on 2023/2/7.
|
|
5
|
+
//
|
|
6
|
+
|
|
7
|
+
import Foundation
|
|
8
|
+
|
|
9
|
+
private let appLanguageStorageKey = "firework_sdk_app_language_storage_key"
|
|
10
|
+
|
|
11
|
+
public enum AppLanguageLayoutDirection: Int {
|
|
12
|
+
case ltr, rtl, unsupported
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public class FWAppLanguageManager {
|
|
16
|
+
public static let shared = FWAppLanguageManager()
|
|
17
|
+
|
|
18
|
+
private var _systemLanguage: String?
|
|
19
|
+
private var _systemLanguageCode: String? {
|
|
20
|
+
guard let language = _systemLanguage else {
|
|
21
|
+
return nil
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return FWLanguageUtil.getLanguageCode(language)
|
|
25
|
+
}
|
|
26
|
+
private var _hasInitialized: Bool = false
|
|
27
|
+
|
|
28
|
+
public var appLanguage: String? {
|
|
29
|
+
didSet {
|
|
30
|
+
if appLanguage != nil {
|
|
31
|
+
FWAppLanguageManager.swizzelMethods()
|
|
32
|
+
FWRTLManager.sharedInstance().enableHorizontalFlip = shouldHorizontalFlip
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public var appLanguageCode: String? {
|
|
38
|
+
guard let language = appLanguage else {
|
|
39
|
+
return nil
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return FWLanguageUtil.getLanguageCode(language)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public var appLanguageLayoutDirection: AppLanguageLayoutDirection? {
|
|
46
|
+
guard let languageCode = appLanguageCode else {
|
|
47
|
+
return nil
|
|
48
|
+
}
|
|
49
|
+
let direction = Locale.characterDirection(forLanguage: languageCode)
|
|
50
|
+
switch direction {
|
|
51
|
+
case .leftToRight:
|
|
52
|
+
return .ltr
|
|
53
|
+
case .rightToLeft:
|
|
54
|
+
return .rtl
|
|
55
|
+
default:
|
|
56
|
+
return .unsupported
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public var shouldHorizontalFlip: Bool {
|
|
61
|
+
guard let appLanguageCode = appLanguageCode else {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
guard let systemLanguageCode = _systemLanguageCode else {
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let appLanguageDirection = Locale.characterDirection(forLanguage: appLanguageCode)
|
|
70
|
+
let systemLanguageDirection = Locale.characterDirection(forLanguage: systemLanguageCode)
|
|
71
|
+
|
|
72
|
+
if appLanguageDirection == .leftToRight, systemLanguageDirection == .rightToLeft {
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if appLanguageDirection == .rightToLeft, systemLanguageDirection == .leftToRight {
|
|
77
|
+
return true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private static func swizzelMethods() {
|
|
84
|
+
DispatchQueue.once {
|
|
85
|
+
UIViewController.swizzleMethodsForViewController()
|
|
86
|
+
Bundle.swizzleMethodsForBundle()
|
|
87
|
+
URLSession.swizzleMethodsForURLSession()
|
|
88
|
+
NumberFormatter.swizzleMethodsForNumberFormatter()
|
|
89
|
+
UIImageView.swizzleMethodsForImageView()
|
|
90
|
+
UILabel.swizzleMethodsForLabel()
|
|
91
|
+
UITextField.swizzleMethodsForTextField()
|
|
92
|
+
UITextView.swizzleMethodsForTextView()
|
|
93
|
+
UIWindow.swizzleMethodsForWindow()
|
|
94
|
+
UIView.swizzleMethodsForView()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private init() {
|
|
99
|
+
initializeManager()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public func initializeManager() {
|
|
103
|
+
guard !_hasInitialized else {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
_hasInitialized = true
|
|
107
|
+
|
|
108
|
+
self._systemLanguage = Locale.preferredLanguages.first
|
|
109
|
+
let cachedAppLanguage = UserDefaults.standard.object(forKey: appLanguageStorageKey) as? String
|
|
110
|
+
if FWLanguageUtil.isValidLanguage(cachedAppLanguage) {
|
|
111
|
+
self.appLanguage = cachedAppLanguage
|
|
112
|
+
}
|
|
113
|
+
#if DEBUG
|
|
114
|
+
print("""
|
|
115
|
+
FWAppLanguageManager init method
|
|
116
|
+
current app language: \(self.appLanguage ?? "")
|
|
117
|
+
system language: \(self._systemLanguage ?? "")
|
|
118
|
+
""")
|
|
119
|
+
#endif
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public func changeAppLanguage(_ language: String?) -> Bool {
|
|
123
|
+
guard let language = language, language.count > 0 else {
|
|
124
|
+
appLanguage = nil
|
|
125
|
+
UserDefaults.standard.removeObject(forKey: appLanguageStorageKey)
|
|
126
|
+
UserDefaults.standard.synchronize()
|
|
127
|
+
return true
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if FWLanguageUtil.isValidLanguage(language) {
|
|
131
|
+
appLanguage = language
|
|
132
|
+
UserDefaults.standard.set(language, forKey: appLanguageStorageKey)
|
|
133
|
+
UserDefaults.standard.synchronize()
|
|
134
|
+
return true
|
|
135
|
+
} else {
|
|
136
|
+
return false
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|