@@ -50,6 +50,9 @@ public class AppsFlyerDestination: UIResponder, DestinationPlugin {
5050 private weak var segDelegate : AppsFlyerLibDelegate ?
5151 private weak var segDLDelegate : DeepLinkDelegate ?
5252
53+ private var isFirstLaunch = true
54+ private var manualMode : Bool = false
55+
5356 // MARK: - Initialization
5457
5558 /// Creates and returns an AppsFlyer destination plugin for the Segment SDK
@@ -60,9 +63,11 @@ public class AppsFlyerDestination: UIResponder, DestinationPlugin {
6063 /// - segDelegate: When provided, this delegate will get called back for all AppsFlyerDelegate methods - ``onConversionDataSuccess(_:)``, ``onConversionDataFail(_:)``, ``onAppOpenAttribution(_:)``, ``onAppOpenAttributionFailure(_:)``
6164 /// - segDLDelegate: When provided, this delegate will get called back for all DeepLinkDelegate routines, or just ``didResolveDeeplink``
6265 public init ( segDelegate: AppsFlyerLibDelegate ? = nil ,
63- segDLDelegate: DeepLinkDelegate ? = nil ) {
66+ segDLDelegate: DeepLinkDelegate ? = nil ,
67+ manualMode: Bool = false ) {
6468 self . segDelegate = segDelegate
6569 self . segDLDelegate = segDLDelegate
70+ self . manualMode = manualMode
6671 }
6772
6873 // MARK: - Plugin
@@ -75,15 +80,46 @@ public class AppsFlyerDestination: UIResponder, DestinationPlugin {
7580
7681 AppsFlyerLib . shared ( ) . appsFlyerDevKey = settings. appsFlyerDevKey
7782 AppsFlyerLib . shared ( ) . appleAppID = settings. appleAppID
83+ AppsFlyerLib . shared ( ) . setPluginInfo ( plugin: Plugin . segment, version: " 2.0.0 " , additionalParams: [ " Segment " : " Analytics-Swift " , " Platform " : " iOS " ] )
7884
79- AppsFlyerLib . shared ( ) . waitForATTUserAuthorization ( timeoutInterval: 60 ) //OPTIONAL
85+ // Commented this in order to let the developer set it as suits them.
86+ // It is available by the developer with AppsFlyerLib.shared() on their iOS native code.
87+ // AppsFlyerLib.shared().waitForATTUserAuthorization(timeoutInterval: 60) //OPTIONAL
8088 AppsFlyerLib . shared ( ) . deepLinkDelegate = self //OPTIONAL
89+ // AppsFlyerLib.shared().isDebug = true
8190
8291 let trackAttributionData = settings. trackAttributionData
8392
8493 if trackAttributionData ?? false {
8594 AppsFlyerLib . shared ( ) . delegate = self
8695 }
96+
97+ // Manual mode is a mode which let's the developer the abillity to start the SDK.
98+ // Once setting manualMode=true in the init, the develper should use startAppsflyerSDK method to start the SDK.
99+ // Once started the SDK it will be start automatically by the life cycle - didBecomeActiveNotification.
100+ if ( !manualMode) {
101+ startAFSDK ( )
102+ NotificationCenter . default. addObserver ( self , selector: #selector( listenerStartSDK) , name: UIApplication . didBecomeActiveNotification, object: nil )
103+ }
104+ }
105+
106+ // This is for the Manual Mode !
107+ // Once calling this function every didBecomeActive start will be called.
108+ public func startAppsflyerSDK( ) {
109+ startAFSDK ( )
110+ NotificationCenter . default. addObserver ( self , selector: #selector( listenerStartSDK) , name: UIApplication . didBecomeActiveNotification, object: nil )
111+ }
112+
113+ private func startAFSDK( ) {
114+ AppsFlyerLib . shared ( ) . start ( )
115+ }
116+
117+ @objc func listenerStartSDK( ) {
118+ if ( isFirstLaunch) {
119+ isFirstLaunch = false
120+ return
121+ }
122+ startAFSDK ( )
87123 }
88124
89125 public func identify( event: IdentifyEvent ) -> IdentifyEvent ? {
@@ -92,59 +128,62 @@ public class AppsFlyerDestination: UIResponder, DestinationPlugin {
92128 }
93129
94130 if let traits = event. traits? . dictionaryValue {
95- var aFTraits : [ AnyHashable : Any ] = [ : ]
131+ var afTraits : [ AnyHashable : Any ] = [ : ]
96132
97133 if let email = traits [ " email " ] as? String {
98- aFTraits [ " email " ] = email
134+ afTraits [ " email " ] = email
99135 }
100136
101137 if let firstName = traits [ " firstName " ] as? String {
102- aFTraits [ " firstName " ] = firstName
138+ afTraits [ " firstName " ] = firstName
103139 }
104140
105141 if let lastName = traits [ " lastName " ] as? String {
106- aFTraits [ " lastName " ] = lastName
142+ afTraits [ " lastName " ] = lastName
143+ }
144+
145+ if let username = traits [ " username " ] as? String {
146+ afTraits [ " username " ] = username
107147 }
108148
109149 if traits [ " currencyCode " ] != nil {
110150 AppsFlyerLib . shared ( ) . currencyCode = traits [ " currencyCode " ] as? String
111151 }
112152
113- AppsFlyerLib . shared ( ) . customData = aFTraits
153+ AppsFlyerLib . shared ( ) . customData = afTraits
114154 }
115155
116156 return event
117157 }
118158
119159 public func track( event: TrackEvent ) -> TrackEvent ? {
120-
160+ // Verify we are not looping an event.
161+ if ( event. event == " Install Attributed " ||
162+ event. event == " Organic Install " ||
163+ event. event == " Deep Link Opened " ||
164+ event. event == " Direct Deep Link " ||
165+ event. event == " Deferred Deep Link " ) {
166+ return nil
167+ }
121168 var properties = event. properties? . dictionaryValue
122169
123170 let revenue : Double ? = extractRevenue ( key: " revenue " , from: properties)
124- let currency : String ? = extractCurrency ( key: " currency " , from: properties, withDefault : " USD " )
171+ let currency : String ? = extractCurrency ( key: " currency " , from: properties)
125172
126173 if let afRevenue = revenue, let afCurrency = currency {
127174 properties ? [ " af_revenue " ] = afRevenue
128175 properties ? [ " af_currency " ] = afCurrency
129176
130177 properties? . removeValue ( forKey: " revenue " )
131- properties? . removeValue ( forKey: " currency " )
132-
133- AppsFlyerLib . shared ( ) . logEvent ( event. event, withValues: properties)
134-
135- } else {
136- AppsFlyerLib . shared ( ) . logEvent ( event. event, withValues: properties)
178+ properties? . removeValue ( forKey: " currency " )
137179 }
138-
180+
181+ AppsFlyerLib . shared ( ) . logEvent ( event. event, withValues: properties)
139182 return event
140183 }
141184}
142185
143186extension AppsFlyerDestination : RemoteNotifications , iOSLifecycle {
144- public func applicationDidBecomeActive( application: UIApplication ? ) {
145- AppsFlyerLib . shared ( ) . start ( )
146- }
147-
148187 public func openURL( _ url: URL , options: [ UIApplication . OpenURLOptionsKey : Any ] ) {
149188 AppsFlyerLib . shared ( ) . handleOpen ( url, options: options)
150189 }
@@ -168,25 +207,30 @@ extension AppsFlyerDestination: UserActivities {
168207// https://github.com/AppsFlyerSDK/segment-appsflyer-ios/blob/master/segment-appsflyer-ios/Classes/SEGAppsFlyerIntegration.m#L148
169208extension AppsFlyerDestination {
170209 internal func extractRevenue( key: String , from properties: [ String : Any ] ? ) -> Double ? {
210+ guard let value = properties ? [ key] else {
211+ return nil
212+ }
171213
172- guard let revenueProperty = properties ? [ key] as? Double else { return nil }
173-
174- if let revenue = properties ? [ " revenue " ] as? String {
175- let revenueProperty = Double ( revenue)
176- return revenueProperty
177-
214+ if let doubleValue = value as? Double {
215+ return doubleValue
216+ } else if let stringValue = value as? String {
217+ return Double ( stringValue)
178218 }
179- return revenueProperty
219+
220+ return nil
180221 }
181222
182223
183- internal func extractCurrency( key: String , from properties: [ String : Any ] ? , withDefault value: String ? = nil ) -> String ? {
224+ internal func extractCurrency( key: String , from properties: [ String : Any ] ? ) -> String ? {
225+ guard let value = properties ? [ key] else {
226+ return nil
227+ }
184228
185- if let currency = properties ? [ key ] as? String {
186- return currency
229+ if let stringValue = value as? String {
230+ return stringValue
187231 }
188232
189- return " USD "
233+ return nil
190234 }
191235
192236}
@@ -233,9 +277,7 @@ extension AppsFlyerDestination: AppsFlyerLibDelegate {
233277 } else {
234278 analytics? . track ( name: " Organic Install " )
235279 }
236- } else {
237280 }
238-
239281 }
240282
241283 public func onConversionDataFail( _ error: Error ) {
0 commit comments