Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/next'
Browse files Browse the repository at this point in the history
  • Loading branch information
rlepinski committed Jul 1, 2024
2 parents 7850f5d + c78874e commit 930af2b
Show file tree
Hide file tree
Showing 33 changed files with 1,145 additions and 344 deletions.
40 changes: 40 additions & 0 deletions Airship/Airship.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extension URLSession: AssetDownloaderSession {
struct DefaultAssetDownloader : AssetDownloader {
var session: AssetDownloaderSession

init(session: AssetDownloaderSession = URLSession.shared) {
init(session: AssetDownloaderSession = URLSession.airshipSecureSession) {
self.session = session
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,16 @@ struct WKWebViewRepresentable: UIViewRepresentable {


private let parent: WKWebViewRepresentable
private let challengeResolver: ChallengeResolver
let nativeBridge: NativeBridge

init(_ parent: WKWebViewRepresentable, actionRunner: NativeBridgeActionRunner) {
init(_ parent: WKWebViewRepresentable, actionRunner: NativeBridgeActionRunner, resolver: ChallengeResolver = .shared) {
self.parent = parent
self.nativeBridge = NativeBridge(actionRunner: actionRunner)
self.challengeResolver = resolver

super.init()

self.nativeBridge.nativeBridgeExtensionDelegate =
self.parent.nativeBridgeExtension
self.nativeBridge.forwardNavigationDelegate = self
Expand Down Expand Up @@ -136,6 +140,14 @@ struct WKWebViewRepresentable: UIViewRepresentable {
webView?.reload()
}
}

func webView(
_ webView: WKWebView,
respondTo challenge: URLAuthenticationChallenge)
async -> (URLSession.AuthChallengeDisposition, URLCredential?) {

return await challengeResolver.resolve(challenge)
}

func performCommand(_ command: JavaScriptCommand, webView: WKWebView) -> Bool {
return false
Expand Down
17 changes: 17 additions & 0 deletions Airship/AirshipAutomation/Source/InAppMessage/View/MediaView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct InAppMessageMediaWebView: UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.scrollView.isScrollEnabled = false
webView.navigationDelegate = context.coordinator

if #available(iOS 16.4, *) {
webView.isInspectable = Airship.isFlying && Airship.config.isWebViewInspectionEnabled
Expand All @@ -75,6 +76,22 @@ struct InAppMessageMediaWebView: UIViewRepresentable {
break // Do nothing for images
}
}

func makeCoordinator() -> Coordinator {
return Coordinator()
}

class Coordinator: NSObject, WKNavigationDelegate {
let challengeResolver: ChallengeResolver

init(resolver: ChallengeResolver = .shared) {
self.challengeResolver = resolver
}

func webView(_ webView: WKWebView, respondTo challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
return await challengeResolver.resolve(challenge)
}
}
}

struct MediaInfo {
Expand Down
2 changes: 2 additions & 0 deletions Airship/AirshipCore/Source/Airship.swift
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ public class Airship: NSObject {
private class func commonTakeOff(_ config: AirshipConfig?, onReady: (() -> Void)? = nil) {

let resolvedConfig = config?.copy() as? AirshipConfig ?? AirshipConfig.default()

ChallengeResolver.shared.resolver = resolvedConfig.connectionChallengeResolver

self.logLevel = resolvedConfig.logLevel
self.logPrivacyLevel = resolvedConfig.logPrivacyLevel
Expand Down
73 changes: 29 additions & 44 deletions Airship/AirshipCore/Source/AirshipContact.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import Foundation
/// within Airship. Contacts may be named and have channels associated with it.
@objc(UAContact)
public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked Sendable {
static let refreshContactPushPayloadKey = "com.urbanairship.contact.update"

public var contactChannelUpdates: AsyncStream<ContactChannelsResult> {
get {
return self.contactChannelsProvider.contactChannels(
Expand Down Expand Up @@ -68,16 +70,15 @@ public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked
private let dataStore: PreferenceDataStore
private let config: RuntimeConfig
private let privacyManager: AirshipPrivacyManager
private let subscriptionListAPIClient: ContactSubscriptionListAPIClientProtocol
private let contactChannelsProvider: ContactChannelsProviderProtocol
private let subscriptionListProvider: SubscriptionListProviderProtocol
private let date: AirshipDateProtocol
private let audienceOverridesProvider: AudienceOverridesProvider
private let contactManager: ContactManagerProtocol
private var smsValidator: SMSValidatorProtocol
private let cachedSubscriptionLists: CachedValue<(String, [String: [ChannelScope]])>
private var setupTask: Task<Void, Never>? = nil
private var subscriptions: Set<AnyCancellable> = Set()
private let fetchSubscriptionListQueue: AirshipSerialQueue = AirshipSerialQueue()
private let serialQueue: AirshipAsyncSerialQueue

/// Publishes all edits made to the subscription lists through the SDK
Expand Down Expand Up @@ -159,8 +160,8 @@ public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked
config: RuntimeConfig,
channel: InternalAirshipChannelProtocol,
privacyManager: AirshipPrivacyManager,
subscriptionListAPIClient: ContactSubscriptionListAPIClientProtocol,
contactChannelsProvider: ContactChannelsProviderProtocol,
subscriptionListProvider: SubscriptionListProviderProtocol,
date: AirshipDateProtocol = AirshipDate.shared,
notificationCenter: AirshipNotificationCenter = AirshipNotificationCenter.shared,
audienceOverridesProvider: AudienceOverridesProvider,
Expand All @@ -172,16 +173,15 @@ public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked
self.dataStore = dataStore
self.config = config
self.privacyManager = privacyManager
self.subscriptionListAPIClient = subscriptionListAPIClient
self.contactChannelsProvider = contactChannelsProvider
self.audienceOverridesProvider = audienceOverridesProvider
self.date = date
self.contactManager = contactManager
self.smsValidator = smsValidator
self.serialQueue = serialQueue

self.subscriptionListProvider = subscriptionListProvider
self.cachedSubscriptionLists = CachedValue(date: date)

super.init()

self.setupTask = Task {
Expand Down Expand Up @@ -330,12 +330,15 @@ public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked
config: config,
channel: channel,
privacyManager: privacyManager,
subscriptionListAPIClient: ContactSubscriptionListAPIClient(config: config),
contactChannelsProvider: ContactChannelsProvider(
audienceOverrides: audienceOverridesProvider,
apiClient: ContactChannelsAPIClient(config: config),
privacyManager: privacyManager
),
subscriptionListProvider: SubscriptionListProvider(
audienceOverrides: audienceOverridesProvider,
apiClient: ContactSubscriptionListAPIClient(config: config),
privacyManager: privacyManager),
audienceOverridesProvider: audienceOverridesProvider,
contactManager: ContactManager(
dataStore: dataStore,
Expand Down Expand Up @@ -715,43 +718,7 @@ public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked

public func fetchSubscriptionLists() async throws -> [String: [ChannelScope]] {
let contactID = await getStableContactID()
var subscriptions = try await self.resolveSubscriptionLists(contactID)

let overrides = await self.audienceOverridesProvider.contactOverrides(contactID: contactID)

subscriptions = AudienceUtils.applySubscriptionListsUpdates(
subscriptions,
updates: overrides.subscriptionLists
)

return subscriptions
}

private func resolveSubscriptionLists(
_ contactID: String
) async throws -> [String: [ChannelScope]] {

return try await self.fetchSubscriptionListQueue.run {
if let cached = self.cachedSubscriptionLists.value,
cached.0 == contactID {
return cached.1
}

let response = try await self.subscriptionListAPIClient.fetchSubscriptionLists(
contactID: contactID
)

guard response.isSuccess, let lists = response.result else {
throw AirshipErrors.error("Failed to fetch subscription lists")
}

AirshipLogger.debug("Fetched lists finished with response: \(response)")
self.cachedSubscriptionLists.set(
value: (contactID, lists),
expiresIn: Self.maxSubscriptionListCacheAge
)
return lists
}
return try await subscriptionListProvider.fetch(contactID: contactID)
}

@objc
Expand All @@ -778,6 +745,8 @@ public final class AirshipContact: NSObject, AirshipContactProtocol, @unchecked
self.lastResolveDate = self.date.now
self.addOperation(.resolve)
}

self.contactChannelsProvider.refreshAsync()
}

@objc
Expand Down Expand Up @@ -927,6 +896,22 @@ extension AirshipContact : InternalAirshipContactProtocol {
}
}

#if !os(watchOS)
extension AirshipContact: AirshipPushableComponent {
public func receivedRemoteNotification(
_ notification: [AnyHashable: Any],
completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
if notification[Self.refreshContactPushPayloadKey] == nil {
completionHandler(.noData)
} else {
self.contactChannelsProvider.refreshAsync()
completionHandler(.newData)
}
}
}
#endif



extension AirshipContact: AirshipComponent {}
Expand Down
2 changes: 1 addition & 1 deletion Airship/AirshipCore/Source/AirshipImageLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public struct AirshipImageLoader {
}

private func fetchImage(url: URL) -> AnyPublisher<AirshipImageData, Error> {
return URLSession.shared.dataTaskPublisher(for: url)
return URLSession.airshipSecureSession.dataTaskPublisher(for: url)
.mapError { AirshipErrors.error("URL error \($0)") }
.map { response -> AnyPublisher<AirshipImageData, Error> in
guard let httpResponse = response.response as? HTTPURLResponse,
Expand Down
13 changes: 12 additions & 1 deletion Airship/AirshipCore/Source/AirshipRequestSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ final class DefaultAirshipRequestSession: AirshipRequestSession, @unchecked Send
sessionConfig.tlsMinimumSupportedProtocolVersion = .TLSv12
return URLSession(
configuration: sessionConfig,
delegate: nil,
delegate: ChallengeResolver.shared,
delegateQueue: nil
)
}()
Expand Down Expand Up @@ -482,5 +482,16 @@ extension URLSession: URLRequestSessionProtocol {
}
}

/**
* URLSession with configured optional challenge resolver
* @note For internal use only. :nodoc:
*/
public extension URLSession {
static let airshipSecureSession: URLSession = .init(
configuration: .default,
delegate: ChallengeResolver.shared,
delegateQueue: nil)
}



13 changes: 12 additions & 1 deletion Airship/AirshipCore/Source/AirshipWebview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,16 @@ struct WebViewView: UIViewRepresentable {
JavaScriptCommandDelegate, NativeBridgeDelegate
{
private let parent: WebViewView
private let challengeResolver: ChallengeResolver
let nativeBridge: NativeBridge

init(_ parent: WebViewView, actionRunner: NativeBridgeActionRunner) {
init(_ parent: WebViewView, actionRunner: NativeBridgeActionRunner, resolver: ChallengeResolver = .shared) {
self.parent = parent
self.nativeBridge = NativeBridge(actionRunner: actionRunner)
self.challengeResolver = resolver

super.init()

self.nativeBridge.nativeBridgeExtensionDelegate =
self.parent.nativeBridgeExtension
self.nativeBridge.forwardNavigationDelegate = self
Expand Down Expand Up @@ -126,6 +130,13 @@ struct WebViewView: UIViewRepresentable {
webView?.reload()
}
}

func webView(
_ webView: WKWebView,
respondTo challenge: URLAuthenticationChallenge)
async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
return await challengeResolver.resolve(challenge)
}

func performCommand(_ command: JavaScriptCommand, webView: WKWebView) -> Bool {
return false
Expand Down
Loading

0 comments on commit 930af2b

Please sign in to comment.