From dd7fda810210162544494e07e64d28e45a856c9b Mon Sep 17 00:00:00 2001 From: Ryan Lepinski Date: Wed, 17 Apr 2024 07:33:15 -0700 Subject: [PATCH] Release 6.0.0 (#38) * Release 6.0.0 # Conflicts: # AirshipFrameworkProxy.podspec # android/gradle/libs.versions.toml # ios/Podfile * Update runner * Runner * Xcode version * Fix warning * Add back parse/names until 18.1.1 --- .github/workflows/ci.yml | 8 +- .github/workflows/release.yml | 11 +-- AirshipFrameworkProxy.podspec | 5 +- android/gradle/libs.versions.toml | 4 +- .../AirshipExtensions.swift | 18 +---- .../DefaultMessageCenterUI.swift | 34 ++++---- .../Proxies/AirshipAnalyticsProxy.swift | 27 +------ .../Proxies/AirshipChannelProxy.swift | 8 +- .../Proxies/AirshipContactProxy.swift | 6 +- .../Proxies/AirshipInAppProxy.swift | 11 ++- .../Proxies/AirshipLocaleProxy.swift | 14 +--- .../Proxies/AirshipProxy.swift | 12 +-- .../Proxies/AirshipPushProxy.swift | 78 ++++--------------- ios/Podfile | 2 +- ios/Podfile.lock | 32 ++++---- 15 files changed, 94 insertions(+), 176 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 976718c..fe7e7c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: [pull_request] env: BUNDLE_PATH: vendor/bundle - DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_15.3.app/Contents/Developer concurrency: group: ${{ github.ref }} @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: set up JDK 11 uses: actions/setup-java@v2 @@ -42,10 +42,10 @@ jobs: fail_on_failure: true ios: - runs-on: macOS-13-xl + runs-on: macos-14-xlarge timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Coreutils run: brew install coreutils diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5dee0de..42d050b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,14 +7,14 @@ on: env: BUNDLE_PATH: vendor/bundle - DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_15.3.app/Contents/Developer jobs: android: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Get the version id: get_version @@ -50,11 +50,12 @@ jobs: closeAndReleaseSonatypeStagingRepository ios: - runs-on: macOS-13-xl + runs-on: macos-14-xlarge timeout-minutes: 30 steps: - - uses: actions/checkout@v2 - + - uses: actions/checkout@v4 + + - name: Install Coreutils run: brew install coreutils diff --git a/AirshipFrameworkProxy.podspec b/AirshipFrameworkProxy.podspec index 4572f95..9db4d65 100644 --- a/AirshipFrameworkProxy.podspec +++ b/AirshipFrameworkProxy.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| - s.version = "5.4.2" + s.version = "6.0.0" s.name = "AirshipFrameworkProxy" s.summary = "Airship iOS mobile framework proxy" s.documentation_url = "https://docs.airship.com/platform/mobile" @@ -15,6 +15,5 @@ Pod::Spec.new do |s| s.requires_arc = true s.swift_version = "5.0" s.source_files = "ios/AirshipFrameworkProxy/**/*.{h,m,swift}" - s.dependency 'Airship', "17.9.1" - + s.dependency 'Airship', "18.1.0" end diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 34f87dc..51f20b3 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] # Airship -airshipProxy = '5.4.2' -airship = '17.7.4' +airshipProxy = '6.0.0' +airship = '17.8.0' # Gradle plugins androidGradlePlugin = '7.3.0' diff --git a/ios/AirshipFrameworkProxy/AirshipExtensions.swift b/ios/AirshipFrameworkProxy/AirshipExtensions.swift index 99fede3..687062f 100644 --- a/ios/AirshipFrameworkProxy/AirshipExtensions.swift +++ b/ios/AirshipFrameworkProxy/AirshipExtensions.swift @@ -52,7 +52,7 @@ extension UAAuthorizedNotificationSettings { } } -extension AirshipFeature: Codable { +extension AirshipFeature { static let nameMap: [String: AirshipFeature] = [ "push": .push, "contacts": .contacts, @@ -63,6 +63,7 @@ extension AirshipFeature: Codable { "all": .all, "none": [] ] + var names: [String] { var names: [String] = [] @@ -105,21 +106,6 @@ extension AirshipFeature: Codable { return features } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.names) - } - - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - if let names: [String] = try? container.decode([String].self) { - self = try AirshipFeature.parse(names) - } else { - throw AirshipErrors.error("Failed to parse features") - } - } } extension UANotificationOptions { diff --git a/ios/AirshipFrameworkProxy/DefaultMessageCenterUI.swift b/ios/AirshipFrameworkProxy/DefaultMessageCenterUI.swift index 0364a5e..7c86280 100644 --- a/ios/AirshipFrameworkProxy/DefaultMessageCenterUI.swift +++ b/ios/AirshipFrameworkProxy/DefaultMessageCenterUI.swift @@ -5,17 +5,17 @@ import SwiftUI public class DefaultMessageCenterUI { static let shared: DefaultMessageCenterUI = DefaultMessageCenterUI() - private var currentDisplay: Disposable? + private var currentDisplay: AirshipMainActorCancellable? private var controller: MessageCenterController = MessageCenterController() @MainActor func dismiss() { - self.currentDisplay?.dispose() + self.currentDisplay?.cancel() } @MainActor func display(messageID: String? = nil) { - guard let scene = try? AirshipUtils.findWindowScene() else { + guard let scene = try? AirshipSceneManager.shared.lastActiveScene else { AirshipLogger.error( "Unable to display message center, missing scene." ) @@ -24,7 +24,7 @@ public class DefaultMessageCenterUI { controller.navigate(messageID: messageID) - currentDisplay?.dispose() + currentDisplay?.cancel() AirshipLogger.debug("Opening default message center UI") @@ -36,15 +36,15 @@ public class DefaultMessageCenterUI { @MainActor func displayMessageView(messageID: String) { - guard let scene = try? AirshipUtils.findWindowScene() else { + guard let scene = try? AirshipSceneManager.shared.lastActiveScene else { AirshipLogger.error( "Unable to display message center, missing scene." ) return } - currentDisplay?.dispose() - + currentDisplay?.cancel() + self.currentDisplay = openMessageView( scene: scene, messageID: messageID, @@ -56,10 +56,10 @@ public class DefaultMessageCenterUI { private func open( scene: UIWindowScene, theme: MessageCenterTheme? - ) -> Disposable { + ) -> AirshipMainActorCancellable { var window: UIWindow? = UIWindow(windowScene: scene) - let disposable = Disposable { + let cancellable = AirshipMainActorCancellableBlock { window?.windowLevel = .normal window?.isHidden = true window = nil @@ -69,7 +69,7 @@ public class DefaultMessageCenterUI { theme: theme, controller: self.controller ) { - disposable.dispose() + cancellable.cancel() } window?.isHidden = false @@ -77,7 +77,7 @@ public class DefaultMessageCenterUI { window?.makeKeyAndVisible() window?.rootViewController = viewController - return disposable + return cancellable } @MainActor @@ -85,10 +85,10 @@ public class DefaultMessageCenterUI { scene: UIWindowScene, messageID: String, theme: MessageCenterTheme? - ) -> Disposable { + ) -> AirshipMainActorCancellable { var window: UIWindow? = UIWindow(windowScene: scene) - let disposable = Disposable { + let cancellable = AirshipMainActorCancellableBlock { window?.windowLevel = .normal window?.isHidden = true window = nil @@ -98,7 +98,7 @@ public class DefaultMessageCenterUI { let viewController = HostingViewController( rootView: StandaloneMessageView( dismissAction: { - disposable.dispose() + cancellable.cancel() }, content: { @@ -106,7 +106,7 @@ public class DefaultMessageCenterUI { messageID: messageID, title: nil ) { - disposable.dispose() + cancellable.cancel() } .messageCenterTheme(theme) } @@ -119,7 +119,7 @@ public class DefaultMessageCenterUI { window?.makeKeyAndVisible() window?.rootViewController = viewController - return disposable + return cancellable } } @@ -187,7 +187,7 @@ struct StandaloneMessageView: View { extension String { var airshipLocalizedString: String { - return LocalizationUtils.localizedString( + return AirshipLocalizationUtils.localizedString( self, withTable: "UrbanAirship", moduleBundle: AirshipCoreResources.bundle diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipAnalyticsProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipAnalyticsProxy.swift index d625943..d5f1bb7 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipAnalyticsProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipAnalyticsProxy.swift @@ -52,9 +52,10 @@ public class AirshipAnalyticsProxy { throw AirshipErrors.error("Invalid event: \(event)") } - try analytics.addCustomEvent(event: customEvent) + try analytics.recordCustomEvent(customEvent) } + @MainActor public func trackScreen(_ screen: String?) throws { try self.analytics.trackScreen(screen) } @@ -63,29 +64,9 @@ public class AirshipAnalyticsProxy { identifier: String?, key: String ) throws { - try self.analytics.associateIdentifier( - identifier: identifier, - key: key - ) - } - -} - -protocol AirshipAnalyticsProtocol: AnyObject { - func trackScreen(_ screen: String?) - func associateIdentifier(identifier: String?, key: String) - func addCustomEvent(event: CustomEvent) -} - -extension AirshipAnalytics: AirshipAnalyticsProtocol { - func associateIdentifier(identifier: String?, key: String) { - let identifiers = self.currentAssociatedDeviceIdentifiers() + let identifiers = try self.analytics.currentAssociatedDeviceIdentifiers() identifiers.set(identifier: identifier, key: key) - self.associateDeviceIdentifiers(identifiers) - } - - func addCustomEvent(event: CustomEvent) { - self.addEvent(event) + try self.analytics.associateDeviceIdentifiers(identifiers) } } diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipChannelProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipChannelProxy.swift index c092b96..6389baf 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipChannelProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipChannelProxy.swift @@ -31,7 +31,7 @@ public class AirshipChannelProxy { @objc public func editTags(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [TagOperation].self, from: data @@ -61,7 +61,7 @@ public class AirshipChannelProxy { @objc public func editTagGroups(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [TagGroupOperation].self, from: data @@ -78,7 +78,7 @@ public class AirshipChannelProxy { } public func editAttributes(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [AttributeOperation].self, from: data @@ -96,7 +96,7 @@ public class AirshipChannelProxy { } public func editSubscriptionLists(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [SubscriptionListOperation].self, from: data diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipContactProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipContactProxy.swift index a07d74c..328fadb 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipContactProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipContactProxy.swift @@ -53,7 +53,7 @@ public class AirshipContactProxy { } public func editTagGroups(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [TagGroupOperation].self, from: data @@ -71,7 +71,7 @@ public class AirshipContactProxy { @objc public func editAttributes(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [AttributeOperation].self, from: data @@ -89,7 +89,7 @@ public class AirshipContactProxy { } public func editSubscriptionLists(json: Any) throws { - let data = try JSONUtils.data(json) + let data = try AirshipJSONUtils.data(json) let operations = try JSONDecoder().decode( [ScopedSubscriptionListOperation].self, from: data diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipInAppProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipInAppProxy.swift index a469ecf..4a79eb2 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipInAppProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipInAppProxy.swift @@ -14,19 +14,23 @@ public class AirshipInAppProxy { self.inAppProvider = inAppProvider } + @MainActor public func isPaused() throws -> Bool { return try self.inApp.isPaused } @objc + @MainActor public func setPaused(_ paused: Bool) throws { try self.inApp.isPaused = paused } + @MainActor public func getDisplayInterval() throws -> Int { return Int(try self.inApp.displayInterval * 1000) } + @MainActor public func setDisplayInterval(_ displayInterval: Int) throws { let seconds = Double(displayInterval)/1000.0 try self.inApp.displayInterval = seconds @@ -34,17 +38,20 @@ public class AirshipInAppProxy { } protocol AirshipInAppProtocol: AnyObject { + @MainActor var displayInterval: TimeInterval { get set } + @MainActor var isPaused: Bool { get set } } extension InAppAutomation : AirshipInAppProtocol { + @MainActor var displayInterval: TimeInterval { get { - self.inAppMessageManager.displayInterval + self.inAppMessaging.displayInterval } set { - self.inAppMessageManager.displayInterval = newValue + self.inAppMessaging.displayInterval = newValue } } } diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipLocaleProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipLocaleProxy.swift index 8a3ba54..458d92c 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipLocaleProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipLocaleProxy.swift @@ -5,12 +5,12 @@ import AirshipKit public class AirshipLocaleProxy { - private let localeProvider: () throws -> AirshipLocaleProtocol - private var locale: AirshipLocaleProtocol { + private let localeProvider: () throws -> AirshipLocaleManagerProtocol + private var locale: AirshipLocaleManagerProtocol { get throws { try localeProvider() } } - init(localeProvider: @escaping () throws -> any AirshipLocaleProtocol) { + init(localeProvider: @escaping () throws -> any AirshipLocaleManagerProtocol) { self.localeProvider = localeProvider } @@ -35,11 +35,3 @@ public class AirshipLocaleProxy { } -protocol AirshipLocaleProtocol: AnyObject { - func clearLocale() - var currentLocale: Locale { get set } -} - -extension AirshipLocaleManager: AirshipLocaleProtocol { - -} diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipProxy.swift index e1465d5..5191e2b 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipProxy.swift @@ -42,7 +42,7 @@ public class AirshipProxy { self.proxyStore = proxyStore self.locale = AirshipLocaleProxy { try AirshipProxy.ensureAirshipReady() - return Airship.shared.localeManager + return Airship.localeManager } self.push = AirshipPushProxy(proxyStore: proxyStore) { @@ -90,7 +90,7 @@ public class AirshipProxy { self.privacyManager = AirshipPrivacyManagerProxy { try AirshipProxy.ensureAirshipReady() - return Airship.shared.privacyManager + return Airship.privacyManager } self.featureFlagManager = AirshipFeatureFlagManagerProxy { @@ -109,7 +109,7 @@ public class AirshipProxy { ) throws -> Bool { let proxyConfig = try JSONDecoder().decode( ProxyConfig.self, - from: try JSONUtils.data(json) + from: try AirshipJSONUtils.data(json) ) return try takeOff(config: proxyConfig, launchOptions: launchOptions) @@ -171,7 +171,7 @@ public class AirshipProxy { AirshipLogger.debug("Taking off! \(airshipConfig)") Airship.takeOff(airshipConfig, launchOptions: launchOptions) - Airship.shared.deepLinkDelegate = self.airshipDelegate + Airship.deepLinkDelegate = self.airshipDelegate Airship.push.registrationDelegate = self.airshipDelegate Airship.push.pushNotificationDelegate = self.airshipDelegate PreferenceCenter.shared.openDelegate = self.airshipDelegate @@ -194,7 +194,7 @@ public class AirshipProxy { .store(in: &self.subscriptions) NotificationCenter.default.addObserver( - forName: MessageCenterInbox.messageListUpdatedEvent, + forName: AirshipNotifications.MessageCenterListUpdated.name, object: nil, queue: .main ) { _ in @@ -202,7 +202,7 @@ public class AirshipProxy { } NotificationCenter.default.addObserver( - forName: AirshipChannel.channelCreatedEvent, + forName: AirshipNotifications.ChannelCreated.name, object: nil, queue: .main ) { _ in diff --git a/ios/AirshipFrameworkProxy/Proxies/AirshipPushProxy.swift b/ios/AirshipFrameworkProxy/Proxies/AirshipPushProxy.swift index bcf8046..5d6865b 100644 --- a/ios/AirshipFrameworkProxy/Proxies/AirshipPushProxy.swift +++ b/ios/AirshipFrameworkProxy/Proxies/AirshipPushProxy.swift @@ -83,8 +83,8 @@ public class AirshipPushProxy { } @MainActor - public func setBadgeNumber(_ badgeNumber: Int) throws { - try self.push.badgeNumber = badgeNumber + public func setBadgeNumber(_ badgeNumber: Int) async throws { + try await self.push.setBadgeNumber(badgeNumber) } @MainActor @@ -101,9 +101,9 @@ public class AirshipPushProxy { ) } - public func getQuietTime() throws -> QuietTimeSettings? { - guard let dict = try self.push.quietTime else { return nil } - return QuietTimeSettings(from: dict) + public func getQuietTime() throws -> ProxyQuietTimeSettings? { + guard let settings = try self.push.quietTime else { return nil } + return settings.proxySettings } public func setQuietTimeEnabled(_ enabled: Bool) throws -> Void { @@ -187,70 +187,22 @@ public class PresentationOptionsOverridesRequest { } } -protocol AirshipPushProtocol: AnyObject { - func enableUserPushNotifications() async -> Bool - var authorizationStatus: UAAuthorizationStatus { get } - var userPushNotificationsEnabled: Bool { get set } - var deviceToken: String? { get } - var notificationOptions: UANotificationOptions { get set } - var authorizedNotificationSettings: UAAuthorizedNotificationSettings { get } - var defaultPresentationOptions: UNNotificationPresentationOptions { get set} - @MainActor - var badgeNumber: Int { get set } - var autobadgeEnabled: Bool { get set } - var notificationStatus: AirshipNotificationStatus { get async } - var quietTime: [AnyHashable: Any]? { get } - var quietTimeEnabled: Bool { get set } - func setQuietTimeStartHour( - _ startHour: Int, - startMinute: Int, - endHour: Int, - endMinute: Int - ) -} - -extension AirshipPush: AirshipPushProtocol {} - -public struct QuietTimeSettings: Codable { +public struct ProxyQuietTimeSettings: Codable { let startHour: UInt let startMinute: UInt let endHour: UInt let endMinute: UInt +} - public init(startHour: UInt, startMinute: UInt, endHour: UInt, endMinute: UInt) throws { - guard startHour < 24, startMinute < 60 else { - throw AirshipErrors.error("Invalid start time") - } - - guard endHour < 24, endMinute < 60 else { - throw AirshipErrors.error("Invalid end time") - } - - self.startHour = startHour - self.startMinute = startMinute - self.endHour = endHour - self.endMinute = endMinute - } - - init?(from dictionary: [AnyHashable: Any]) { - guard - let startTime = dictionary["start"] as? String, - let endTime = dictionary["end"] as? String - else { - return nil - } - - let startParts = startTime.components(separatedBy:":").compactMap { UInt($0) } - let endParts = endTime.components(separatedBy:":").compactMap { UInt($0) } - - guard startParts.count == 2, endParts.count == 2 else { return nil } - - self.startHour = startParts[0] - self.startMinute = startParts[1] - self.endHour = endParts[0] - self.endMinute = endParts[1] +extension QuietTimeSettings { + var proxySettings: ProxyQuietTimeSettings { + return ProxyQuietTimeSettings( + startHour: startHour, + startMinute: startMinute, + endHour: endHour, + endMinute: endMinute + ) } } - diff --git a/ios/Podfile b/ios/Podfile index 1db5567..2b869f4 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ target 'AirshipFrameworkProxy' do - pod 'Airship', '17.9.1' + pod 'Airship', '18.1.0' end target 'AirshipFrameworkProxyTests' do diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 4d0b39f..f12fa1f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,33 +1,33 @@ PODS: - - Airship (17.9.0): - - Airship/Automation (= 17.9.0) - - Airship/Basement (= 17.9.0) - - Airship/Core (= 17.9.0) - - Airship/FeatureFlags (= 17.9.0) - - Airship/MessageCenter (= 17.9.0) - - Airship/PreferenceCenter (= 17.9.0) - - Airship/Automation (17.9.0): + - Airship (18.1.0): + - Airship/Automation (= 18.1.0) + - Airship/Basement (= 18.1.0) + - Airship/Core (= 18.1.0) + - Airship/FeatureFlags (= 18.1.0) + - Airship/MessageCenter (= 18.1.0) + - Airship/PreferenceCenter (= 18.1.0) + - Airship/Automation (18.1.0): - Airship/Core - - Airship/Basement (17.9.0) - - Airship/Core (17.9.0): + - Airship/Basement (18.1.0) + - Airship/Core (18.1.0): - Airship/Basement - - Airship/FeatureFlags (17.9.0): + - Airship/FeatureFlags (18.1.0): - Airship/Core - - Airship/MessageCenter (17.9.0): + - Airship/MessageCenter (18.1.0): - Airship/Core - - Airship/PreferenceCenter (17.9.0): + - Airship/PreferenceCenter (18.1.0): - Airship/Core DEPENDENCIES: - - Airship (= 17.9.0) + - Airship (= 18.1.0) SPEC REPOS: trunk: - Airship SPEC CHECKSUMS: - Airship: 4ef6143b418dcb1cc0c2754b58cfebdc5c60ec9e + Airship: e8909d114578d2261c35317dae00d28fb88ea13b -PODFILE CHECKSUM: aeddf1dc72986105724cea33b9dc7892e1b7ff9b +PODFILE CHECKSUM: 02da8cc921d0b64d5f52222660a32d106ffdb5ff COCOAPODS: 1.15.2