Commit aa43b4f1 by Dmitriy Stepanets

Processing integrating new modules

parent 38815a08
......@@ -12,7 +12,7 @@
<key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>53</integer>
<integer>55</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -2,6 +2,9 @@
<Workspace
version = "1.0">
<FileRef
location = "group:OneWeatherAnalytics/OneWeatherAnalytics.xcodeproj">
</FileRef>
<FileRef
location = "group:DelayedSaveStorage/DelayedSaveStorage.xcodeproj">
</FileRef>
<FileRef
......
......@@ -23,7 +23,7 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>57</integer>
<integer>56</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......@@ -72,7 +72,7 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>56</integer>
<integer>57</integer>
</dict>
</dict>
</dict>
......
......@@ -13,6 +13,7 @@ import IASDKCore
import MoEngage
import Flurry_iOS_SDK
import Firebase
import FirebaseCore
import FirebaseCoreDiagnostics
import CommonCrypto
import CryptoKit
......
......@@ -16,7 +16,7 @@ public enum AnalyticsAttribute {
case splashFTUE
/// Use this instead of rawValue, because we might have different attributes with the same string value.
var attributeName: String {
public var attributeName: String {
switch self {
case .lastSeenCity:
return "LAST_SEEN_CITY"
......
......@@ -6,7 +6,6 @@
//
import Foundation
import OneWeatherCore
public class AppAnalytics {
private let services: [AnalyticsService]
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
......@@ -37,9 +37,14 @@ public final class Logger {
return
}
onMain {
if Thread.isMainThread {
Crashlytics.crashlytics().log(message)
}
else {
DispatchQueue.main.async {
Crashlytics.crashlytics().log(message)
}
}
let prefix = "\(Logger.prefix) \(componentName) [\(logLevelString(level: level))]"
if message.count < maxCharsPerMessage {
......
//
// OneWeatherAnalytics.h
// OneWeatherAnalytics
//
// Created by Dmitry Stepanets on 31.05.2021.
//
#import <Foundation/Foundation.h>
//! Project version number for OneWeatherAnalytics.
FOUNDATION_EXPORT double OneWeatherAnalyticsVersionNumber;
//! Project version string for OneWeatherAnalytics.
FOUNDATION_EXPORT const unsigned char OneWeatherAnalyticsVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <OneWeatherAnalytics/PublicHeader.h>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
//
// OneWeatherAnalyticsTests.swift
// OneWeatherAnalyticsTests
//
// Created by Dmitry Stepanets on 31.05.2021.
//
import XCTest
@testable import OneWeatherAnalytics
class OneWeatherAnalyticsTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
......@@ -7,6 +7,7 @@
//
import UIKit
import OneWeatherAnalytics
extension Notification.Name {
static let adConfigChanged = Notification.Name(rawValue: "AdConfig.Changed")
......@@ -38,7 +39,7 @@ class AdConfigManager: NSObject {
super.init()
self.configManager.add(delegate: self)
}
private let log = AdLogger(componentName: "AdConfigManager")
public var adConfig: AdConfig {
......
......@@ -9,7 +9,7 @@
import Foundation
import Firebase
import FirebaseRemoteConfig
import OneWeatherCore
import OneWeatherAnalytics
public struct AppConfig: Codable {
let popularCities: [GeoNamesPlace]?
......
//
// NWSAlertsManager.swift
// 1Weather
//
// Created by Demid Merzlyakov on 16.04.2021.
//
import Foundation
import OneWeatherAnalytics
public enum NWSError: Error {
case insufficientLocationInfo
case alreadyBeingUpdated
case badUrl
case networkError(Error?)
case badServerResponse(Error?)
}
public protocol NWSAlertsManagerDelegate: AnyObject {
func alertsListDidChange(in alertsManager: NWSAlertsManager)
func extendedInfoAvailable(from alertsManager: NWSAlertsManager, for alert: NWSAlert)
}
public class NWSAlertsManager {
public var delegates = MulticastDelegate<NWSAlertsManagerDelegate>()
public typealias Completion = ([NWSAlert]?, NWSError?) -> ()
#if DEBUG
public let updateInterval = TimeInterval(2 * 60) // 2 minutes
#else
public let updateInterval = TimeInterval(15 * 60) // 15 minutes
#endif
#if DEBUG
public var useStaging = true
#else
public var useStaging = false
#endif
private let baseUrlProduction = "https://nwsalert.onelouder.com"
private let baseUrlStaging = "https://sta-nwsalert.onelouder.com"
private let log = Logger(componentName: "NWSAlertsManager")
private var locationsBeingUpdated = Set<Location>()
private var extendedInfoBeingFetched = Set<NWSAlert>()
private var baseUrl: String {
useStaging ? baseUrlStaging : baseUrlProduction
}
/// This queue is needed to synchronize access to locationsBeingUpdated and extendedInfoBeingFetched. Also, to make logging more clear.
private let internalQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = "WdtWeatherSource Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
private lazy var currentEventsDateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
return dateFormatter
}()
public var alerts = [NWSAlert]()
public func loadAlerts(from locations: [Location]) {
internalQueue.addOperation { [weak self] in
var allAlerts = [NWSAlert]()
for location in locations {
if let locationAlerts = location.notifications?.nwsAlerts {
allAlerts = allAlerts + locationAlerts
}
}
if allAlerts.count > 0 {
self?.merge(alerts: allAlerts)
}
}
}
public func fetchActiveAlerts(for location: Location, completion: @escaping NWSAlertsManager.Completion) {
internalQueue.addOperation { [weak self] in
let extendedCompletion: NWSAlertsManager.Completion = { [weak self] (updatedLocation, error) in
self?.internalQueue.addOperation {
completion(updatedLocation, error)
self?.locationsBeingUpdated.remove(location)
}
}
self?.fetchActiveAlertsInternal(for: location, completion: extendedCompletion)
}
}
public func extendedInfo(for alert: NWSAlert) -> NWSAlertExtendedInfo? {
return alerts.first(where: { $0 == alert })?.extendedInfo
}
private func fetchActiveAlertsInternal(for location: Location, completion: @escaping NWSAlertsManager.Completion) {
guard !locationsBeingUpdated.contains(location) else {
completion(nil, .alreadyBeingUpdated)
return
}
locationsBeingUpdated.insert(location)
log.info("Start update for \(location)")
var params: [String: String] = [
"format": "json"
]
if let fips_code = location.fipsCode {
params["weather_id"] = fips_code
}
else if let coordinates = location.coordinates{
params["geo"] = String(format: "%.5f,%.5f", coordinates.latitude, coordinates.longitude)
}
else {
completion(nil, .insufficientLocationInfo)
return
}
params["echoCity"] = location.cityName
guard var urlComponents = URLComponents(string: self.baseUrl.appending("/current_events")) else {
log.error("Couldn't create URLComponents from \(self.baseUrl)")
completion(nil, .badUrl)
fatalError("Should never happen. Couldn't create URL components from \(self.baseUrl). This URL has to always be correct.") // Should never happen, but a lot of stuff that should never happen happens from time to time, so let's at least handle it gracefully in prod.
#if !DEBUG
return
#endif
}
urlComponents.queryItems = params.map { URLQueryItem(name: $0, value: $1) }
guard let url = urlComponents.url else {
log.error("Couldn't create URL with params: \(params)")
completion(nil, .badUrl)
return
}
log.debug("Network request (\(location)): \(url)")
let urlSession = URLSession.shared
let dataTask = urlSession.dataTask(with: url) { [weak self] (data, response, error) in
guard let self = self else { return }
guard let data = data else {
self.log.debug("Network response (\(location)): error \(String(describing: error))")
completion(nil, .networkError(error))
return
}
let responseBodyString = String(data: data, encoding: .utf8) ?? "<couldn't show as string"
self.log.debug("Network response (\(location)): \(responseBodyString)")
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(self.currentEventsDateFormatter)
do {
let response = try decoder.decode(NWSCurrentEventsReponse.self, from: data)
guard let events = response.events?.events else {
completion(nil, .badServerResponse(nil))
return
}
let eventsWithLocation: [NWSAlert] = events.map { (alert) -> NWSAlert in
var updated: NWSAlert = alert
updated.city = location.cityName ?? ""
updated.timeZone = location.timeZone
return updated
}
self.merge(alerts: eventsWithLocation)
completion(eventsWithLocation, nil)
}
catch {
completion(nil, .badServerResponse(error))
}
}
dataTask.resume()
}
private func fetchAllExtendedInfo(for alerts: [NWSAlert]) {
internalQueue.addOperation { [weak self] in
guard let self = self else { return }
let localExtendedInfoBeingFetched = self.extendedInfoBeingFetched
let alertsToFetch = alerts.filter { (alert: NWSAlert) -> Bool in
alert.extendedInfo == nil && !localExtendedInfoBeingFetched.contains(alert)
}
let parser = NWSAlertInfoParser()
for alert in alertsToFetch {
self.extendedInfoBeingFetched.insert(alert)
parser.fetchExtendedInfo(for: alert) { [weak self] (weatherMessage, extendedInfo) in
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard let alertIndex = self.alerts.firstIndex(where: { $0 == alert }) else {
return
}
self.alerts[alertIndex].weatherMessage = weatherMessage
self.alerts[alertIndex].extendedInfo = extendedInfo
self.internalQueue.addOperation {
self.extendedInfoBeingFetched.remove(alert)
}
self.delegates.invoke { (delegate) in
delegate.extendedInfoAvailable(from: self, for: self.alerts[alertIndex])
}
}
}
}
}
}
private func merge(alerts newAlerts: [NWSAlert]) {
//TODO: optimize
var resultingSet = Set<NWSAlert>()
func notTooOld(alert: NWSAlert) -> Bool {
return alert.expires.addingTimeInterval(7 * 24 * 3600) >= Date() // 7 days in the past or newer
}
for alert in self.alerts {
if notTooOld(alert: alert) {
resultingSet.insert(alert)
}
}
for alert in newAlerts {
if notTooOld(alert: alert) && !resultingSet.contains(alert) {
resultingSet.insert(alert)
}
}
// TODO: maybe we need a more sophisticated logic for sorting: take severity into account, for instance.
let resultingAlerts = resultingSet.sorted(by: { $1.expires > $0.expires })
// sync, because we don't want another merge to strat before assignment to self.alerts completes.
// A deadlock shouldn't happen unless main thread waits on internalQueue, which it doesn't.
DispatchQueue.main.sync {
self.alerts = resultingAlerts
self.delegates.invoke { (delegate) in
delegate.alertsListDidChange(in: self)
}
}
fetchAllExtendedInfo(for: resultingAlerts)
}
}
//
// PushNotificationsManager.swift
// OneWeather
//
// Created by Demid Merzlyakov on 18.01.2021.
// Copyright © 2021 OneLouder, Inc. All rights reserved.
//
import Foundation
import MoEngage
import AppsFlyerLib
import OneWeatherAnalytics
public class PushNotificationsManager: NSObject {
// MARK: - Private
private let log = Logger(componentName: "PushNotificationsManager")
private let configManager: ConfigManager
// MARK: - Public
public static let shared = PushNotificationsManager()
public init(configManager: ConfigManager = ConfigManager.shared) {
self.configManager = configManager
super.init()
self.configManager.add(delegate: self)
UNUserNotificationCenter.current().delegate = self
}
deinit {
self.configManager.remove(delegate: self)
}
// TODO: forced re-register on timeout
public func registerForRemoteNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { [weak self]
(granted, error) in
guard let self = self else { return }
if let error = error {
self.log.error("Error: \(error)")
}
else {
self.log.info("Granted: \(granted)")
}
onMain {
MoEngage.sharedInstance().registerForRemoteNotification(withCategories: nil, withUserNotificationCenterDelegate: self)
}
}
}
internal var lastSetFIPSList: String? = ""
internal var lastSetFIPSCode: String? = ""
private func updateNwsSubscriptions(with fipsList: String?, currentFipsCode: String?) {
if configManager.config.nwsAlertsViaMoEngageEnabled {
if fipsList != lastSetFIPSList {
log.info("Set \(AnalyticsAttribute.fipsList.attributeName) to '\(fipsList ?? "")'")
lastSetFIPSList = fipsList
}
if currentFipsCode != lastSetFIPSCode {
log.info("Set \(AnalyticsAttribute.lastSeenFipsCodeNonTracfone.attributeName) to '\(currentFipsCode ?? "")'")
lastSetFIPSCode = currentFipsCode
}
}
else {
log.info("Set \(AnalyticsAttribute.fipsList.attributeName) to nil, because nwsAlertsViaMoEngage are disabled via config.")
lastSetFIPSList = nil
log.info("Set \(AnalyticsAttribute.lastSeenFipsCodeNonTracfone.attributeName) to nil, because nwsAlertsViaMoEngage are disabled via config")
lastSetFIPSCode = nil
}
analytics(set: .fipsList, to: lastSetFIPSList)
analytics(set: .lastSeenFipsCodeNonTracfone, to: lastSetFIPSCode)
}
public func updateNwsSubscriptions(for locations: [Location], selectedLocation: Location?) {
let fipsCodes = locations.compactMap { $0.fipsCode }
let newFipsList = fipsCodes.joined(separator: ",")
updateNwsSubscriptions(with: newFipsList, currentFipsCode: selectedLocation?.fipsCode)
}
var lastKnownPushToken: String?
public func set(pushToken: Data) {
let tokenString = pushToken.map { String(format: "%02.2hhx", $0) }.joined()
self.lastKnownPushToken = tokenString
log.info("Got new APNS token: \(tokenString)")
MoEngage.sharedInstance().setPushToken(pushToken)
AppsFlyerLib.shared().registerUninstall(pushToken)
#if DEBUG
AppsFlyerLib.shared().useUninstallSandbox = true
#endif
}
}
extension PushNotificationsManager: ConfigManagerDelegate {
public func dataUpdated(by configManager: ConfigManager) {
updateNwsSubscriptions(with: lastSetFIPSList, currentFipsCode: lastSetFIPSCode)
}
}
extension PushNotificationsManager: UNUserNotificationCenterDelegate {
private enum MoEngageScreenName: String {
case mainScreen = "com.handmark.expressweather.ui.activities.mainactivity"
case detailsScreen = "com.handmark.expressweather.ui.activities.weatherdetailsactivity"
case videosScreen = "com.handmark.expressweather.ui.activities.videodetailsactivity"
case alertsScreen = "com.handmark.expressweather.ui.activities.alertactivity"
}
private func makeMoEngageDeeplinkUrl(from response: UNNotificationResponse) -> URL? {
guard let appExtra = response.notification.request.content.userInfo["app_extra"] as? [String: Any] else {
return nil
}
if let urlString = appExtra["moe_deeplink"] as? String, let url = URL(string: urlString) {
return url
}
return nil
}
private func switchLocationIfNeeded(parsing screenData: [String: Any]?, using router: DeeplinksRouter) {
guard let screenData = screenData else {
return
}
let cityId = screenData["location"] as? String
let lat = screenData["lat"] as? String
let lon = screenData["lon"] as? String
let fipsCode = screenData["fipsCode"] as? String
guard (lat != nil && lon != nil) || cityId != nil || fipsCode != nil else {
log.debug("MoEngage push: no location data found")
return
}
let newLoc = GeoNamesPlace()
newLoc.latitude = lat
newLoc.longitude = lon
newLoc.optionalCityId = cityId
newLoc.fipsCode = fipsCode
LocationManager.shared.addIfNeeded(partialLocation: newLoc, selectLocation: true) { success in
if !success {
self.log.error("Failed to add / switch the location")
}
}
log.info("MoEngage push: location found: \(newLoc)")
}
private func isMoEngagePush(_ response: UNNotificationResponse) -> Bool {
let userInfo = response.notification.request.content.userInfo
guard userInfo["moengage"] as? [String: Any] != nil else {
log.debug("No MoEngage data found.")
return false
}
return true
}
private func handleMoEngageDeeplinks(for response: UNNotificationResponse) {
let userInfo = response.notification.request.content.userInfo
guard userInfo["moengage"] as? [String: Any] != nil else {
log.debug("No MoEngage data found.")
return
}
log.info("MoEngage push received: \(userInfo)")
let router = DeeplinksRouter()
if let moEngageUrl = makeMoEngageDeeplinkUrl(from: response) {
router.open(url: moEngageUrl)
}
else {
guard let appExtra = userInfo["app_extra"] as? [String: Any] else {
log.error("MoEngage push: no app_extra found.")
return
}
guard let screenNameStr = (appExtra["screenName"] as? String)?.trim().lowercased(),
let screenName = MoEngageScreenName(rawValue: screenNameStr) else {
log.error("MoEngage push: screenName not found or is not correct.")
return
}
let screenData = appExtra["screenData"] as? [String: Any]
switchLocationIfNeeded(parsing: screenData, using: router)
switch screenName {
case .detailsScreen:
router.openWeatherDetail()
case .videosScreen:
router.openVideo()
case .mainScreen:
guard let launchScreenId = screenData?["LaunchScreenID"] as? String else {
log.error("MoEngage push: LaunchScreenID not found for a MainActivity screen.")
return
}
switch launchScreenId {
case "0":
router.openToday()
case "1":
router.openForecast(timePeriod: nil)
case "2":
router.openPrecipitation()
case "3":
router.openRadar()
case "4":
router.openSunMoon()
default:
log.error("MoEngage push: Unknown launch screen id: \(launchScreenId)")
}
case .alertsScreen:
router.openAlerts()
}
}
}
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
MoEngage.sharedInstance().userNotificationCenter(center, didReceive: response) // not sure we should call it for AppsFlyer notifications
log.debug("Got a push notification: \(response.notification.request.content.userInfo)")
if isMoEngagePush(response) {
handleMoEngageDeeplinks(for: response)
}
else {
log.info("Handle push notification with AppsFlyer")
AppsFlyerLib.shared().handlePushNotification(response.notification.request.content.userInfo)
}
completionHandler()
}
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if UIApplication.shared.applicationState == .active {
analytics(log: .ANALYTICS_PUSH_RECEIVED)
} else {
analytics(log: .ANALYTICS_PUSH_SELECTED, params: [.ANALYTICS_KEY_PUSH_NOTIFICATION_SOURCE: "background"])
}
log.debug("Got a push notification: \(notification.request.content.userInfo)");
completionHandler([.sound,.alert])
}
}
......@@ -7,7 +7,7 @@
import Foundation
import CoreLocation
import UIKit
import OneWeatherCore
import OneWeatherAnalytics
internal protocol DeviceLocationMonitorDelegate: AnyObject {
func deviceLocationMonitor(_ monitor: DeviceLocationMonitor, didUpdateLocation newLocation: PartialLocation)
......
......@@ -8,13 +8,13 @@
import Foundation
import CoreLocation
import UIKit
import MoEngage
import OneWeatherCore
import WDTWeatherSource
import BlendHealthSource
import BlendFIPSSource
import CoreDataStorage
import DelayedSaveStorage
import OneWeatherAnalytics
//import MoEngage
//import WDTWeatherSource
//import BlendHealthSource
//import BlendFIPSSource
//import CoreDataStorage
//import DelayedSaveStorage
public protocol LocationManagerDelegate: AnyObject {
func locationManager(_ locationManager: LocationManager, changedSelectedLocation newLocation: Location?)
......@@ -184,13 +184,15 @@ public class LocationManager {
}
}
public static let shared = LocationManager(
weatherUpdateSource: WdtWeatherSource(),
healthSource: BlendHealthSource(),
nwsAlertsManager: NWSAlertsManager(),
fipsSource: BlendFIPSSource(),
pushNotificationsManager: PushNotificationsManager.shared,
storage: DelayedSaveStorage(storage: CoreDataStorage(), delay: 2))
public static var shared:LocationManager!
// public static let shared = LocationManager(
// weatherUpdateSource: WdtWeatherSource(),
// healthSource: BlendHealthSource(),
// nwsAlertsManager: NWSAlertsManager(),
// fipsSource: BlendFIPSSource(),
// pushNotificationsManager: PushNotificationsManager.shared,
// storage: DelayedSaveStorage(storage: CoreDataStorage(), delay: 2))
public let maxLocationsCount = 12
private var loadedLocations = false
......
......@@ -6,7 +6,7 @@
//
import Foundation
import OneWeatherCore
import OneWeatherAnalytics
class NWSAlertInfoParser {
private var strongSelfReference: NWSAlertInfoParser?
......
//
// WdtWeatherSourceError.swift
// OneWeatherCore
//
// Created by Dmitry Stepanets on 31.05.2021.
//
import Foundation
public enum WdtWeatherSourceError: Error {
case insufficientLocationInfo
case badUrl
case networkError(Error?)
case badServerResponse(Error?)
case dataMergeError(String)
case alreadyBeingUpdated
}
......@@ -6,6 +6,7 @@
//
import Foundation
import OneWeatherAnalytics
public class DefaultSettingsFactory {
public func getSettings() -> DefaultSettings {
......
......@@ -6,7 +6,7 @@
//
import Foundation
import OneWeatherCore
import OneWeatherAnalytics
class LegacyMigrationManager {
typealias Completion = ([PartialLocation]?, MigrationError?) -> ()
......
......@@ -6,7 +6,6 @@
//
import Foundation
import OneWeatherCore
// Used in Settings and Widget
enum LegacyTempUnits: Int {
......
......@@ -11,29 +11,21 @@ def shared_pods
pod 'XMLCoder', '~> 0.12.0'
pod 'Localize-Swift'
pod 'Firebase/Crashlytics'
pod 'MoEngage-iOS-SDK'
pod 'AppsFlyerFramework'
pod 'Firebase/RemoteConfig'
pod 'GoogleUtilities'
# If updating the podspec, make sure to add a tag and push it to origin
pod 'Swarm', :git => 'git@gitlab.pinsightmedia.com:oneweather/wdt-skywisetilekit-ios.git', :branch => 'develop'
end
#Application
target '1Weather' do
shared_pods
# Pods for 1Weather
pod 'SnapKit'
pod 'BezierKit'
pod 'lottie-ios'
pod 'Cirque', :git => 'https://github.com/StepanetsDmtry/Cirque.git'
pod 'AlgoliaSearchClient', '~> 8.2'
# Analytics
pod 'Flurry-iOS-SDK/FlurrySDK'
pod 'MoEngage-iOS-SDK'
pod 'AppsFlyerFramework'
# Recommended: Add the Firebase pod for Google Analytics
pod 'Firebase/Analytics'
pod 'Firebase/RemoteConfig'
end
#Ad pods
def ad_pods
# Amazon A9
pod 'AmazonPublisherServicesSDK'
# Google Ads & Mediation
......@@ -41,7 +33,19 @@ target '1Weather' do
pod 'GoogleMobileAdsMediationFacebook'
pod 'GoogleMobileAdsMediationFyber'
pod 'GoogleMobileAdsMediationMoPub'
end
#Application
target '1Weather' do
shared_pods
ad_pods
# Pods for 1Weather
pod 'SnapKit'
pod 'BezierKit'
pod 'lottie-ios'
pod 'Cirque', :git => 'https://github.com/StepanetsDmtry/Cirque.git'
pod 'AlgoliaSearchClient', '~> 8.2'
pod 'PKHUD', '~> 5.0'
end
......@@ -57,6 +61,13 @@ target 'WDTWeatherSource' do
shared_pods
end
#Analytics
target 'OneWeatherAnalytics' do
project 'OneWeatherAnalytics/OneWeatherAnalytics.project'
shared_pods
ad_pods
end
target 'OneWeatherNotificationServiceExtension' do
use_frameworks!
pod 'MORichNotification'
......
PODS:
- AlgoliaSearchClient (8.8.1):
- Logging
- AmazonPublisherServicesSDK (4.0.0)
- AmazonPublisherServicesSDK (4.1.0)
- AppsFlyerFramework (6.3.0):
- AppsFlyerFramework/Main (= 6.3.0)
- AppsFlyerFramework/Main (6.3.0)
- BezierKit (0.11.0)
- Cirque (1.0.3)
- FBAudienceNetwork (6.4.1):
- FBAudienceNetwork (6.5.0):
- FBSDKCoreKit/Basics (>= 7.0.1)
- FBSDKCoreKit/Basics (9.3.0)
- Firebase/Analytics (8.0.0):
......@@ -108,15 +108,15 @@ PODS:
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
- PromisesObjC (~> 1.2)
- GoogleMobileAdsMediationFacebook (6.4.1.0):
- FBAudienceNetwork (= 6.4.1)
- Google-Mobile-Ads-SDK (>= 8.4.0)
- GoogleMobileAdsMediationFacebook (6.5.0.0):
- FBAudienceNetwork (= 6.5.0)
- Google-Mobile-Ads-SDK (~> 8.0)
- GoogleMobileAdsMediationFyber (7.8.5.0):
- Fyber_Marketplace_SDK (= 7.8.5)
- Google-Mobile-Ads-SDK (~> 8.0)
- GoogleMobileAdsMediationMoPub (5.16.2.0):
- Google-Mobile-Ads-SDK (>= 8.2.0)
- mopub-ios-sdk (= 5.16.2)
- GoogleMobileAdsMediationMoPub (5.17.0.0):
- Google-Mobile-Ads-SDK (~> 8.0)
- mopub-ios-sdk (= 5.17.0)
- GoogleUserMessagingPlatform (2.0.0)
- GoogleUtilities (7.4.1):
- GoogleUtilities/AppDelegateSwizzler (= 7.4.1)
......@@ -161,13 +161,13 @@ PODS:
- lottie-ios (3.2.3)
- MoEngage-iOS-SDK (7.0.4):
- MORichNotification (< 5.1.0, >= 5.0.0)
- mopub-ios-sdk (5.16.2):
- mopub-ios-sdk/MoPubSDK (= 5.16.2)
- mopub-ios-sdk/Core (5.16.2)
- mopub-ios-sdk/MoPubSDK (5.16.2):
- mopub-ios-sdk (5.17.0):
- mopub-ios-sdk/MoPubSDK (= 5.17.0)
- mopub-ios-sdk/Core (5.17.0)
- mopub-ios-sdk/MoPubSDK (5.17.0):
- mopub-ios-sdk/Core
- mopub-ios-sdk/NativeAds
- mopub-ios-sdk/NativeAds (5.16.2):
- mopub-ios-sdk/NativeAds (5.17.0):
- mopub-ios-sdk/Core
- MORichNotification (5.0.0)
- nanopb (2.30908.0):
......@@ -260,11 +260,11 @@ CHECKOUT OPTIONS:
SPEC CHECKSUMS:
AlgoliaSearchClient: bbafe7f014cc0b474646d794ae3675ef4e92f0d5
AmazonPublisherServicesSDK: f73e281872525d1b929dca506c3a0332d4f902fa
AmazonPublisherServicesSDK: 3ce8e44f693c585fe069dde00236a256187e2a56
AppsFlyerFramework: e5b13250a1b68887213efcd854238791d17cf50b
BezierKit: 8b9bc3aaaa34ede809ff7c7c8aecc2bbe3b86a89
Cirque: 1eb134f2180b33107106dc72e47340aefb054da3
FBAudienceNetwork: 1974ec59ef88102f284c29941d73d0cbec3f2874
FBAudienceNetwork: cfe55330dcc4e9a1df083c34a346ca7f8e32951e
FBSDKCoreKit: 0d1ae58388a458b8222f72025804cdc84eb5d0c3
Firebase: 73c3e3b216ec1ecbc54d2ffdd4670c65c749edb1
FirebaseABTesting: daebc95ec8829607d07dfe5e92dc3285aca29bc4
......@@ -279,16 +279,16 @@ SPEC CHECKSUMS:
Google-Mobile-Ads-SDK: 6f5c41bf73db1656e5b203ba9c31e3d0899a128d
GoogleAppMeasurement: c6bbc9753d046b5456dd4f940057fbad2c28419e
GoogleDataTransport: 11e3a5f2c190327df1a4a5d7e7ae3d4d5b9c9e4c
GoogleMobileAdsMediationFacebook: b1a98c90211ef76afd766804d07207c436709a47
GoogleMobileAdsMediationFacebook: ab5fbf7605dfc2a8dd5aeb2c2c5bc906c1185634
GoogleMobileAdsMediationFyber: 19c630966dffdaf7a454c329da27c4df1bbc9674
GoogleMobileAdsMediationMoPub: b565583c96b600e204496c037eb1fff294240490
GoogleMobileAdsMediationMoPub: 5c17c4e8553e1aa46376e08ac3a9f3ccf8f62698
GoogleUserMessagingPlatform: ab890ce5f6620f293a21b6bdd82e416a2c73aeca
GoogleUtilities: f8a43108b38a68eebe8b3540e1f4f2d28843ce20
Localize-Swift: 6f4475136bdb0d7b2882ea3d4ea919d70142b232
Logging: beeb016c9c80cf77042d62e83495816847ef108b
lottie-ios: c058aeafa76daa4cf64d773554bccc8385d0150e
MoEngage-iOS-SDK: 71451fb44893536b0c70ff7dace1989e6fa734ff
mopub-ios-sdk: ce5aa12abe59d42cb8866b1297bdff7b6a42e273
mopub-ios-sdk: 36d322902674b79b0560a1cb8d15af0548d364ea
MORichNotification: abf1f1aaed3a9366dc8e966c55652bff7716b3b8
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
PKHUD: 98f3e4bc904b9c916f1c5bb6d765365b5357291b
......@@ -297,6 +297,6 @@ SPEC CHECKSUMS:
Swarm: 95393cd52715744c94e3a8475bc20b4de5d79f35
XMLCoder: f884dfa894a6f8b7dce465e4f6c02963bf17e028
PODFILE CHECKSUM: 091a3e21eac8770cf37838fb2380f320d1c7b921
PODFILE CHECKSUM: ac13e6cc676f46278961b7e409eef9e4b7d2e951
COCOAPODS: 1.10.1
......@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
#import "DTBLog.h"
#import "DTBDebugProperties.h"
@class DTBSlotGroup;
......@@ -51,6 +52,7 @@ typedef enum
@property (nonatomic) DTBMRAIDPolicy mraidPolicy;
@property (nonatomic) NSArray<NSString *> * _Nullable mraidCustomVersions;
@property (nonatomic) BOOL isReady;
@property (nonatomic) DTBDebugProperties * _Nonnull debugProperties;
// Serverless Environment Markers
@property (nonatomic) NSArray * _Nonnull serverlessMarkers;
......
//
// DTBDebugProperties.h
// DTBiOSSDK
//
// Created by Amazon Publisher Services on 9/12/20.
// Copyright © 2020 amazon.com. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DTBDebugProperties : NSObject
// Test flag to test out or enable SKAdnetwork response
@property (nonatomic) BOOL skadnTestMode;
+ (BOOL)isDebugFlagTurnedOnForFeature:(NSString *)feature;
@end
NS_ASSUME_NONNULL_END
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment